有趣面试题

来源:互联网 发布:excel一列数据自动复制 编辑:程序博客网 时间:2024/06/16 22:33

1,【微软】小老鼠试毒药

题目:

有1000瓶水,其中1瓶是有毒的,小老鼠如果喝了有毒的水会在一个星期后死掉,问至少需要多少只小老鼠来做实验,才能够在一星期后选出有毒的一瓶水。

答案:

10只。

我来解释一下,并给出一个方案,时间不是问题,一星期内肯定可以找出有毒的那瓶。 
给1000个瓶分别标上如下标签(10位长度): 
0000000001 (第1瓶) 
0000000010 (第2瓶) 
0000000011 (第3瓶) 
...... 
1111101000 (第1000瓶) 
从编号最后1位是1的所有的瓶子里面取出1滴混在一起(比如从第一瓶,第三瓶,。。。里分别取出一滴混在一起)并标上记号为1。以此类推,从编号第一位是1的所有的瓶子里面取出1滴混在一起并标上记号为10。现在得到有10个编号的混合液,小白鼠排排站,分别标上10,9,。。。1号,并分别给它们灌上对应号码的混合液。一周过去了,过来验尸吧: 从左到右,死了的小白鼠贴上标签1,没死的贴上0,最后得到一个序号,把这个序号换成10进制的数字,就是有毒的那瓶水的编号。 

检验一下:假如第一瓶有毒,按照0000000001 (第1瓶),说明第1号混合液有毒,因此小白鼠的生死符为0000000001(编号为1的小白鼠挂了),0000000001二进制标签转换成十进制=1号瓶有毒;假如第三瓶有毒,0000000011 (第3瓶),第1号和第2号混合液有毒,因此小白鼠的生死符为00000011(编号为1,2的鼠兄弟挂了),0000000011二进制标签转换成十进制=3号瓶有毒。

支撑算法:Bloom Filter算法

题目2:11瓶酒,有一瓶有毒,找出这瓶酒

答案2:因为2^3=8<11<2^4=16,所以用4个位就可以表示,所以四只老鼠。

题目3:11瓶酒,有一瓶有毒,找出可以喝的9瓶酒

答案3:http://blog.csdn.net/hbjiaxiaoxue/article/details/12585643

2,【微软】N球称重量

题目:有N个球,其中只有一个是重量较轻的,用天平只称三次就能找到较轻的球,以下的N值哪个是可能的?

Given a set of N balls and one of which is defective(weighs less than others), you are allowed to weigh with a balance 3 times to find the defective. Which of the following are possible N?(A,B,C,D)
   A. 12
   B. 16
   C. 20
   D. 24
   E. 28
解答:12个,分成三组4,4,4,先拿出其中两组称量,若左边轻,则坏球在左边,若右边轻则坏球在右边,若平衡,则坏球在剩下的四个当中。这样一次称量就将球减少到4个。将4个分成两组2,2,若左边轻则坏球在左边,反之在右边。第二次称量将球减少到2个。剩下两个再称量一次显然就确定了。也有其他可行的分组方法。A正确。
16个,分成三组6,6,4,先称量两组6个球的,则坏球要么在4个当中,要么在两组6个当中的一组,4个的用两次可以确定坏球。6个的再分成2,2,2三组,同样可以用一次减少到两个球,最后确定坏球。也有其他可行的分组方法。B正确。
20个,分成6,6,8三组,若在6个当中,两次可以找出,若在8个当中,将其分成3,3,2三组,两次也可以确定。也有其他可行的分组方法。
24个,分成8,8,8。第一次称量减少到8个,剩下8个两次可以确定,也有其他可行的分组方法。
28个无法三次确定。
总之,3个球以内一次,可一次称量确定。4~9个球可两次称量确定。10~27个球可三次称量确定。对于三次称量,只要第一次分成的三组,每组不多于9个即可。


3,【微软】知道了二叉树的哪些排序序列后,可以确定一颗二叉树。

We can revocer the binary tree if given the output of
   A. Preorder traversal and inorder traversal
   B. Preorder traversal and postorder traversal
   C. Inorder traversal and postorder traversal
   D. Postorder traversal 
解答:

中序遍历和前序遍历
中序遍历和后序遍历
中序遍历和层次遍历

这3种都可以确定一颗二叉树,从上面可以看出,必须要有中序遍历。

前序遍历和后续遍历不能恢复出原来的二叉树结构,因为存在不同的二叉树树,前序遍历和后续遍历都相同。如:

第一棵二叉树
        1
      /
    2
前序遍历是1 2, 后序遍历是2 1。
另一棵二叉树
       1
          \
           2
前序遍历是1 2, 后序遍历是2 1。
所以B不正确,D就更不正确了。


4,【腾讯】不用除法

两个数组a[N],b[N],其中A[N]的各个元素值已知,现给b[i]赋值,b[i] = a[0]*a[1]*a[2]...*a[N-1]/a[i];
要求:
1.不准用除法运算
2.除了循环计数值,a[N],b[N]外,不准再用其他任何变量(包括局部变量,全局变量等)
3.满足时间复杂度O(n),空间复杂度O(1)

解答1:

1,b[i] = a[0] *a[1] * a[2] … a[i-1] *a[i+1] * a[i+2] * … a[N-2] * a[N -1]

a[0]a[1]a[2]a[3]2475

2,第一次循环:

依次计算a[0] *a[1] * a[2] … a[i-1]的结果

b[0]=1;

b[i]=b[i-1]*a[i-1];

b[0]b[1]b[2]b[3]122*42*4*7

3,第二次循环(因为不能用局部变量,所以必须利用数组a[]):

依次计算a[i+1] * a[i+2] * … a[N-2] * a[N -1]的结果

a[N-1] = a[N-1];

a[i] = a[i]*a[i+1];

a[0]a[1]a[2]a[3]2*4*7*54*7*57*55

4,第三次循环(计算每个b[i])

b[i] = b[i]*a[i+1];

b[0]b[1]b[2]b[3]4*7*52*7*52*4*52*4*7

void Translate(int a[], int b[], int n)  {//第一次循环b[0] = 1;for(int i = 1; i<n; i++){b[i] = b[i-1]*a[i-1];}//第二次循环for(i = n-2; i>=0; i--){a[i] = a[i]*a[i+1];}//第三次循环for(i = 0; i<n-1; i++){b[i] = b[i]*a[i+1];}b[n] = a[n];}

缺点是修改了a[]数组

解答2:

可以直接借助b[0]作为临时变量来生成:

void Translate(int a[], int b[], int n){b[0] = 1;for (int i = 1; i <= n-1; i++){b[i] = b[i-1]*a[i-1];}for (int i = n-1; i >= 1; i--){b[i] *= b[0];b[0] *= a[i];}}


5,【ebay】戴帽子

问题:

“有2顶白帽子和3顶红帽子, 3个人按等腰三角形位置对坐下。将3个人蒙住眼睛分别各戴上一顶帽子后,让他们睁开眼睛,可以看见别人头上戴着什么颜色的帽子,但不能看见自己头上戴着什么颜色的帽子,然后要他们只说出自己头上戴的是什么颜色的帽子。3个人先沉默了一会儿,之后一起回答说自己头上戴的是红颜色帽子。请解释他们根据什么作出了正确的判断?”
解答:

假设是abc三个人。
三个人中不可能有两个人都戴白帽子,否则第三个人就能够立刻知道自己戴的是红帽子。
三个人中不可能有一个人戴白帽子,否则:
假设c戴白帽子,a、b戴红帽子,我们来分析a和b(这两个人的思路相同)的想法:
a看到那俩人一红一白,那么a推知自己不可能是白帽子,否则那个戴红帽子的立刻知道自己戴的是红帽子。
将上述分析用b来代替a完全成立。
由于三人同时说出自己戴的帽子颜色,所以肯定是abc都戴红帽子。


衍生:

一个小区有50户人家,养有50条狗。有一天免疫部门来说:你们这个小区有狗中了病毒,必须把病狗杀掉。
已知:这50个主人只能看出别家的狗是否生病,看不出自家的狗是否生病,且自家的狗只能由自己杀(用枪)。
第一天过去了,无枪声;第二天,也无枪声;第三天,有了一声枪声。
问:有几条病狗?  
答案:

3条病狗,三人同时开枪,合成了一声枪声。
结论1:不可能只有一条病狗,否则那条病狗主人看到剩余49条狗都是健康狗,剩下自家的狗肯定是病狗。
结论2:不可能只有两条病狗,否则那两条病狗的主人看到剩下49条狗有一条病狗,然后根据结论1,他们俩会在第二天知道自家的狗是病狗。
第三天开枪了,说明了病狗主人看到剩下49条狗中有两条病狗。由于第二天那两个病狗主人没有开枪,说明他们各自也看到了两条病狗,所以自家的狗肯定病了。

【事实上,第K天才开枪,说明有K条病狗】


6,【腾讯】海量数据找中位数

题目:在一个文件中有 10G 个整数,乱序排列,要求找出中位数。内存限制为 2G。只写出思路即可。

http://blog.csdn.net/v_july_v/article/details/6279498


7,【腾讯】上下对应

题目:给你10分钟时间,根据上排给出十个数,在其下排填出对应的十个数。要求下排每个数都是先前上排对应那个数在下排十个数中出现的次数。

下面给出一个更具一般性的结论: 
  这个是有规律可循的,不仅0~9有唯一解,0~n都只有唯一解。关键是最后面一个1它可以左右移动,1和2下面的数永远是2和1,0下面对应的数为n-3(n>=3),上排数n-3下面对应的数为1,其它上排数下面对应为0就ok了。有了这个一般性的结论再大的数都可以马上给出答案。

  比如 0  1  2  3  4  5  6  7  8  9  10  11 12 13 14 15 
          12 2  1  0  0  0  0  0  0  0   0    0    1   0   0   0

8,【微软】房间三盏灯

题目:有两个房间,一间房间里有三盏灯,另一个房间里有控制这三盏灯的三个开关(这两个房间是分割开的,毫无联系).现在你分别进入这两个房间一次,然后判断出这三盏灯分别是由哪个开关控制的,说出判断的方法。

思路:先进有开关的房子,先开A开关,过一段时间关上,再开B开关,然后进入有灯的房子,亮着的灯由B开关控制,用手摸熄灭的两盏灯,热的受A开关控制,剩下的受C开关控制

9,求1+2+...+n

http://zhedahht.blog.163.com/blog/static/2541117420072915131422/

题目:求1+2+…+n,要求不能使用乘除法、for、while、if、else、switch、case等关键字以及条件判断语句(A?B:C)。

思路1:1+..+n=n*(n+1)/2=(n^2+n)/2=(n^2+n)<<1 

int sum(int n){    return ((n * (n+1))>>1);} 

思路2:递归

int func(int n){    int i=1;    (n>1)&&(i=func(n-1)+n);    return i;}

思路3:让构造函数进行循环

循环只是让相同的代码执行n遍而已,我们完全可以不用for和while达到这个效果。比如定义一个类,我们new一含有n个这种类型元素的数组,那么该类的构造函数将确定会被调用n次。我们可以将需要执行的代码放到构造函数里。

#include <iostream.h>class Temp{public:      Temp()      {          ++N;          Sum += N;      }      static void Reset() { N = 0; Sum = 0; }      static int GetSum() { return Sum; }private:      static int N;      static int Sum;};int Temp::N = 0;int Temp::Sum = 0;int solution1_Sum(int n){      Temp::Reset();      Temp *a = new Temp[n];   //就是这个意思,new出n个数组。       delete []a;      a = 0;      return Temp::GetSum();}int main(){    cout<<solution1_Sum(100)<<endl;    return 0;}

10,约瑟夫问题??

题目:n个数字(0,1,…,n-1)形成一个圆圈,从数字0开始,每次从这个圆圈中删除第m个数字。当一个数字删除后,从被删除数字的下一个继续删除第m个数字。求出在这个圆圈中剩下的最后一个数字。

思路1:

xxx

思路2:存在数学递归解,f(1, m) = 0;f(n,m)=(f(n-1,m)+m)%n;效率O(n)

int joseph(int n, int m) {  int fn=0;  for (int i=2; i<=n; i++) fn = (fn+m)%i; //当n远大于m时,此处可优化  return fn;}

具体分析参见:

http://blog.csdn.net/v_JULY_v/article/details/6301244

11,裴波那切数列??

定义Fibonacci 数列如下:
f(0) = 0 f(1)= 1
f(n) = f(n-1)+f(n-2) n>=2
输入n,用最快的方法求该数列的第n 项。
思路:注意数值类型(long),并使用公式O(n)


int f(int n) { int A[4] = {1,1,1,0}; int result[4]; power(A, n, result); return result[0]; }  void multiply(int[] A, int[] B, int[] _r) { _r[0] = A[0]*B[0] + A[1]*B[2]; _r[1] = A[0]*B[1] + A[1]*B[3]; _r[2] = A[2]*B[0] + A[3]*B[2]; _r[3] = A[2]*B[1] + A[3]*B[3]; }  void power(int[] A, int n, int[] _r) { if (n==1) { memcpy(A, _r, 4*sizeof(int)); return; } int tmp[4]; power(A, n>>1, _r); multiply(_r, _r, tmp); if (n & 1 == 1) { multiply(tmp, A, _r); } else { memcpy(_r, tmp, 4*sizeof(int)); } }

12,【微软】有4张红色的牌和4张蓝色的牌

有4张红色的牌和4张蓝色的牌,主持人先拿任意两张,再分别在A、B、C三人额头上贴任意两张牌,A、B、C三人都可以看见其余两人额头上的牌,看完后让他们猜自己额头上是什么颜色的牌,A说不知道,B说不知道,C说不知道,然后A说知道了。请教如何推理,A是怎么知道的。

排除法,A、B、C若有一人见到三红一篮,或三蓝一红,或全红,或全蓝,肯定能在前三次推出自己的颜色。答案为一红一蓝。

13,【微软】跳台阶问题

题目:一个台阶总共有n级,如果一次可以跳1级,也可以跳2级。求总共有多少总跳法,并分析算法的时间复杂度???

思路:现在我们再来讨论一般情况。我们把n级台阶时的跳法看成是n的函数,记为f(n)。
当n>2时,第一次跳的时候就有两种不同的选择:一是第一次只跳1级,
此时跳法数目等于后面剩下的n-1级台阶的跳法数目,即为f(n-1);
另外一种选择是第一次跳2级,此时跳法数目等于后面剩下的n-2级台阶的跳法数目,即为f(n-2)。
因此n级台阶时的不同跳法的总数f(n)=f(n-1)+(f-2)。

结论:f(n)=f(n-1)+f(n-2), f(1)=1, f(2)=2, let f(0) = 1, then f(n) = fibo(n-1); 

int jump_sum(int n)  //递归版本{    assert(n>0);    if (n == 1 || n == 2) return n;    return jump_sum(n-1)+jump_sum(n-2);}

14,【微软】整数的二进制表示中1 的个数 

思路:xxxxxx10000  & (xxxxxx10000-1)  = xxxxxx00000 

int countOf1(int n) {   int count=0;   while (n!=0) {     n&=n-1;     count++;   }   return count; }

15,【百度】用天平找轻者球

题目:用天平(只能比较,不能称重)从一堆小球中找出其中唯一一个较轻的,使用x次天平,最多可以从y个小球中找出较轻的那个,求y与x的关系式。

思路:x=1, y=3。因为检测3个球,用一次天平,那么如果a==b,则c为轻者。否则轻者为轻者。因此,y=3^x


16,【百度】无限输入流随机取记录

题目:有一个很大很大的输入流,大到没有存储器可以将其存储下来, 而且只输入一次,如何从这个输入流中随机取得m 个记录。

思路:现在这里是一个很大的流按常理来说里面的记录数量应该大于m个,但是在这里为了考虑全面先假设 流里面的记录数小于m个,那么只需要将流里面的记录全部获取即可,当流里面的记录数量大于m个记录的时候,我们这样处理, 将每一个我们取出的记录通过一个随机算法得到一个随机数(当然随机数的范围大小的选取,与流中的记录数有关,如果记录很多的话那么范围就选大一点),首先用前m个记录的随机数建立一个大顶堆(小顶堆)也可以,然后依次读取后面的每一个记录然后计算得到随机数,然后将这个随机数加到堆里面去然后调整堆的结构是它满足堆的定义,这样当流全部读取完毕时,在内存中保存的是一个大小为M的堆。堆里面的记录就是问题所求,(这个问题妙的一点就是,任何一个记录的随机数都有被加入大顶堆的机会,所以这m个记录就是随机的)

17,【百度+网易】在一个半径为R的圆内找随机n个点

http://ngloom.me/2011/10/17/rand_points_in_circle/

思路1【极坐标法错误】:如果利用极坐标(半径r,弧度theta)其中r=rand(0,R),theta=rand(0,2PI)。

这个方法是错误的,用极坐标法得到的点不是均匀分布,而是越接近圆心,点越密集。这个我们用程序模拟过了。与大圆同圆心半径为R/2的圆上落有一半的点,而它的面积只占大圆面积的1/4。

思路2【外接正方形正确】:对于一个正方形,很容易由随机取点(x,y),其中0<=x,y<=2R来均匀落在该圆的外接正方形内。如果某个点落在外接正方形内且在圆外,那么重新随机一次,直到也落在圆内,这样对于圆来说,里面的点都是面积落点均匀的。

思路3【正确解法】:http://stackoverflow.com/questions/5837572/generate-a-random-point-within-a-circle-uniformly

原因是随机点rand[0~1]+rand[0~1] != rand[0~1]。所以尽管弧度制是一样的,但是半径在[0~1]上逐渐变密。 

t = 2*pi*random() //随机一个弧度u = random()+random()//随机一个小角度菱形r = if u>1 then 2-u else u //随机一个半径[r*cos(t), r*sin(t)]  //结果

18,矩形重合面积

问题:两个矩形,边平行于坐标轴,每个矩形用坐上和右下两个坐标表示,即R1(x1,y1,x2,y2)R2(x3,y3,x4,y4),如何计算重合部分的面积?

思路:

double getCommonArea(){//没有交集if(x2<x3)||(x4<x1)||(y4<y1)||(y3>y2)return 0;//有交集return (min(y4,y2)-max(y1,y3))*(min(x4,x2)-max(x1,x3));}

19,



原创粉丝点击