百度试题总结

来源:互联网 发布:啪啪啪是什么感觉知乎 编辑:程序博客网 时间:2024/06/04 22:11

 *1. 阶乘嵌套的两个数进行比较, eg. 50!!!!!! 85!!!!
2. 很多的url去重
*3. 大整数的加减乘除
*4. 很多的记录(1千万), 统计里面的频度, 把前5%和后5%的输出来
*5. 01矩阵, 求最大子矩阵.  算法期末考试题.
6. 判断一个字符串是不是合法的点分十进制ip.
7. 信号量机制, 消费者, 生产者. 尽量最快.
8. 一个可以产生1~5随机数的函数, 写一个1~n随机数产生器.
9. 一个字符串, 把前面一部分写到后面, 常数空间. 比如 abcdefg --> efgabcd
10. 输入一个整数, 用a-z表示, 相当于从10进制转换到26进制 比如27->aa,
 28->ab
11.设计一个cache系统.
1)列出设计时需要考虑的几个问题;
2)设计出数据结构和主要函数的为代码
3)描述你所使用的调度算法,并分析;
4)设计分布式cache系统,并说明其扩展性 

 

百度面试时问了这么一道题,没答全

句柄和指针有什么区别?
   什么是“句柄”(handle),handle的本意是把柄,把手的意思。是你与操作系统打交道的东东。举个通俗的例子,比如你考上了大学,入学后,学校(操作系统)会给你一个学生证号。注意,这个号码是学校指定的,你无法自选。有了这个号码(学生证,假设一证多用)享受学校提供的服务:如你就可以去图书馆借书,去食堂吃饭,去教室上课等等。但你不能到食堂里买啤酒,因为学校不允许这种服务。而在计算机中系统提供的服务就是API调用,你有了HANDLE,就可以理直气壮地向系统提出调用API的服务。而指针的权力就大多了,有了指针你可以到处去喝酒,打架,学校(操作系统)管不着,所以句柄和指针的区别在于句柄指针调用系统提供的服务。而句柄虽然是一个能相互区别的号码,但与我们普通的ID号又有区别,普通的ID号是可以由程序员自己定义的,而句柄不行,它是对象生成是系统指定的,是为了区别系统中存在的各个对象,这个句柄不是由程序员符给的。

  句柄 
    1
。句柄,是整个windows编程的基础,一个句柄是指使用的一个唯一的整数值,是指一个四字节长的数值,用于标志应用程序中的不同对象和同类对象中的不同的实例,诸如,一个窗口,按钮,图标,滚动条,输出设备,控件或者文件等。应用程序能够通过句柄访问相应的对象的信息。
    2
。句柄不是一个指针,程序不能利用它句柄来直接阅读文件中的信息。如果句柄不用在I/O文件中,它是毫无用处的。
    3
。句柄是windows用来标志应用程序中建立的或是使用的唯一整数,windows使用了大量的句柄来来标志很多对象。
  
说明:实例——windows环境下,不但可以运行多个应用程序,还可以运行多个应用程序的多份拷贝,每个拷贝叫做一个实例,并且有不同的实例句柄。一个实例句柄是windows可以单独运行的副本,是唯一可以标志此实例的整数。 

 

题目:有一根27厘米的细木杆,在第3厘米、7厘米、11厘米、17厘米、23厘米这五个位置上各有一只蚂蚁。木杆很细,不能同时通过一只蚂蚁。开始时, 蚂蚁的头朝左还是朝右是任意的,它们只会朝前走或调头,但不会后退。当任意两只蚂蚁碰头时,两只蚂蚁会同时调头朝反方向走。假设蚂蚁们每秒钟可以走一厘米 的距离。编写程序,求所有蚂蚁都离开木杆的最小时间和最大时间。
以上题目来源:http://www.blog.sh/user3/king821221/110716.html

问题分析:
1.最小时间肯家是各自朝最近的一端跑,27-11=14,11<14,所以中间的蚂蚁会朝11cm那端跑,最适时短时间11。
2. 最长时间呢,肯定两端的蚂蚁都往中间跑,具体怎么跑好像有点儿想不清楚,那试算之,假设3cm处的和7cm处的相对而行,碰面后会怎样?如果你眼神不好, 你会发现你分不出来哪个是哪个,因为3cm的转头后就相当于7cm的一直在走。到这里,一切就已经没有刚开始那样想不清楚了,事情很清楚:蚂蚁碰头可以用 等量代换的思想,在这种情况下,任何蚂蚁都是自由地向它面向的一端直接爬过去。那最长时间就清楚了:27-3=24,27-23=4 24>23,所以最长时间是24。
有了以上分析,算法就简单了。
算法:
1.找出中间的蚂蚁离两端的距离中较小的。
a[2]=11
a[2]'=27-11=14,
因为a[2]<a[2]',所以最小距离是11,时间11/1=11
2.找出两端的蚂蚁距两端的距离中较大的。
a[0]=3
a[0]'=27-3=24
a[4]=23
a[4]'=27-23=4
这四个数中最大的是24
3.所以,最大时间24,最小时间11
程序:略。

路老师也出了一道题:
有 一群海盗抢了很多金币,有一天,海盗头子拿出一些金币分成100袋,每次作战前随机抽出一袋,奖励这次作战第一个登上商船的部下(如果第一个上去的没有活 着回来,则奖给第二个),后来这100袋都分完了,结果得奖的勇士们发现,这个分配太不公平,因为最多的有100个,有的是37个,有的是89个,最少的 甚至只有一个。最后大家惊奇地发现,居然每一袋的个数都不一样。试问海盗头子总共拿出了多少金币?
程同学的解答如下:
代码如下:
#include <iostream>
#include <cstdlib>

#define BAG_MAX 100
#define COIN_MAX 100
using namespace std;

//取得第i个在1-100之间的随机数
int get_random(int cur)
{
   static int money[BAG_MAX]; //保存已经用过的金币数目
   int cur_money=rand()%COIN_MAX+1;//产生1-100的随机数
   bool flag=true;
   while(flag)
   {
      for(int i=0;i<cur;i++)
      {
         if(money[i]==cur_money)//金币数重复,重新产生
         {
               cur_money=rand()%COIN_MAX+1;
               break;
         }
      }
      if(i==cur) flag = false;//i=cur时说明新产生的金币数没有重复的。
      money[cur]=cur_money;
   }
   cout<<cur_money<<endl;
   return cur_money;
}
//本程序很好地模拟了海盗头子分发金币的过程
//并统计出了金币总数
int main()
{
   int total =0;
   for(int i=0;i<BAG_MAX;i++)
   {
        total+=get_random(i);
   }
   cout<<total<<endl;
}
王者之剑的点评:
嗯,程同学充分理解了客户(路老师)的思想,真实再现了客户的业务流程,程序可能不太简洁,但容易理解,运行结果正确,效率也没有问题,完全符合把复杂的事情让计算机去做,人要把精力放在业务上的信息化思想。
程同学(紧紧握住王者之剑的手):
前几天还在CSDN上看到讨论程序员要不要学数学的SB问题,当时就觉得变态,现在都什么时代了阿,是业务时代了阿,中国的信息化进程这么慢,就是因为缺少务实的业务专家,大家都去专研能够节省2K内存的算法,要是多一些象您这样的专家多好。
2007年10月27日日面试baidu高程,惨败题目如下,给大家共勉:

A厂有1万个工人,编号0-9999,(   EE[10000]   ),     1个厂长(   GG   )分派任务,     1个监工(   MM   )管理工人.
厂子忙的时间不确定,可能突然很忙,1天接到任务5000多个,1个任务只能分配给1个工人做,   也可能好几十天没新任务.

厂长分配任务给这1万个工人干,按工人编号一个一个来,到最后一个工人就又从头开始,任务完成时间各不相同,
可能一个工人在分配任务的时候手里还有任务,   就得换下一个。

但是这1万个工人都很懒,领到了任务先不做,需要监工1个1个去问,如果工人有任务,就做,如果工人没任务,则不做。
厂长只管分任务,1个1个来,可能几天也没新任务,不累;
但是监工很累,监工每天都要看所有工人的情况,即使这些工人都没有任务,   实际上每天工人(80%左右)是没任务的,
请问,怎么让监工的工作轻松下来.   比如说每天只问1小半工人.
以上题目来源:http://topic.csdn.net/u/20071028/23/8a4e6edb-7592-45bf-9a69-92a65467be76.html?seed=1777439653

我的解答:
1.问题分析
    现在的情况是监工每天要查看所有的工人,催他们工作,因为不催他们不开工,即要访问EE[10000]的每个元素一次。目标是每天只问一小半的工人,实际 上没有工作的工人是不需要问的,最理想的情况就是监工只问有工作的工人,或者尽可能少地问没有工作的工人,即要尽可能少地访问EE[10000]的元素。
   怎么办呢?监工想了一个办法,他做了一万张卡片,每张卡片上写着工人的编号,从0-9999,恰好和数组EE[10000]的下标对应。
    监工拿着他的秘密武器上阵了,0号,有工作没?没有。好,放右边口袋。1号,有工作没?有。今天能干完吗?能。好,放右边口袋(并且放在0号后面)。2 号,有工作没?有,今天能干完吗?不能。嗯,放左边。3号,没有。放右边(1号后面)。4号,今天干不完,放左边。……第二天,先看右边(昨天没事的), 0号,有工作没,有,今天能干完吗,能,好,不动。1号有工作没,有,干不完,好,放左边(接着昨天后面放)。3号,没有,哦,厂长GG还没有分配到这里 阿,那明天检查空的从这里开始就可以了(记住),但今天仍然轻松不了,因为可能是从后面的号码过来,并且分配到前面来了。右边全部查完了。再查左边。2 号,今天能干完,放右边(并且放在0号后面)。阿,又碰到了1号了,今天的检查结束。第三天,总算可以轻松了。从3号开始,先查右边。今天做得完,不动, 做不完,移到左边后面,碰到没有任务的,或者碰到3号,右边检查完。再看左边,做得完的,移到右边,并且按顺序插入其它卡片中间,做不完的,不动,直到碰 到今天新加入的做不完的,或者整个左边的卡片都检查完。
  说了这么多,实际上就是左边的是工作的队列,右边的是没工作的队列,左边和右边的区别就是右边的要保持按编号排序(因为厂长GG分配任务是按顺序分的)。 再拿支铅笔,在有的卡片上做做记号。工作轻松不少。以上都是假设工人必须每天催才会工作,并且监工每天都是在厂长分配任务之后才去催。如果工人催一次就会 一直工作,那么简单,监工只需要在卡片上记下还要几天才去问就行了。如果监工是每天早上去催,厂长去可能在清早(问之前)或下午(问之后,但工人还没工作 完)去分任务。
如果是前者,当然没问题,如果是后者问题来了,因为3号是今天才能完工的,但也是放在右边,如果厂长刚好分了2号和4号,那么按上面的逻辑,4号就催不到了。
所以为了避免这种情况,当天能完的还不能放在空闲里。嗯,只要再准备一个口袋就行了。好在监工的衣服上面有两个口袋,下面也有两个,用了下面两个,上面还有两个没有被使用。拿一个来用就行了。
检 查下面的左右口袋时,凡是当天能做完的,都放在左上。OK,先右下,再左下,当天能做完的这会儿放右上,再把左上的按顺序插入右下。应该没问题了吧,不管 厂长何时分任务,监工只需要看自己的口袋就可以了。右下需要访问的是从昨天访问的没工作的开始,再到一个新的没工作的。左下都要访问。右上或左上的卡片只 需要整理。因为实际上每天工人(80%左右)是没任务的,都在右下,并且也可能好几十天没新任务,这下轻松了,只用问小部分工人就能保证工作正常进行了。
好了,如果想先模拟一遍怎么办,用大脑模拟,10000人太多想不过来,累。写个程序吧。要写程序得先有算法。
2.算法
  以下算法模拟最一般的情况,即监工不知道厂长何时分任务,厂长在一天的任何时候都可以分任务,工人每天都要问才工作,监工只在早上去催(为了简化工时的计算,即工时以天为单位)。
  完全的随机起点模拟,即此方法可从任何监工想要采用此方法的时间点开始。
  假设一个任务完成的时间是1-N天,厂子一天接到的新任务数是0-M个。用T分钟模拟一天。定时器是精度毫秒级。
  A.用0-N之间的数初始化EE[10000],模拟当前工人的工作状态。EE[i]表示工人还要多少天完成这个任务。EE[i]=0,表示没任务。
  B.设置定时器,厂长分任务定时器为1-T*60*1000毫秒之间某个时间,监工定时器设为T*60*1000毫秒。
  C.厂长定时器到,厂长分任务。用c记录厂长从哪里开始。第一次时有个随机初始化的工程,随机一个0-9999之间的数,然后找到第一个EE[i]=0的i,从这个c=i开始分配。
    随机产生0-M,如果=0,则c=i不变,如果是1-M之间的值,则一个个查,碰到EE[i]=0的,给他随机一个1-N的值,直到分完这些任务,并且c=最后分到的+1。这个题目是研究监工的问题是,厂长比较轻松,下次让他继续从这里找下去。
    重新设定厂长定时器,定时为T*60*1000-上次定的时,再加上1-T*60*1000毫秒之间某个时间,因为厂长也只会一天分一次,所以先要把今天 的时间用完,再加上下一天的某个时间,(从前面可以看出,厂长的定时器设成T也是一样的,只要考虑一下访问共享数据的问题,这里先不考虑这个问题)。
  D.监工定时器到,监工问工人。
    新建四个链表。a(今天还不能做完的),b(没有工作的),c(今天可以做完的),d(今天可以做完的),初始化为空。但b为有序链表。c和d轮流使用。
    第一次定时到,访问EE[10000],今天不能做完的(EE[i]>1)接到a的尾巴上,能做完的(EE[i]=1)接到c的尾巴上,没有工作的(EE[i]=0)接b,d空。
    第二次定时到,访问b,今天不能做完的接到a的尾巴上,能做完的接到d的尾巴上,并且记录出现有工作的人后再出现没有工作的人的结点指针p。如果没有这样的人,那就是链表第一个人或者为空(大家都有工作)。但不管怎样必须把整个链表b访问一遍。
         访问a,不能做完的不动,能做完的接到d的尾巴上,最后将c中的元素插入b。注意,链表中元素唯一,也就是说移到另一个链表的时候,也意味着从原链表删除。
    第三次定时到,从p开始访问b中的元素,今天不能做完的接到a的尾巴上,能做完的接到c的尾巴上,直到找到一个没有工作的或者已经全部找了一遍(找 的时候调整p到新的位置)。到链表最后一个后,可能要从头找。
          访问a,不能做完的不动,能做完的接到c的尾巴上,最后将d中的元素插入b。
    第四次定时到,和第3次类似,只是c和d的位置对调了一下。到这时,监工的工作已经轻松了,整个系统将按这个新的方式一直运行下去。
   监工的定时器不需要重新设置。
  a,b,c,d中的元素内容为工人编号,访问时语法类似if(EE[p->index]>1)。

一排N(最大1M)个正整数+1递增,乱序排列,第一个不是最小的,把它换成-1,最小数为a且未知求第一个被

-1替换掉的数原来的值,并分析算法复杂度。

解题思路:

一般稍微有点算法知识的人想想就会很容易给出以下解法:

设 Sn = a + (a+1) + (a+2) + .........+ (a+n-1) = na +n(n-1)/2

扫一次数组即可找到最小值a,时间复杂度O(n)

设 S = 修改第一项后所有数组项之和,  求和复杂度为O(n)

则被替换掉的第一项为  a1=Sn-S-1

总的时间复杂度为 O(1)+O(n)+O(n) = O(n)

根据该算法写出程序很简单,就不写了

主要是解题过程中没有太考虑题目中给的1M这个数字,一面的时候被问到求和溢出怎么办?

当时我一想,如果要考虑溢出,必然是要处理大数问题,以前没有看到大数就头疼……所以立马想了个绕过大数加法的方法,如下:

设定另外一个数组b[N]

用 a, a+1,a+2....a+n-1依次分别减去原数组,得到的差放在该数组里,此求差过程复杂度为O(n)

对该数组各项求和即可得到Sn-S

面试官让证明一下我的设想,当时还没有给我纸和笔,用手在桌子上比划了一下没想出来,回来躺在床上想了一会就想出来了,也没什么难度: 

相减求和后的数组,最差情况下应该是连续n/2个负数或者正数相加,如果不溢出,后面正负混合相加的话肯定不会溢出;这种情况下的最差特殊情况就是,原数列按照降序排列(除了第一项被替换掉了),而我们减时所用数列是增序排列。所得结果将是1个正数,n/2-1个负数,n/2个正数;而且我们相当于用最大的n/2个数减去最小的n/2个数,差值之和最大,取到了最差情况,我们只考虑后面一半求和的情况即可(前面有个-1不方便处理):

S(n/2) = (n-1) + (n-3) + (n-5)+ .....+ 1    (n为奇数时最后一项是0,不影响我们讨论数量级计算溢出)

   = [(n-1)+1] * n/4 = n^2/4

题目中给定n最大为1M = 1024*1024

那么S(n/2)的最大量级为1024^4 = 2^40

而long long类型为64位,可以存放下该和,成功避免大数问题。

直接求和办法,一是和可能溢出,二是面试官要求把原始数组改称long long的话(即a可以也可能很大,求和时稍微加一下就会溢出)就得考虑大数求解了;而这种差值办法可以直接消掉a,求和只和n相关,和a无关。

原创粉丝点击