记一道有趣的Twitter面试题
微信上的“程序员的那些事”想必是很多码农们关注的公众账号之一,我也是其粉丝,每天都会看看里面有没有什么趣事,前段时间“程序员的那些事”分享了一篇博文《我的Twitter技术面试失败了》挺有意思,链接如下http://mp.weixin.qq.com/mp/appmsg/show?__biz=MjM5OTA1MDUyMA==&appmsgid=10000710&itemidx=1&sign=fab77147279ef685c50e39cc06623e5d&uin=MjM3Mjc1NTIwMA%3D%3D&key=38b17fed399880fb7129f69083fd038240b4873f89a22a1bf82803ef35479ff9dda602589570716d1a73ae7c3e9f739d&devicetype=android-17&version=25000105&lang=zh_CN。
里面说到了一道算法题,给出一个数组,将其转化为二维坐标系的点,并且连接每个点形成一个“容器”状的图形,问“容器”能装多少水(详情参照上面的链接)。这道题吸引我的原因是,每当说到算法题,许多公司总是喜欢考一些查找、排序、大数据处理等等,没什么多新意,也考不了程序员的思维能力(面试前复习下《数据结构》即可);而这道题很有新意,引发了我做一做的欲望,顺便也开拓一下思维,毕竟在实际工作中还是很少用到算法的。
第一次解题:思路很自然地想到从左到右遍历数组,确定左右底部的位置(即一个“蓄水区域”),进行一次容积的计算,然后将指针移动到此“蓄水区域”的右侧,继续确定下一个“蓄水区域” 的左右底部位置,再进行一次容积的计算...直到数组最右端,代码如下:
/*
* waterHolder.c
*
* Created on: 2013-11-5
* Author: pengyiming
* Description: 1,输入如下的非负数组(2, 5, 1, 2, 4),将其转化到二维坐标系中的点((2, 0), (2, 1), (5, 1), (5, 2), (1, 2), (1, 3), (2, 3), (2, 4), (4, 4), (4, 5))
* 2,连接上述个点形成一个"容器"
* 试问"容器"可装多少水,以1x1方格为单位
* Answer: 第一次遍历,从左向右遍历数组,找出左右侧最高点
* 第二次遍历,遍历左右侧最高点之间的数组,计算容积
*/ #include <stdio.h> /* 宏begin */
#define TRUE 1
#define FALSE 0
/* 宏end */ /* 输入数据begin */
static const unsigned int WATER_BUCKET[] = { , , , , , , , , };
/* 输入数据end */ int main(int argc, char *argv[])
{
int volume = ; // "容器"左右侧高度,底部高度
int leftHeight = ;
int bottomHeight = ;
int rightHeight = ; // "容器"左右侧高度,底部高度位置
int leftPos = ;
int bottomPos = ;
int rightPos = ; // "容器"左侧高度,底部高度确定状态
int leftPosOK = FALSE;
int bottomPosOK = FALSE; // 遍历数组以获取"容器"中的每个蓄水区域
int length = sizeof(WATER_BUCKET) / sizeof(unsigned int);
while (TRUE)
{
int i;
for (i = rightPos; i < length; i++)
{
// 寻找左侧位置
if (!leftPosOK && WATER_BUCKET[i] >= leftHeight)
{
leftHeight = WATER_BUCKET[i];
leftPos = i; bottomHeight = WATER_BUCKET[i];
} // 寻找底部位置,确定左侧位置
if (!bottomPosOK && (WATER_BUCKET[i] < bottomHeight))
{
bottomHeight = WATER_BUCKET[i];
bottomPos = i; leftPosOK = TRUE;
rightHeight = WATER_BUCKET[i];
} // 寻找右侧位置,确定底部位置
if (leftPosOK && (WATER_BUCKET[i] > rightHeight))
{
rightHeight = WATER_BUCKET[i];
rightPos = i; bottomPosOK = TRUE; // 优化:若右侧高度已经大于等于左侧高度,则已确定右侧位置,无需遍历完数组
if (rightHeight >= leftHeight)
{
break;
}
} printf("loop : left = %d, right = %d, bottom = %d\n", leftPos, rightPos, bottomPos);
} if (bottomPos > leftPos
&& rightPos > bottomPos)
{
volume += countVolume(leftPos, rightPos, bottomPos); // 重置并计算下一个"蓄水区域"
leftHeight = ;
bottomHeight = ;
rightHeight = ; leftPos = rightPos;
bottomPos = rightPos; leftPosOK = FALSE;
bottomPosOK = FALSE;
}
else
{
// 可能在"容器"中有多个蓄水区域,遍历可能需要多次,设定一个变量标识是否找到蓄水区域,当找不到时退出
break;
}
} printf("total volume = %d\n", volume); return ;
} int countVolume(int leftPos, int rightPos, int bottomPos)
{
printf("count volume : left = %d, right = %d, bottom = %d\n", leftPos, rightPos, bottomPos); if (WATER_BUCKET[leftPos] == WATER_BUCKET[bottomPos]
|| WATER_BUCKET[rightPos] == WATER_BUCKET[bottomPos])
{
return ;
} // "容器"能装多少水取决于最短一侧的高度
int minHeight = WATER_BUCKET[leftPos];
if (minHeight > WATER_BUCKET[rightPos])
{
minHeight = WATER_BUCKET[rightPos];
} int volume = ;
int i;
for (i = leftPos + ; i < rightPos; i++)
{
volume += minHeight - WATER_BUCKET[i];
} return volume;
}
上述算法通过了链接中及我自己列举的测试用例,说明上述代码确实能工作,但是其中掺杂了太多奇怪的boolean变量来帮助确定左右底部的位置,这使得代码可读性不好,且逻辑不够清晰,最近发完版后又闲下来了,重新理解了一下链接中的解题思路,确实比这段算法的思路清晰很多。
第二次解题:第一次遍历确定“容器”最高点,从而将“容器”一分为二,接下来只要分别确定“容器”左右侧最高点,并计算左右侧容积即可(“容器”左右侧最高点必定小于“容器”最高点,根据“木桶原理”,用左右侧最高点计算容积)。
多插一句,给出的数组是线性的,但是不见得非要从左到右遍历去解决问题,可以试一试上面这种分治的思路,线性的数组一分为二,分别通过左右两侧的子遍历来解决问题,代码如下:
/*
* waterHolder2.c
*
* Created on: 2013-11-19
* Author: pengyiming
* Description: 1,输入如下的非负数组(2, 5, 1, 2, 4),将其转化到二维坐标系中的点((2, 0), (2, 1), (5, 1), (5, 2), (1, 2), (1, 3), (2, 3), (2, 4), (4, 4), (4, 5))
* 2,连接上述个点形成一个"容器"
* 试问"容器"可装多少水,以1x1方格为单位
* Answer: 第一次遍历,遍历整个数组,找出最高点
* 第二次遍历,分为两个子遍历最高点左右侧的数组,计算容积
*/ #include <stdio.h> /* 宏begin */
/* 宏end */ /* 输入数据begin */
static const unsigned int WATER_BUCKET[] = { , , , , , , , , };
/* 输入数据end */ int main(int argc, char *argv[])
{
int length = sizeof(WATER_BUCKET) / sizeof(unsigned int);
int volume = ; int maxPos = findMaxPos(length); volume += countLeftVolume(maxPos);
volume += countRightVolume(length, maxPos);
printf("total volume = %d\n", volume);
} int findMaxPos(int length)
{
int max = ;
int maxPos = ;
int i;
for (i = ; i < length; i++)
{
if (WATER_BUCKET[i] > max)
{
max = WATER_BUCKET[i];
maxPos = i;
}
} return maxPos;
} int countLeftVolume(int maxPos)
{
int volume = ;
int leftMax = ;
int i;
for (i = ;i < maxPos; i++)
{
if (leftMax >= WATER_BUCKET[i])
{
volume += leftMax - WATER_BUCKET[i];
}
else
{
leftMax = WATER_BUCKET[i];
}
} printf("left volume = %d\n", volume);
return volume;
} int countRightVolume(int length, int maxPos)
{
int volume = ;
int rightMax = ;
int i;
for (i = length - ; i > maxPos; i--)
{
if (rightMax >= WATER_BUCKET[i])
{
volume += rightMax - WATER_BUCKET[i];
}
else
{
rightMax = WATER_BUCKET[i];
}
} printf("right volume = %d\n", volume);
return volume;
}
上述算法也通过了链接中及我自己列举的测试用例,如果大家还有什么更好的解法,欢迎不啬赐教!
记一道有趣的Twitter面试题的更多相关文章
- 一道有趣的Twitter技术面试题
来自:http://blog.jobbole.com/50705/ 看下面这个图片” “在这个图片里我们有不同高度的墙.这个图片由一个整数数组所代表,数组中每个数是墙的高度.上边的图可以表示为数组[2 ...
- javascript基础修炼(13)——记一道有趣的JS脑洞练习题
目录 一. 题目 二. 解法风暴 示例代码托管在:http://www.github.com/dashnowords/blogs 博客园地址:<大史住在大前端>原创博文目录 华为云社区地址 ...
- javascript基础修炼(13)——记一道有趣的JS脑洞练习题【华为云技术分享】
版权声明:本文为博主原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明. 本文链接:https://blog.csdn.net/devcloud/article/detai ...
- 一道关于String的面试题,新鲜出炉,刚被坑过,趁热!!
很多人都会答错的一道关于String的题目,究竟有什么难度? 我们一起来看一道关于String的面试题,准确说是改编的面试题! 准备好啦?在放大招之前先来一个小招式 String s1 = new S ...
- 一道有趣的for循环题
一道有趣的for循环题 今天在复习js基础知识时发现了一个for循环的题,第一眼看到直接懵逼了,没想到for循环竟然还可以这样玩?涨姿势了. 题目是这样的 for(i=0, j=0; i<10, ...
- codeforces 1451D,一道有趣的博弈论问题
大家好,欢迎来到codeforces专题. 今天选择的问题是Contest 1451场的D题,这是一道有趣简单的伪博弈论问题,全场通过的人有3203人.难度不太高,依旧以思维为主,坑不多,非常友好. ...
- 一道Twitter面试题的解答
前言 这道面试题是我在博客园首页的一篇文章中看到的,面试题我简单的提取出来了,文章链接:http://news.cnblogs.com/n/192014/. 在我用JS实现了我自己的想法之后,我再一次 ...
- 一道Twitter面试题
在微博上看到的这个问题,忍住没看答案自己解决了.建议没看过的同学也自己先尝试下. “看下面这个图片” 在这个图片里我们有不同高度的墙.这个图片由一个整数数组所代表,数组中每个数是墙的高度.上边的图可以 ...
- 记一道css面试题 : 三栏布局两边宽度固定,中间宽度自适应,并且布局随屏幕大小改变。
前几天面试时有道css题没做出来,回来好好学习一番后把其记录下来. 题目是这样的:左中右三栏布局,左右两栏宽度固定,左右两栏的宽度为200像素,中间栏宽度自适应.当屏幕小于600px时,3栏会分别占用 ...
随机推荐
- App Inspector 功能详解
前言: App Inspector:浏览器端的移动设备 UI 查看器,使用树状态结构查看 UI 布局,自动生成 XPaths 官网:https://macacajs.github.io/app-ins ...
- PAT甲题题解-1103. Integer Factorization (30)-(dfs)
该题还不错~. 题意:给定N.K.P,使得可以分解成N = n1^P + … nk^P的形式,如果可以,输出sum(ni)最大的划分,如果sum一样,输出序列较大的那个.否则输出Impossible. ...
- D. Vasya and Arrays
链接 [http://codeforces.com/contest/1036/problem/D] 题意 给你两个数组长度分别为n,m; 有这么一种操作,用某个数组的某个子区间元素之和代替这个子区间, ...
- C++ 实验 使用重载运算符实现一个复数类
实验目的: 1.掌握用成员函数重载运算符的方法 2.掌握用友元函数重载运算符的方法 实验要求: 1.定义一个复数类,描述一些必须的成员函数,如:构造函数,析构函数,赋值函数,返回数据成员值的函数等. ...
- C语言入门:04.数据类型、常量、变量
一.数据 1.什么是数据 生活中时时刻刻都在跟数据打交道,比如体重数据.血压数据.股价数据等.在我们使用计算机的过程中,会接触到各种各样的数据,有文档数据.图片数据.视频数据,还有聊QQ时产生的文字数 ...
- 学习构建一个简单的wcf服务
入门,构建第一个WCF程序 1.服务端 建立一个控制台应用程序作为Server,新建一个接口IData作为服务契约.这个契约接口一会儿也要放到Client端,这样双方才能遵循相同的标准.别忘了添加对 ...
- Vue项目框架
Vue项目框架 基本组件的使用: new Vue({ el, //要绑定的DOM element data, //要绑定的资料 props, //可用来接收父原件资料的属性 template, //要 ...
- SP1043 GSS1
题目链接 简单说就是带修的查询区间最大子段和,用线段树维护即可 对于每个区间,我们肯定要记录它的最大子段和\(v\),但是怎么维护呢? 我们可以记录下从区间左端点开始的最大子段和\(v1\),从右端点 ...
- 使用doxygen静态分析开源代码
doxygen是一款生成开源代码说明文件的工具,因为不需要编译源码,用作代码的分析也十分方便. 一.安装 sudo apt-get install graphviz sudo apt-get inst ...
- Pathwalks CodeForces - 960F(主席树 || 树状数组)
题意: 求树上最长上升路径 解析: 树状数组版: 998ms edge[u][w] 代表以u为一条路的终点的小于w的最长路径的路的条数 · 那么edge[v][w] = max(edge[u][w-1 ...