2014年百度实习电话面试

学姐接到百度电话面试,我上网查了查面经

如下:

第一题,介绍熟悉的排序算法。(狂问时间复杂度,感觉百度很关注这个。)


第二题:已经排序两个正整数数组,等长,如何找到两个数组里面全部数的中位数


第三题  两个文件各存50亿个url,求两个文件里同时出现的url。(hash表)

给定a、b两个文件,各存放50亿个url,每个url各占64字节,内存限制是4G,让你找出a、b文件共同的url?


方案1:可以估计每个文件安的大小为50G×64=320G,远远大于内存限制的4G。所以不可能将其完全加载到内存中处理。考虑采取分而治之的方法。s 遍历文件a,对每个url求取 ,然后根据所取得的值将url分别存储到1000个小文件(记为 )中。这样每个小文件的大约为300M。s 遍历文件b,采取和a相同的方式将url分别存储到1000各小文件(记为 )。这样处理后,所有可能相同的url都在对应的小文件( )中,不对应的小文件不可能有相同的url。然后我们只要求出1000对小文件中相同的url即可。s 求每对小文件中相同的url时,可以把其中一个小文件的url存储到hash_set中。然后遍历另一个小文件的每个url,看其是否在刚才构建的hash_set中,如果是,那么就是共同的url,存到文件里面就可以了。


方 案2:如果允许有一定的错误率,可以使用Bloom filter,4G内存大概可以表示340亿bit。将其中一个文件中的url使用Bloom filter映射为这340亿bit,然后挨个读取另外一个文件的url,检查是否与Bloom filter,如果是,那么该url应该是共同的url(注意会有一定的错误率)。


思路:

可以估计每个文件的大小为5G*64=300G,远大于4G。所以不可能将其完全加载到内存中处理。考虑采取分而治之的方法。 
遍历文件a,对每个url求取hash(url)%1000,然后根据所得值将url分别存储到1000个小文件(设为a0,a1,...a999)当中。这样每个小文件的大小约为300M。遍历文件b,采取和a相同的方法将url分别存储到1000个小文件(b0,b1....b999)中。这样处理后,所有可能相同的url都在对应的小文件(a0 vs b0, a1 vs b1....a999 vs b999)当中,不对应的小文件(比如a0 vs b99)不可能有相同的url。然后我们只要求出1000对小文件中相同的url即可。 
比如对于a0 vs b0,我们可以遍历a0,将其中的url存储到hash_map当中。然后遍历b0,如果url在hash_map中,则说明此url在a和b中同时存在,保存到文件中即可。 
如果分成的小文件不均匀,导致有些小文件太大(比如大于2G),可以考虑将这些太大的小文件再按类似的方法分成小小文件即可。



第四题 经典的 字符串反转 问题, 后来说,句子里面的单词反转。


第五题   字典查找问题,给定一个英文字典,给出12个不重复的字母,如果字典里的单词由这12个字母或12字母里面的某几个字母组成,则把此单词挑出来。


问题就到这里,gg问我有没有什么问题,问问面试官所在项目组是做什么工作的,可不可以介绍下,然后,不管结果如何,一定要通知我一下,然后他说他不方便现在说。


另外一份面经:

我的简历上写的主要是做视频图像处理的,然后写了自己的编程能力。所以感觉整个面试的问题都是围绕简历上图像处理、C、C++编程的问题展开的。(所以建议对自己的简历一定要熟悉,基础知识要扎实,不会的东西还是不要吹比较好==|||,不然问的东西都不知道会死的很难看)。


按照我的回忆,问了下面的内容:
1、简单自我介绍(我事先打了个草稿,就按照草稿上的东西说的,说完之后面试官笑了一下,可能是觉得我说的官方了吧^^)
2、项目介绍(介绍完之后,他又问了几个项目的问题,不过感觉他不是很懂)
      图像处理部分:
3、香农定理、奈奎斯特定理(他问我的是香农定理 ,但是给我的提示是奈奎斯特定理 ,所以他自己也没分清吧==|||)
4、常见低通滤波器、滤波器的原理。
    C、C++部分的问题:
这个部分是他考的重点,很多问题他都是先抛过来,我给了答案之后他会再十分仔细的追问,问很本质的问题,就是考察对C、C++的认知够不够吧,有点招架不住。
5、this指针怎么用、作用。


6、一个指向类的指针占用内存大小。


7、new delete和malloc free的区别。(我说new可以重载,他又问为什么可以重载)


8、const的作用、好处。

     一个const型的函数和普通的函数特别的地方在哪里?


9、引用和指针的区别。


10、如果叫你设计函数strcpy(),你要考虑哪些问题?给你5分钟时间,一一列举。(感觉这个问题是考你思维缜不缜密,想的全不全面)
       为什么strcpy() 会返回一个指针型的返回值?有什么好处?


11、编程过程中遇到过哪些内存崩溃的问题,解决办法,怎么避免?(因为简历中我写了阅读代码5万行以上,写代码2万行以上,所以他考了我这个吧)


12、代码执行的时候有时候内存会不够,怎么保证内存足够?
      数据结构的问题:


12、你所知道的排序算法及复杂度,其中哪些是稳定的,哪些是不稳定的,为什么。
     最后一个是智力题:


13、25辆车,5个赛道,没有秒表,可以看到跑的快慢,最少比多少次,能测出前三名?(这个问题他给了我很多的时间,我给的答案是8,他说错了不过做不出来也没关系)

 最后他就问我还有什么问题吗?我就问了一些无关紧要的问题。挂电话的时候是6:00,面了整整一个小时~我的处女面就这样华丽丽的结束啦!


百度贴吧测试部门实习生电话面试

来自:http://blog.csdn.net/zxxyyxf/article/details/6547225

1.内存如何分配?

答:  内存分配方式有三种:

 (1)从静态存储区域分配。内存在程序编译的时候就已经分配好,这块内存在程序的整个运行期间都存在。例如全局变量,static变量。

 (2)在栈上创建。在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放。栈内存分配运算内置于处理器的指令集中,效率很高,但是分配的内存容量有限。

 (3)从堆上分配,亦称动态内存分配。程序在运行的时候用malloc或new申请任意多少的内存,程序员自己负责在何时用free或delete释放内存。动态内存的生存期由我们决定,使用非常灵活,但问题也最多。

参考:http://www.cnblogs.com/gaochaooo/archive/2009/09/03/1559764.html

 

2.TCP/IP三次握手过程?第三次握手失败会如何?

答: 在TCP/IP协议中,TCP协议提供可靠的连接服务,采用三次握手建立一个连接。 
第一次握手:建立连接时,客户端发送syn包(syn=j)到服务器,并进入SYN_SEND状态,等待服务器确认; 
第二次握手:服务器收到syn包,必须确认客户的SYN(ack=j+1),同时自己也发送一个SYN包(syn=k),即SYN+ACK包,此时服务器进入SYN_RECV状态; 
第三次握手:客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ack=k+1),此包发送完毕,客户端和服务器进入ESTABLISHED状态,完成三次握手。 
完成三次握手,客户端与服务器开始传送数据,在上述过程中,还有一些重要的概念: 
未连接队列:在三次握手协议中,服务器维护一个未连接队列,该队列为每个客户端的SYN包(syn=j)开设一个条目,该条目表明服务器已收到SYN包,并向客户发出确认,正在等待客户的确认包。这些条目所标识的连接在服务器处于Syn_RECV状态,当服务器收到客户的确认包时,删除该条目,服务器进入ESTABLISHED状态。 
Backlog参数:表示未连接队列的最大容纳数目。 
SYN-ACK 重传次数:服务器发送完SYN-ACK包,如果未收到客户确认包,服务器进行首次重传,等待一段时间仍未收到客户确认包,进行第二次重传,如果重传次数超过系统规定的最大重传次数,系统将该连接信息从半连接队列中删除。注意,每次重传等待的时间不一定相同。

半连接存活时间:是指半连接队列的条目存活的最长时间,也即服务从收到SYN包到确认这个报文无效的最长时间,该时间值是所有重传请求包的最长等待时间总和。有时我们也称半连接存活时间为Timeout时间、SYN_RECV存活时间。

 

3算法:单链表中查找倒数第n个元素,如何测试?

通过一次遍历找到单链表中倒数第n个节点,链表可能相当大,可使用辅助空间,但是辅助空间的数目必须固定,不能和n有关。

单向链表的特点是遍历到末尾后不能反向重数N个节点。因此必须在到达尾部的同时找到倒数第N个节点。

不管是顺数n个还是倒数n个,其实都是距离-标尺问题。标尺是一段距离可以用线段的两个端点来衡量,我们能够判断倒数第一个节点,因为他的next==NULL。如果我们用两个指针,并保持他们的距离为n,那么当这个线段的右端指向末尾节点时,左端节点就指向倒数第n个节点。

测试考虑:结点个数是否大于n;链表为空的情况;……(可补充)

01

iNode * GetLastNnode(iNode * head, int n)

02

{

03

    iNode * pfirst=head;

04

    iNode *psecond=head;

06

    int counter;

07

    //第1步:建立标尺,移动pfirst N步

08

    for(counter=0; counter

09

    {

10

        if(NULL == pfirst)

11

        break; // 此时pfirst->next无意义

12

        pfirst=pfirst->next;

13

    }

15

    if(n != counter) //长度不够n,未找到倒数第n个节点

16

        return NULL;

18

    //第2步:保持距离让标尺向右移动,直到右端指向末尾,左端即结果

19

    while(pfirst!=NULL) 

20

    {

21

        pfirst=pfirst->next;

22

        psecond=psecond->next;

23

    }

24

    return psecond;

25

}

28

iNode * GetLastNnode ( iNode *head, int n)

29

{

30

    iNode * pfirst = head;

31

    iNode * psecond = NULL;//可能没有n个

32

    while( n-- > 0 && (pfirst!= NULL)

33

    {

34

        pfirst = pfirst ->next;

35

    }

37

    if(pfirst!= NULL)// 有n个节点

38

        psecond = head;

40

    while(pfirst!=NULL)

41

    {

42

         pfirst = pfirst ->next;

43

         psecond = psecond ->next;

44

    }

45

    return psecond; //只有一个出口,无论是否有n个节点,都能返回正确值

46

}

                                                                           

附加1:一次遍历单向链表找到中间节点

和上面的思路类似,设置2个指针,一个走2步时,另一个走1步。那么一个走到头时,另一个走到中间。

01

iNode * GetMiddleNode ( iNode *head )

02

{

03

    iNode *p1 = head;

04

    iNode *p2 = p1;

05

    while( p2 )

06

    {

07

        p2 = p2->next;

08

        if(p2)

09

        {

10

            p2 = p2->next;

11

            p1=p1->next;

12

        }

13

    }

14

    return p1;

15

}

附加2:找到第2/3个结点

 我们用两个指针,一个指针每次向后移动2个,一个指针每次向后移动3个。

 

4.如何测试登陆框:

快捷键的使用是否正常:
TAB 键的使用是否正确;上下左右键是否正确;界面如果支持 ESC键 看是否正常的工作;ENTER 键的使用是否正确切换时是否正常。

 

布局美感:
界面的布局是否符合人的审美的标准,具体因人而依

输入框的功能:
输入合法的用户名和密码可以成功进入
输入合法的用户名和不合法密码不可以进入,并给出合理的提示
输入不合法的用户名和正确密码不可以进入,并给出合理的提示
输入不合法的用户名和不正确的密码不可以进入,并给出合理的提示
不合法的用户名有:不正确的用户名;使用了字符大于用户名的限制;
正常用户名不允许的特殊字符、空的用户名,系统(操作系统和应用系统)的保留字符
不合法的密码有:空密码(除有特殊规定的),错误的密码,字符大于密码的限制
正常密码不允许的特殊字符,系统(操作系统和应用系统)的保留字符

界面的链接:
对于界面有链接的界面,要测试界面上的所有的链接都正常或者给出合理的提示

 

补充:
输入框是否支持 复制和黏贴 和移动
密码框显示的不要是具体的字符,要是一些密码的字符
验证用户名前有空格是否可以进入,一般情况可以。
验证用户名是否区分大小写。(有的软件是区分大小写的)
验证必填项为空,是否允许进入。
验证登录的次数是否有限制。从安全角度考虑,有些安全级别高的软件会考虑这方面的限制。

 

再补充:

同一用户名在不同地点登陆是否出现问题;

登陆有效时间(session值);



以下来自:http://blog.csdn.net/hackbuteer1/article/details/7581306

一面:
第一题、任意给一个数,试证明这个数的某个倍数的十进制表示是01串,比如3的倍数111是二进制表示,5的倍数10是二进制表示,等等。
假设序列1,11,111,1111…用A1~AN标识,下脚标N即为1的个数,如:A1=1,A2=11,A3=111…
其中没有一个是N的倍数,即AK mod N不等于0(K属于1~N),并且AK mod N的余数各不相同,设它们为a1,a2,a3,…,aN,但AK mod N的余数最多只有N-1个不同,则由鸽巢原理可知,a1,a2,a3,…,aN中必有两个相同,即ai=aj(j>i),则Aj-Ai=0(mod N),Aj-Ai即为所求的0和1组成的十进制数M,得证。

第二题、证明素数有无穷多个。
     假若素数只有有限多个,设最大的一个是P,从2到P的全体素数是:
  2,3,5,7,11……,P。
  所有的素数都在这里,此外再没有别的素数了。
  现在,我们来考察上面从2到P的全体素数相乘、再加上1这个数,设它是A,即
  A=2×3×5×7×11×……×P+1。
  A是一个大于1的正整数,它不是素数,就是合数。
  如果A是素数,那么,就得到了一个比素数P还要大的素数,这与素数P是最大素数的假设矛盾。
  如果A是合数,那么,它一定能够被某个素数整除,设它能被g整除。
  因为A被从2到P的任何一个素数除,余数都是1,就是都不能整除,而素数g是能整除A的,所以素数g不在从2到P的全体素数之中。这说明素数g是一个比素数P更大的素数,这又与P是最大的素数的假设矛盾。
  上面的证明否定了素数只有有限多个的假定,这就证明了素数是无穷多个。

第三题、给一个很大的数组,里面有两个数只出现过一次,其他数都出现过两次,把这两个数找出来。
很简单,根据所有数的异或结果,将数字分为两组,然后找出这两个数。前面我的blog里有这个题的介绍的。
第四题、把一个链表逆过来,要求空间复杂度O(1),这个算简单的。

[cpp]  view plain copy
  1. /* 
  2. ========================== 
  3. 功能:链表逆序 
  4. (链表的头变成链表的尾,链表的尾变成头) 
  5. 返回:指向链表表头的指针 
  6. ========================== 
  7. */  
  8. struct node *Reverse (struct node *head)  
  9. {  
  10.     struct node *p;         //临时存储  
  11.     struct node *p1;        //存储返回结果  
  12.     struct node *p2;        //源结果节点一个一个取  
  13.   
  14.     p1 = NULL;            //开始颠倒时,已颠倒的部分为空  
  15.     p2 = head;            //p2指向链表的头节点  
  16.     while(p2 != NULL)  
  17.     {  
  18.         p = p2->next;  
  19.         p2->next = p1;  
  20.         p1 = p2;  
  21.         p2 = p;  
  22.     }  
  23.     head = p1;  
  24.     return head;  
  25. }  

二面:
1、是如何统计代码行数以及注释的行数,并写出具体的实现代码。
代码行数是按照\n数的,行注释//需要注意//...\n算一个注释,但注意//...\n之间的//与/**/不算注释。
/**/要注意/* /* */ 等于一个注释, 也就是一旦遇见/*之后就要记下来,一直匹配到*/才算一个完整的注释,中间的内容随便它是什么,包括//可能也嵌套在其中。

2、要求用最快的速度求两个数组的交集,提示数组中的元素是无序的。写出具体的实现代码。
如果哈希真的是O(1)的,那么可以达到O(n+m),否则就是nlogn + mlogm。
3、写程序,将一个浮点数转化为字符串。。
先将浮点数赋值给一个int类型的整数,然后分别将整数部分、小数部分转化为字符串就可以了。
4、下面两个printf的输出结果是什么?为什么会有这样的结果?
[cpp]  view plain copy
  1. int main(void)  
  2. {  
  3.     char a = 255;  
  4.     printf("%d\n",sizeof(++a));  
  5.     printf("%d\n",a);  
  6.     return 0;  
  7. }  
输出结果是:1               -1
第一个输出的是字符类型占用的内存大小,一个字符类型占用一个字节的大小,所以输出1
由于255的二进制表示是1111 1111,将其作为int类型输出的时候,由于最高位是1,表示的是一个负数,其表示的数字就是将最高位后面的7个1取反后在加上1,表示的就是-1,所以第二个输出应该是-1。

5、下面代码的输出是什么?
[cpp]  view plain copy
  1. char *c[] = {"ENTER","NEW","POINT","FIRST"};  
  2. char **cp[] = { c + 3 , c + 2 , c + 1 , c};  
  3. char ***cpp = cp;  
  4.   
  5. int main(void)  
  6. {  
  7.     printf("%s",**++cpp);  
  8.     printf("%s",*--*++cpp+3);  
  9.     printf("%s",*cpp[-2]+3);  
  10.     printf("%s\n",cpp[-1][-1]+1);  
  11.   
  12.     return 0;  
  13. }  
c是一个指针数组,一个数组,元素是char*类型的指针,值分别是那些字符串(的首地址)
c[0] = "ENTER"
c[1] = "NEW"
c[2] = "POINT"
c[3] = "FIRST"
而[]和*是本质一样的运算,即c[i]=*(c+i)
c和c+i都是char *[]类型,它可以退化成char **类型,再看cp,它正好是一个char **的数组,来看它的值:
cp[0] = c + 3
cp[1] = c + 2
cp[2] = c + 1
cp[3] = c
再引用一次看得清楚些
cp[0][0]=c[3]="FIRST",etc
cp是char **[]类型,它可以退化成char ***类型,看最后的cpp,它正是char ***类型,它是一个指针变量,和上面两个不同,上面两个是数组。

1、printf("%s",**++cpp); 
++cpp 的值是cp+1,引用一次后是cp[1]再引用是*cp[1]=c[2]="POINT",第一句的输出
2、printf("%s",*--*++cpp+3); 
再++cpp 的值是cp+2,引用一次是cp[2]=c+1,再对这进行--,减后是c再引用是c[0]="ENTER"再+3,字符串指针指到"ER",输出是"ER"
3、printf("%s",*cpp[-2]+3); 
这时cpp的值是cp+2,cpp[-2]=*(cpp-2)=*(cp+2-2)=cp[0]=c+3,再引用是c[3]="FIRST",+3 字符串指针指到"ST",输出是"ST"
4、printf("%s\n",cpp[-1][-1]+1); 
cpp还是cp+2,cpp[-1]=*(cpp-1)=*(cp+2-1)=cp[1]=c+2,再[-1]得*(c+2-1)=c[1]="NEW",+1字符串指针指到"EW",输出是"EW"


三面:
1、给定两个排好序的数组A和B,他们中的元素个数都是n,求他们所有元素的中位数。要求:时间复杂度为O(logn),空间复杂度为O(1)。(二分查找)
http://blog.csdn.net/hackbuteer1/article/details/7584838    实现代码
2、有两个数组a、b,大小都为n,数组中元素的值是整数类型、无序;要求:通过交换a,b中的元素,使数组a元素的和与数组b元素的和之间的差最小?
3、对已排好序的数组A,一般来说可用二分查找可以很快找到。现有一特殊数组A[],它是循环递增的,如A[]={ 17 19 20 25 1 4 7 9},
试在这样的数组中找一元素x,看看是否存在。
请写出你的算法,必要时可写伪代码,并分析其空间、时间复杂度。
http://blog.csdn.net/hackbuteer1/article/details/7581596       实现代码


你可能感兴趣的:(学习,学习)