百度最新面试题集锦

分类: 面试珠玑2012-03-13 15:10 17272人阅读 评论(67) 收藏 举报

1、实现一个函数,对一个正整数n ,算得到1需要的最少操作次数。操作规则为:如果n 为偶数,将其除以2;如果n 为奇数,可以加1或减1;一直处理下去。

例子:

func(7) = 4,可以证明最少需要4次运算

n = 7

n-1 6

n/2 3

n-1 2

n/2 1

要求:实现函数(实现尽可能高效) int func(unsign int n);n 为输入,返回最小的运算次数。给出思路(文字描述) ,完成代码,并分析你算法的时间复杂度。

答:

[cpp] view plaincopy 1. int func(unsigned int n)

2. {

3. if (n == 1)

4. return 0;

5. if (n % 2 == 0)

6. return 1 + func(n/2);

7. int x = func(n + 1);

8. int y = func(n - 1);

9. if (x > y)

10. return y+1;

11. else

12. return x+1;

13. }

假设n 表示成二进制有x bit,可以看出计算复杂度为O(2^x),也就是O(n)。

将n 转换到二进制空间来看(比如7为111,6为110):

- 如果最后一位是0,则对应于偶数,直接进行除2操作。

- 如果最后一位是1,情况则有些复杂。

**如果最后几位是???01,则有可能为???001,???1111101。在第一种情况下,显然应该-1;在第二种情况下-1和+1最终需要的步数相同。所以在???01的情况下,应该选择-1操作。

**如果最后几位是???011,则有可能为???0011,???11111011。在第一种情况下,+1和-1最终需要的步数相同;在第二种情况下

+1步数更少些。所以在???011的情况下,应该选择+1操作。

**如果最后有更多的连续1,也应该选择+1操作。

如果最后剩下的各位都是1,则有11时应该选择-1;111时+1和-1相同;1111时应选择+1;大于四个1时也应该选择+1;

[cpp] view plaincopy 1. int func(unsigned int n)

2. {

3. if (n == 1)

4. return 0;

5. if (n % 2 == 0)

6. return 1 + func(n/2);

7. if (n == 3)

8. return 2;

9. if (n&2)

10. return 1 + func(n+1);

11. else

12. return 1 + func(n-1);

13. }

由以上的分析可知,奇数的时候加1或减1,完全取决于二进制的后两位,如果后两位是10、00那么肯定是偶数,选择除以2,如果后两位是01、11,那么选择结果会不一样的,如果是*****01,那么选择减1,如果是*****11,那么选择加1,特殊情况是就是n 是3的时候,选择减1操作。

非递归代码如下:

[cpp] view plaincopy

1. // 非递归写法

2. int func(int n)

3. {

4. int count = 0;

5. while (n > 1)

6. {

7. if (n % 2 == 0)

8. n >>= 1;

9. else if (n == 3)

10. n--;

11. else

12. {

13. if (n&2) // 二进制是******11时

14. n++;

15. else // 二进制是******01时

16. n--;

17. }

18. count++;

19. }

20. return count;

21. }

另外一种写法如下:

[cpp] view plaincopy 1. // 非递归写法

2. int func(int n)

3. {

4. int count = 0;

5. while (n > 1)

6. {

7. if (n % 2 == 0) // n % 4等于0或2

8. n >>= 1;

9. else if (n == 3)

10. n--;

11. else

12. n += (n % 4 - 2); // n % 4等于1或3

13. count++;

14. }

15. return count;

16. }

2、找到满足条件的数组

给定函数d(n)=n+n的各位之和,n 为正整数,如d(78)=78+7+8=93。这样这个函数可以看成一个生成器,如93可以看成由78生成。

定义数A :数A 找不到一个数B 可以由d(B)=A,即A 不能由其他数生成。现在要写程序,找出1至10000里的所有符合数A 定义的数。

回答:

申请一个长度为10000的bool 数组,每个元素代表对应的值是否可以有其它数生成。开始时将数组中的值都初始化为false 。

由于大于10000的数的生成数必定大于10000,所以我们只需遍历1到10000中的数,计算生成数,并将bool 数组中对应的值设置为true ,表示这个数可以有其它数生成。

最后bool 数组中值为false 的位置对应的整数就是不能由其它数生成的。

3、一个大的含有50M 个URL 的记录,一个小的含有500个URL 的记录,找出两个记录里相同的URL 。

回答:

首先使用包含500个url 的文件创建一个hash_set。

然后遍历50M 的url 记录,如果url 在hash_set中,则输出此url 并从hash_set中删除这个url 。

所有输出的url 就是两个记录里相同的url 。

4、海量日志数据,提取出某日访问百度次数最多的那个IP 。

回答:

IP 地址最多有2^32=4G种取值可能,所以不能完全加载到内存中。

可以考虑分而治之的策略,按照IP 地址的hash(IP)%1024值,将海量日志存储到1024个小文件中。每个小文件最多包含4M 个IP 地址。

对于每个小文件,可以构建一个IP 作为key ,出现次数作为value 的hash_map,并记录当前出现次数最多的1个IP 地址。 有了1024个小文件中的出现次数最多的IP ,我们就可以轻松得到总体上出现次数最多的IP 。

5、有10个文件,每个文件1G ,每个文件的每一行都存放的是用户的query ,每个文件的query 都可能重复。如何按照query 的频度排序?

回答:

1)读取10个文件,按照hash(query)%10的结果将query 写到对应的文件中。这样我们就有了10个大小约为1G 的文件。任意一个query 只会出现在某个文件中。

2)对于1)中获得的10个文件,分别进行如下操作

-利用hash_map(query ,query_count)来统计每个query 出现的次数。

-利用堆排序算法对query 按照出现次数进行排序。

-将排序好的query 输出的文件中。

这样我们就获得了10个文件,每个文件中都是按频率排序好的query 。

3)对2)中获得的10个文件进行归并排序,并将最终结果输出到文件中。

6、蚂蚁爬杆问题

有一根27厘米长的细木杆,在第3厘米,7厘米,11厘米,17厘米,23厘米这五个位置上各有一只蚂蚁,木杆很细,不能同时通过两只蚂蚁,开始时,蚂蚁的头朝向左还是右是任意的,他们只会朝前走或掉头,但不会后退,当两只蚂蚁相遇后,蚂蚁会同时掉头朝反方向走,假设蚂蚁们每秒钟可以走1厘米的距离。求所有蚂蚁都离开木杆的最小时间和最大时间。

答案:

两只蚂蚁相遇后,各自掉头朝相反方向走。如果我们不考虑每个蚂蚁的具体身份,这和两只蚂蚁相遇后,打个招呼继续向前走没有什么区别。

所有蚂蚁都离开木杆的最小时间为

max(min(3,27-3),min(7,27-7), min(11,27-11), min(17,27-17),min(23,27-23))=11

所有蚂蚁都离开木杆的最大时间为

max(max(3,27-3),max(7,27-7), max(11,27-11), max(17,27-17),max(23,27-23))=24

7、当在浏览器中输入一个url 后回车,后台发生了什么?比如输入url 后,你看到了百度的首页,那么这一切是如何发生的呢? 回答:

简单来说有以下步骤:

1、查找域名对应的IP 地址。这一步会依次查找浏览器缓存,系统缓存,路由器缓存,ISPDNS 缓存,根域名服务器。

2、向IP 对应的服务器发送请求。

3、服务器响应请求,发回网页内容。

4、浏览器解析网页内容。

当然,由于网页可能有重定向,或者嵌入了图片,AJAX ,其它子网页等等,这4个步骤可能反复进行多次才能将最终页面展示给用户。

8、判断两棵树是否相等,请实现两棵树是否相等的比较,相等返回1,否则返回其他值,并说明算法复杂度。

数据结构为:

[cpp] view plaincopy

1. typedef struct TreeNode

2. {

3. char c;

4. TreeNode *leftchild;

5. TreeNode *rightchild;

6. }TreeNode;

函数接口为:int CompTree(TreeNode* tree1,TreeNode* tree2);

注:A 、B 两棵树相等当且仅当RootA->c==RootB-->c,而且A 和B 的左右子树相等或者左右互换相等。

递归方法:

[cpp] view plaincopy 1. bool CompTree(TreeNode *tree1, TreeNode *tree2)

2. {

3. if (tree1 == NULL && tree2 == NULL)

4. return true ;

5. if (tree1 == NULL || tree2 == NULL)

6. return false ;

7. if (tree1->c != tree2->c)

8. return false ;

9. if ( (CompTree(tree1->leftchild, tree2->leftchild) && CompTree(tree1->rightchild, tree2->rightchild)) || Comp

Tree(tree1->leftchild, tree2->rightchild) && CompTree(tree1->rightchild, tree2->leftchild))

10. return true ;

11. } 时间复杂度:

在树的第0层,有1个节点,我们会进行1次函数调用;

在树的第1层,有2个节点,我们可能会进行4次函数调用;

在树的第2层,有4个节点,我们可能会进行16次函数调用;

....

在树的第x 层,有2^x个节点,我们可能会进行(2^x)^2次函数调用;

所以假设总节点数为n ,则算法的复杂度为O(n^2)。

腾讯面试题:求一个论坛的在线人数,假设有一个论坛,其注册ID 有两亿个,每个ID 从登陆到退出会向一个日志文件中记下登陆时间和退出时间,要求写一个算法统计一天中论坛的用户在线分布,取样粒度为秒。

回答:

一天总共有3600*24=86400秒。

定义一个长度为86400的整数数组intdelta[86400],每个整数对应这一秒的人数变化值,可能为正也可能为负。开始时将数组元素都初始化为0。

然后依次读入每个用户的登录时间和退出时间,将与登录时间对应的整数值加1,将与退出时间对应的整数值减1。

这样处理一遍后数组中存储了每秒中的人数变化情况。

定义另外一个长度为86400的整数数组intonline_num[86400],每个整数对应这一秒的论坛在线人数。

假设一天开始时论坛在线人数为0,则第1秒的人数online_num[0]=delta[0]。第n+1秒的人数

online_num[n]=online_num[n-1]+delta[n]。

这样我们就获得了一天中任意时间的在线人数。

9、三个警察和三个囚徒的过河问题

三个警察和三个囚徒共同旅行。一条河挡住了去路,河边有一条船,但是每次只能载2人。存在如下的危险:无论在河的哪边,当囚徒人数多于警察的人数时,将有警察被囚徒杀死。问题:请问如何确定渡河方案,才能保证6人安全无损的过河。

答案:第一次:两囚徒同过,回一囚徒

第二次:两囚徒同过,回一囚徒

第三次:两警察同过,回一囚徒一警察(此时对岸还剩下一囚徒一警察,是安全状态)

第四次:两警察同过,回一囚徒(此时对岸有3个警察,是安全状态)

第五次:两囚徒同过,回一囚徒

第六次:两囚徒同过;over

10、从300万字符串中找到最热门的10条

搜索的输入信息是一个字符串,统计300万输入信息中的最热门的前10条,我们每次输入的一个字符串为不超过255byte ,内存使用只有1G 。请描述思想,写出算法(c 语言),空间和时间复杂度。

答案:

300万个字符串最多(假设没有重复,都是最大长度)占用内存3M*1K/4=0.75G。所以可以将所有字符串都存放在内存中进行处理。

可以使用key 为字符串(事实上是字符串的hash 值),值为字符串出现次数的hash 来统计每个每个字符串出现的次数。并用一个长度为10的数组/链表来存储目前出现次数最多的10个字符串。

这样空间和时间的复杂度都是O(n)。

11、如何找出字典中的兄弟单词。给定一个单词a ,如果通过交换单词中字母的顺序可以得到另外的单词b ,那么定义b 是a 的兄弟单词。现在给定一个字典,用户输入一个单词,如何根据字典找出这个单词有多少个兄弟单词?

答案:

使用hash_map和链表。

首先定义一个key ,使得兄弟单词有相同的key ,不是兄弟的单词有不同的key 。例如,将单词按字母从小到大重新排序后作为其key ,比如bad 的key 为abd ,good 的key 为dgoo 。

使用链表将所有兄弟单词串在一起,hash_map的key 为单词的key ,value 为链表的起始地址。

开始时,先遍历字典,将每个单词都按照key 加入到对应的链表当中。当需要找兄弟单词时,只需求取这个单词的key ,然后到hash_map中找到对应的链表即可。

这样创建hash_map时时间复杂度为O(n),查找兄弟单词时时间复杂度是O(1)。

12、找出数组中出现次数超过一半的数,现在有一个数组,已知一个数出现的次数超过了一半,请用O(n)的复杂度的算法找出这个数。 答案1:

创建一个hash_map,key 为数组中的数,value 为此数出现的次数。遍历一遍数组,用hash_map统计每个数出现的次数,并用两个值存储目前出现次数最多的数和对应出现的次数。

这样可以做到O(n)的时间复杂度和O(n)的空间复杂度,满足题目的要求。

但是没有利用“一个数出现的次数超过了一半”这个特点。也许算法还有提高的空间。

答案2:

使用两个变量A 和B ,其中A 存储某个数组中的数,B 用来计数。开始时将B 初始化为0。

遍历数组,如果B=0,则令A 等于当前数,令B 等于1;如果当前数与A 相同,则B=B+1;如果当前数与A 不同,则令B=B-1。遍历结束时,A 中的数就是要找的数。

这个算法的时间复杂度是O(n),空间复杂度为O(1)。

13、找出被修改过的数字

n 个空间(其中n

例如:n=6,a=2,原始的串为5,3,7,6,2,4。现在被别人修改为-1,3,7,6,2,4。现在希望找到5。

回答:

由于修改的数不是最小的,所以遍历第二个空间到最后一个空间可以得到a 的值。

a 到a+n-1这n 个数的和是total=na+(n-1)n/2。

将第二个至最后一个空间的数累加获得sub_total。

那么被修改的数就是total-sub_total。

14、设计DNS 服务器中cache 的数据结构。

要求设计一个DNS 的Cache 结构,要求能够满足每秒5000以上的查询,满足IP 数据的快速插入,查询的速度要快。(题目还给出了一系列的数据,比如:站点数总共为5000万,IP 地址有1000万,等等)

回答:

DNS 服务器实现域名到IP 地址的转换。

每个域名的平均长度为25个字节(估计值),每个IP 为4个字节,所以Cache 的每个条目需要大概30个字节。

总共50M 个条目,所以需要1.5G 个字节的空间。可以放置在内存中。(考虑到每秒5000次操作的限制,也只能放在内存中。) 可以考虑的数据结构包括hash_map,字典树,红黑树等等。

15、找出给定字符串对应的序号。

序列Seq=[a,b,…z,aa,ab…az,ba,bb,…bz,…,za,zb,…zz,aaa,…]类似与excel 的排列,任意给出一个字符串s=[a-z]+(由a-z 字符组成的任意长度字符串),请问s 是序列Seq 的第几个。

回答:

注意到每满26个就会向前进一位,类似一个26进制的问题。

比如ab ,则位置为26*1+2;

比如za ,则位置为26*26+1;

比如abc ,则位置为26*26*1+26*2+3;

16、找出第k 大的数字所在的位置。写一段程序,找出数组中第k 大小的数,输出数所在的位置。例如{2,4,3,4,7}中,第一大的数是7,位置在4。第二大、第三大的数都是4,位置在1、3随便输出哪一个均可。

答案:

先找到第k 大的数字,然后再遍历一遍数组找到它的位置。所以题目的难点在于如何最高效的找到第k 大的数。

我们可以通过快速排序,堆排序等高效的排序算法对数组进行排序,然后找到第k 大的数字。这样总体复杂度为O(NlogN)。

我们还可以通过二分的思想,找到第k 大的数字,而不必对整个数组排序。从数组中随机选一个数t ,通过让这个数和其它数比较,我们可以将整个数组分成了两部分并且满足,{x,xx,...,t}

在将数组分成两个数组的过程中,我们还可以记录每个子数组的大小。这样我们就可以确定第k 大的数字在哪个子数组中。 然后我们继续对包含第k 大数字的子数组进行同样的划分,直到找到第k 大的数字为止。

平均来说,由于每次划分都会使子数组缩小到原来1/2,所以整个过程的复杂度为O(logN)。

17、给40亿个不重复的unsigned int的整数,没排过序的,然后再给几个数,如何快速判断这几个数是否在那40亿个数当中? 答案:

unsigned int的取值范围是0到2^32-1。我们可以申请连续的2^32/8=512M的内存,用每一个bit 对应一个unsigned int数字。首先将512M 内存都初始化为0,然后每处理一个数字就将其对应的bit 设置为1。当需要查询时,直接找到对应bit ,看其值是0还是1即可。

18、在一个文件中有10G 个整数,乱序排列,要求找出中位数。内存限制为2G 。

回答:

不妨假设10G 个整数是64bit 的。

2G 内存可以存放256M 个64bit 整数。

我们可以将64bit 的整数空间平均分成256M 个取值范围,用2G 的内存对每个取值范围内出现整数个数进行统计。这样遍历一边10G 整数后,我们便知道中数在那个范围内出现,以及这个范围内总共出现了多少个整数。

如果中数所在范围出现的整数比较少,我们就可以对这个范围内的整数进行排序,找到中数。如果这个范围内出现的整数比较多,我们还可以采用同样的方法将此范围再次分成多个更小的范围(256M=2^28,所以最多需要3次就可以将此范围缩小到1,也就找到了中数)。

19、时分秒针在一天之类重合多少次?(24小时)

2次

而时针和分针重合了22次。

20、将多个集合合并成没有交集的集合。

给定一个字符串的集合,格式如:{aaabbbccc},{bbbddd},{eeefff},{ggg},{dddhhh}要求将其中交集不为空的集合合并,要求合并完成后的集合之间无交集,例如上例应输出{aaabbbcccdddhhh},{eeefff},{ggg}。

(1)请描述你解决这个问题的思路;

(2)请给出主要的处理流程,算法,以及算法的复杂度

(3)请描述可能的改进。

回答:

集合使用hash_set来表示,这样合并时间复杂度比较低。

1、给每个集合编号为0,1,2,3...

2、创建一个hash_map,key 为字符串,value 为一个链表,链表节点为字符串所在集合的编号。遍历所有的集合,将字符串和对应的集合编号插入到hash_map中去。

3、创建一个长度等于集合个数的int 数组,表示集合间的合并关系。例如,下标为5的元素值为3,表示将下标为5的集合合并到下标为3的集合中去。开始时将所有值都初始化为-1,表示集合间没有互相合并。在集合合并的过程中,我们将所有的字符串都合并到编号较小的集合中去。

遍历第二步中生成的hash_map,对于每个value 中的链表,首先找到最小的集合编号(有些集合已经被合并过,需要顺着合并关系数组找到合并后的集合编号),然后将链表中所有编号的集合都合并到编号最小的集合中(通过更改合并关系数组)。

4、现在合并关系数组中值为-1的集合即为最终的集合,它的元素来源于所有直接或间接指向它的集合。

算法的复杂度为O(n),其中n 为所有集合中的元素个数。

题目中的例子:

0:{aaabbbccc}

1:{bbbddd}

2:{eeefff}

3:{ggg}

4:{dddhhh}

生成的hash_map,和处理完每个值后的合并关系数组分别为

aaa:0。[-1,-1,-1,-1,-1]

bbb:0,1。[-1,0,-1,-1,-1]

ccc:0。[-1,0,-1,-1,-1]

ddd:1,4。[-1,0,-1,-1,0]

eee:2。[-1,0,-1,-1,0]

fff:2。[-1,0,-1,-1,0]

ggg:3。[-1,0,-1,-1,0]

hhh:4。[-1,0,-1,-1,0]

所以合并完后有三个集合,第0,1,4个集合合并到了一起,

分类: 面试珠玑2012-03-13 15:10 17272人阅读 评论(67) 收藏 举报

1、实现一个函数,对一个正整数n ,算得到1需要的最少操作次数。操作规则为:如果n 为偶数,将其除以2;如果n 为奇数,可以加1或减1;一直处理下去。

例子:

func(7) = 4,可以证明最少需要4次运算

n = 7

n-1 6

n/2 3

n-1 2

n/2 1

要求:实现函数(实现尽可能高效) int func(unsign int n);n 为输入,返回最小的运算次数。给出思路(文字描述) ,完成代码,并分析你算法的时间复杂度。

答:

[cpp] view plaincopy 1. int func(unsigned int n)

2. {

3. if (n == 1)

4. return 0;

5. if (n % 2 == 0)

6. return 1 + func(n/2);

7. int x = func(n + 1);

8. int y = func(n - 1);

9. if (x > y)

10. return y+1;

11. else

12. return x+1;

13. }

假设n 表示成二进制有x bit,可以看出计算复杂度为O(2^x),也就是O(n)。

将n 转换到二进制空间来看(比如7为111,6为110):

- 如果最后一位是0,则对应于偶数,直接进行除2操作。

- 如果最后一位是1,情况则有些复杂。

**如果最后几位是???01,则有可能为???001,???1111101。在第一种情况下,显然应该-1;在第二种情况下-1和+1最终需要的步数相同。所以在???01的情况下,应该选择-1操作。

**如果最后几位是???011,则有可能为???0011,???11111011。在第一种情况下,+1和-1最终需要的步数相同;在第二种情况下

+1步数更少些。所以在???011的情况下,应该选择+1操作。

**如果最后有更多的连续1,也应该选择+1操作。

如果最后剩下的各位都是1,则有11时应该选择-1;111时+1和-1相同;1111时应选择+1;大于四个1时也应该选择+1;

[cpp] view plaincopy 1. int func(unsigned int n)

2. {

3. if (n == 1)

4. return 0;

5. if (n % 2 == 0)

6. return 1 + func(n/2);

7. if (n == 3)

8. return 2;

9. if (n&2)

10. return 1 + func(n+1);

11. else

12. return 1 + func(n-1);

13. }

由以上的分析可知,奇数的时候加1或减1,完全取决于二进制的后两位,如果后两位是10、00那么肯定是偶数,选择除以2,如果后两位是01、11,那么选择结果会不一样的,如果是*****01,那么选择减1,如果是*****11,那么选择加1,特殊情况是就是n 是3的时候,选择减1操作。

非递归代码如下:

[cpp] view plaincopy

1. // 非递归写法

2. int func(int n)

3. {

4. int count = 0;

5. while (n > 1)

6. {

7. if (n % 2 == 0)

8. n >>= 1;

9. else if (n == 3)

10. n--;

11. else

12. {

13. if (n&2) // 二进制是******11时

14. n++;

15. else // 二进制是******01时

16. n--;

17. }

18. count++;

19. }

20. return count;

21. }

另外一种写法如下:

[cpp] view plaincopy 1. // 非递归写法

2. int func(int n)

3. {

4. int count = 0;

5. while (n > 1)

6. {

7. if (n % 2 == 0) // n % 4等于0或2

8. n >>= 1;

9. else if (n == 3)

10. n--;

11. else

12. n += (n % 4 - 2); // n % 4等于1或3

13. count++;

14. }

15. return count;

16. }

2、找到满足条件的数组

给定函数d(n)=n+n的各位之和,n 为正整数,如d(78)=78+7+8=93。这样这个函数可以看成一个生成器,如93可以看成由78生成。

定义数A :数A 找不到一个数B 可以由d(B)=A,即A 不能由其他数生成。现在要写程序,找出1至10000里的所有符合数A 定义的数。

回答:

申请一个长度为10000的bool 数组,每个元素代表对应的值是否可以有其它数生成。开始时将数组中的值都初始化为false 。

由于大于10000的数的生成数必定大于10000,所以我们只需遍历1到10000中的数,计算生成数,并将bool 数组中对应的值设置为true ,表示这个数可以有其它数生成。

最后bool 数组中值为false 的位置对应的整数就是不能由其它数生成的。

3、一个大的含有50M 个URL 的记录,一个小的含有500个URL 的记录,找出两个记录里相同的URL 。

回答:

首先使用包含500个url 的文件创建一个hash_set。

然后遍历50M 的url 记录,如果url 在hash_set中,则输出此url 并从hash_set中删除这个url 。

所有输出的url 就是两个记录里相同的url 。

4、海量日志数据,提取出某日访问百度次数最多的那个IP 。

回答:

IP 地址最多有2^32=4G种取值可能,所以不能完全加载到内存中。

可以考虑分而治之的策略,按照IP 地址的hash(IP)%1024值,将海量日志存储到1024个小文件中。每个小文件最多包含4M 个IP 地址。

对于每个小文件,可以构建一个IP 作为key ,出现次数作为value 的hash_map,并记录当前出现次数最多的1个IP 地址。 有了1024个小文件中的出现次数最多的IP ,我们就可以轻松得到总体上出现次数最多的IP 。

5、有10个文件,每个文件1G ,每个文件的每一行都存放的是用户的query ,每个文件的query 都可能重复。如何按照query 的频度排序?

回答:

1)读取10个文件,按照hash(query)%10的结果将query 写到对应的文件中。这样我们就有了10个大小约为1G 的文件。任意一个query 只会出现在某个文件中。

2)对于1)中获得的10个文件,分别进行如下操作

-利用hash_map(query ,query_count)来统计每个query 出现的次数。

-利用堆排序算法对query 按照出现次数进行排序。

-将排序好的query 输出的文件中。

这样我们就获得了10个文件,每个文件中都是按频率排序好的query 。

3)对2)中获得的10个文件进行归并排序,并将最终结果输出到文件中。

6、蚂蚁爬杆问题

有一根27厘米长的细木杆,在第3厘米,7厘米,11厘米,17厘米,23厘米这五个位置上各有一只蚂蚁,木杆很细,不能同时通过两只蚂蚁,开始时,蚂蚁的头朝向左还是右是任意的,他们只会朝前走或掉头,但不会后退,当两只蚂蚁相遇后,蚂蚁会同时掉头朝反方向走,假设蚂蚁们每秒钟可以走1厘米的距离。求所有蚂蚁都离开木杆的最小时间和最大时间。

答案:

两只蚂蚁相遇后,各自掉头朝相反方向走。如果我们不考虑每个蚂蚁的具体身份,这和两只蚂蚁相遇后,打个招呼继续向前走没有什么区别。

所有蚂蚁都离开木杆的最小时间为

max(min(3,27-3),min(7,27-7), min(11,27-11), min(17,27-17),min(23,27-23))=11

所有蚂蚁都离开木杆的最大时间为

max(max(3,27-3),max(7,27-7), max(11,27-11), max(17,27-17),max(23,27-23))=24

7、当在浏览器中输入一个url 后回车,后台发生了什么?比如输入url 后,你看到了百度的首页,那么这一切是如何发生的呢? 回答:

简单来说有以下步骤:

1、查找域名对应的IP 地址。这一步会依次查找浏览器缓存,系统缓存,路由器缓存,ISPDNS 缓存,根域名服务器。

2、向IP 对应的服务器发送请求。

3、服务器响应请求,发回网页内容。

4、浏览器解析网页内容。

当然,由于网页可能有重定向,或者嵌入了图片,AJAX ,其它子网页等等,这4个步骤可能反复进行多次才能将最终页面展示给用户。

8、判断两棵树是否相等,请实现两棵树是否相等的比较,相等返回1,否则返回其他值,并说明算法复杂度。

数据结构为:

[cpp] view plaincopy

1. typedef struct TreeNode

2. {

3. char c;

4. TreeNode *leftchild;

5. TreeNode *rightchild;

6. }TreeNode;

函数接口为:int CompTree(TreeNode* tree1,TreeNode* tree2);

注:A 、B 两棵树相等当且仅当RootA->c==RootB-->c,而且A 和B 的左右子树相等或者左右互换相等。

递归方法:

[cpp] view plaincopy 1. bool CompTree(TreeNode *tree1, TreeNode *tree2)

2. {

3. if (tree1 == NULL && tree2 == NULL)

4. return true ;

5. if (tree1 == NULL || tree2 == NULL)

6. return false ;

7. if (tree1->c != tree2->c)

8. return false ;

9. if ( (CompTree(tree1->leftchild, tree2->leftchild) && CompTree(tree1->rightchild, tree2->rightchild)) || Comp

Tree(tree1->leftchild, tree2->rightchild) && CompTree(tree1->rightchild, tree2->leftchild))

10. return true ;

11. } 时间复杂度:

在树的第0层,有1个节点,我们会进行1次函数调用;

在树的第1层,有2个节点,我们可能会进行4次函数调用;

在树的第2层,有4个节点,我们可能会进行16次函数调用;

....

在树的第x 层,有2^x个节点,我们可能会进行(2^x)^2次函数调用;

所以假设总节点数为n ,则算法的复杂度为O(n^2)。

腾讯面试题:求一个论坛的在线人数,假设有一个论坛,其注册ID 有两亿个,每个ID 从登陆到退出会向一个日志文件中记下登陆时间和退出时间,要求写一个算法统计一天中论坛的用户在线分布,取样粒度为秒。

回答:

一天总共有3600*24=86400秒。

定义一个长度为86400的整数数组intdelta[86400],每个整数对应这一秒的人数变化值,可能为正也可能为负。开始时将数组元素都初始化为0。

然后依次读入每个用户的登录时间和退出时间,将与登录时间对应的整数值加1,将与退出时间对应的整数值减1。

这样处理一遍后数组中存储了每秒中的人数变化情况。

定义另外一个长度为86400的整数数组intonline_num[86400],每个整数对应这一秒的论坛在线人数。

假设一天开始时论坛在线人数为0,则第1秒的人数online_num[0]=delta[0]。第n+1秒的人数

online_num[n]=online_num[n-1]+delta[n]。

这样我们就获得了一天中任意时间的在线人数。

9、三个警察和三个囚徒的过河问题

三个警察和三个囚徒共同旅行。一条河挡住了去路,河边有一条船,但是每次只能载2人。存在如下的危险:无论在河的哪边,当囚徒人数多于警察的人数时,将有警察被囚徒杀死。问题:请问如何确定渡河方案,才能保证6人安全无损的过河。

答案:第一次:两囚徒同过,回一囚徒

第二次:两囚徒同过,回一囚徒

第三次:两警察同过,回一囚徒一警察(此时对岸还剩下一囚徒一警察,是安全状态)

第四次:两警察同过,回一囚徒(此时对岸有3个警察,是安全状态)

第五次:两囚徒同过,回一囚徒

第六次:两囚徒同过;over

10、从300万字符串中找到最热门的10条

搜索的输入信息是一个字符串,统计300万输入信息中的最热门的前10条,我们每次输入的一个字符串为不超过255byte ,内存使用只有1G 。请描述思想,写出算法(c 语言),空间和时间复杂度。

答案:

300万个字符串最多(假设没有重复,都是最大长度)占用内存3M*1K/4=0.75G。所以可以将所有字符串都存放在内存中进行处理。

可以使用key 为字符串(事实上是字符串的hash 值),值为字符串出现次数的hash 来统计每个每个字符串出现的次数。并用一个长度为10的数组/链表来存储目前出现次数最多的10个字符串。

这样空间和时间的复杂度都是O(n)。

11、如何找出字典中的兄弟单词。给定一个单词a ,如果通过交换单词中字母的顺序可以得到另外的单词b ,那么定义b 是a 的兄弟单词。现在给定一个字典,用户输入一个单词,如何根据字典找出这个单词有多少个兄弟单词?

答案:

使用hash_map和链表。

首先定义一个key ,使得兄弟单词有相同的key ,不是兄弟的单词有不同的key 。例如,将单词按字母从小到大重新排序后作为其key ,比如bad 的key 为abd ,good 的key 为dgoo 。

使用链表将所有兄弟单词串在一起,hash_map的key 为单词的key ,value 为链表的起始地址。

开始时,先遍历字典,将每个单词都按照key 加入到对应的链表当中。当需要找兄弟单词时,只需求取这个单词的key ,然后到hash_map中找到对应的链表即可。

这样创建hash_map时时间复杂度为O(n),查找兄弟单词时时间复杂度是O(1)。

12、找出数组中出现次数超过一半的数,现在有一个数组,已知一个数出现的次数超过了一半,请用O(n)的复杂度的算法找出这个数。 答案1:

创建一个hash_map,key 为数组中的数,value 为此数出现的次数。遍历一遍数组,用hash_map统计每个数出现的次数,并用两个值存储目前出现次数最多的数和对应出现的次数。

这样可以做到O(n)的时间复杂度和O(n)的空间复杂度,满足题目的要求。

但是没有利用“一个数出现的次数超过了一半”这个特点。也许算法还有提高的空间。

答案2:

使用两个变量A 和B ,其中A 存储某个数组中的数,B 用来计数。开始时将B 初始化为0。

遍历数组,如果B=0,则令A 等于当前数,令B 等于1;如果当前数与A 相同,则B=B+1;如果当前数与A 不同,则令B=B-1。遍历结束时,A 中的数就是要找的数。

这个算法的时间复杂度是O(n),空间复杂度为O(1)。

13、找出被修改过的数字

n 个空间(其中n

例如:n=6,a=2,原始的串为5,3,7,6,2,4。现在被别人修改为-1,3,7,6,2,4。现在希望找到5。

回答:

由于修改的数不是最小的,所以遍历第二个空间到最后一个空间可以得到a 的值。

a 到a+n-1这n 个数的和是total=na+(n-1)n/2。

将第二个至最后一个空间的数累加获得sub_total。

那么被修改的数就是total-sub_total。

14、设计DNS 服务器中cache 的数据结构。

要求设计一个DNS 的Cache 结构,要求能够满足每秒5000以上的查询,满足IP 数据的快速插入,查询的速度要快。(题目还给出了一系列的数据,比如:站点数总共为5000万,IP 地址有1000万,等等)

回答:

DNS 服务器实现域名到IP 地址的转换。

每个域名的平均长度为25个字节(估计值),每个IP 为4个字节,所以Cache 的每个条目需要大概30个字节。

总共50M 个条目,所以需要1.5G 个字节的空间。可以放置在内存中。(考虑到每秒5000次操作的限制,也只能放在内存中。) 可以考虑的数据结构包括hash_map,字典树,红黑树等等。

15、找出给定字符串对应的序号。

序列Seq=[a,b,…z,aa,ab…az,ba,bb,…bz,…,za,zb,…zz,aaa,…]类似与excel 的排列,任意给出一个字符串s=[a-z]+(由a-z 字符组成的任意长度字符串),请问s 是序列Seq 的第几个。

回答:

注意到每满26个就会向前进一位,类似一个26进制的问题。

比如ab ,则位置为26*1+2;

比如za ,则位置为26*26+1;

比如abc ,则位置为26*26*1+26*2+3;

16、找出第k 大的数字所在的位置。写一段程序,找出数组中第k 大小的数,输出数所在的位置。例如{2,4,3,4,7}中,第一大的数是7,位置在4。第二大、第三大的数都是4,位置在1、3随便输出哪一个均可。

答案:

先找到第k 大的数字,然后再遍历一遍数组找到它的位置。所以题目的难点在于如何最高效的找到第k 大的数。

我们可以通过快速排序,堆排序等高效的排序算法对数组进行排序,然后找到第k 大的数字。这样总体复杂度为O(NlogN)。

我们还可以通过二分的思想,找到第k 大的数字,而不必对整个数组排序。从数组中随机选一个数t ,通过让这个数和其它数比较,我们可以将整个数组分成了两部分并且满足,{x,xx,...,t}

在将数组分成两个数组的过程中,我们还可以记录每个子数组的大小。这样我们就可以确定第k 大的数字在哪个子数组中。 然后我们继续对包含第k 大数字的子数组进行同样的划分,直到找到第k 大的数字为止。

平均来说,由于每次划分都会使子数组缩小到原来1/2,所以整个过程的复杂度为O(logN)。

17、给40亿个不重复的unsigned int的整数,没排过序的,然后再给几个数,如何快速判断这几个数是否在那40亿个数当中? 答案:

unsigned int的取值范围是0到2^32-1。我们可以申请连续的2^32/8=512M的内存,用每一个bit 对应一个unsigned int数字。首先将512M 内存都初始化为0,然后每处理一个数字就将其对应的bit 设置为1。当需要查询时,直接找到对应bit ,看其值是0还是1即可。

18、在一个文件中有10G 个整数,乱序排列,要求找出中位数。内存限制为2G 。

回答:

不妨假设10G 个整数是64bit 的。

2G 内存可以存放256M 个64bit 整数。

我们可以将64bit 的整数空间平均分成256M 个取值范围,用2G 的内存对每个取值范围内出现整数个数进行统计。这样遍历一边10G 整数后,我们便知道中数在那个范围内出现,以及这个范围内总共出现了多少个整数。

如果中数所在范围出现的整数比较少,我们就可以对这个范围内的整数进行排序,找到中数。如果这个范围内出现的整数比较多,我们还可以采用同样的方法将此范围再次分成多个更小的范围(256M=2^28,所以最多需要3次就可以将此范围缩小到1,也就找到了中数)。

19、时分秒针在一天之类重合多少次?(24小时)

2次

而时针和分针重合了22次。

20、将多个集合合并成没有交集的集合。

给定一个字符串的集合,格式如:{aaabbbccc},{bbbddd},{eeefff},{ggg},{dddhhh}要求将其中交集不为空的集合合并,要求合并完成后的集合之间无交集,例如上例应输出{aaabbbcccdddhhh},{eeefff},{ggg}。

(1)请描述你解决这个问题的思路;

(2)请给出主要的处理流程,算法,以及算法的复杂度

(3)请描述可能的改进。

回答:

集合使用hash_set来表示,这样合并时间复杂度比较低。

1、给每个集合编号为0,1,2,3...

2、创建一个hash_map,key 为字符串,value 为一个链表,链表节点为字符串所在集合的编号。遍历所有的集合,将字符串和对应的集合编号插入到hash_map中去。

3、创建一个长度等于集合个数的int 数组,表示集合间的合并关系。例如,下标为5的元素值为3,表示将下标为5的集合合并到下标为3的集合中去。开始时将所有值都初始化为-1,表示集合间没有互相合并。在集合合并的过程中,我们将所有的字符串都合并到编号较小的集合中去。

遍历第二步中生成的hash_map,对于每个value 中的链表,首先找到最小的集合编号(有些集合已经被合并过,需要顺着合并关系数组找到合并后的集合编号),然后将链表中所有编号的集合都合并到编号最小的集合中(通过更改合并关系数组)。

4、现在合并关系数组中值为-1的集合即为最终的集合,它的元素来源于所有直接或间接指向它的集合。

算法的复杂度为O(n),其中n 为所有集合中的元素个数。

题目中的例子:

0:{aaabbbccc}

1:{bbbddd}

2:{eeefff}

3:{ggg}

4:{dddhhh}

生成的hash_map,和处理完每个值后的合并关系数组分别为

aaa:0。[-1,-1,-1,-1,-1]

bbb:0,1。[-1,0,-1,-1,-1]

ccc:0。[-1,0,-1,-1,-1]

ddd:1,4。[-1,0,-1,-1,0]

eee:2。[-1,0,-1,-1,0]

fff:2。[-1,0,-1,-1,0]

ggg:3。[-1,0,-1,-1,0]

hhh:4。[-1,0,-1,-1,0]

所以合并完后有三个集合,第0,1,4个集合合并到了一起,


相关文章

  • 最新九月百度人搜,阿里巴巴,腾讯华为京东360笔试面试二十题
  • 引言 自发表上一篇文章至今(事实上,上篇文章更新了近3个月之久),blog已经停了3个多月,而在那之前,每月都不曾断过.正如上一篇文章支持向量机通俗导论(理解SVM的三层境界)末尾所述:"额,blog许久未有更新了,因为最近实在忙 ...查看


  • 14年最新高考祝福语集锦
  • ★以下是本站为大家整理的<14年最新高考祝福语集锦>的文章,供大家参考 [小编提示]更多祝福语请点击本站以下链接: 放假安排|高考祝福语|周末祝福短信|结婚祝福语|生日祝福语 高考,几家欢喜几家愁,有悔恨的泪,也有成功的笑.愿落 ...查看


  • 最新优秀高考祝福短信集锦
  • ★以下是本站为大家整理的<最新优秀高考祝福短信集锦>的文章,供大家参考 [小编提示]更多祝福语请点击本站以下链接: 父亲节祝福语|毕业祝福语|高考祝福语|中考祝福语|周末祝福短信|结婚祝福语|生日祝福语 没有目标就没有方向,每一 ...查看


  • 最新周末幽默祝福短信集锦
  • 以下是本站为大家整理的<最新周末幽默祝福短信集锦>的文章,供大家参考 [小编提示]更多祝福语请点击本站以下链接: 放假安排|劳动节祝福语|周末祝福短信|结婚祝福语|生日祝福语 周末又至,祝福送上,幸福指令,听好:悲伤烦恼,扔掉: ...查看


  • 最新温馨周末祝福短信集锦
  • 以下是本站为大家整理的<最新温馨周末祝福短信集锦>的文章,供大家参考 [小编提示]更多祝福语请点击本站以下链接: 清明节挽联|劳动节祝福语|周末祝福短信|结婚祝福语|生日祝福语 周末耍耍宝,心情保持好.抛开工作表,睡个大懒觉.温 ...查看


  • 单元课题探究:对蜡烛及其燃烧进行的探究活动
  • 单元课题探究:对蜡烛及其燃烧进行的探究活动 匿名投稿 给我留言 首页 教案中心 试题中心 课件中心 学科论文 范文大全 学生频道 中考频道 您现在的位置: 初中在线 >> 试题中心 >> 化学试题 >> ...查看


  • 2014年最新高考祝福语集锦
  • 以下是本站为大家整理的<2014年最新高考祝福语集锦>的文章,供大家参考 [小编提示]更多祝福语请点击本站以下链接: 放假安排|劳动节祝福语|周末祝福短信|结婚祝福语|生日祝福语 1.健康身体是基础,良好学风是条件,勤奋刻苦是前 ...查看


  • 搞笑句子:最新搞笑句子集锦
  • 1. 不要跟老子问东问西,百度比老子懂得多. 2. 男人都是骗子,幸运的女人被一个男人骗一辈子,不幸的女人被很多男人骗一阵子. 3. 孔子解决不了的问题老子给你解决. 4. 对你微笑,纯属礼貌. 5. 明骚易躲,暗贱难防. 6. 我们要向前 ...查看


  • 维修电工基础
  • 维修电工基础"相关的推广 2013全新,正版,权威<维修>66DVD+书籍 <维修>更新至7月,由国家汽车协会指定发售,每天前十位有礼包赠送. http://www.174yy.net 维修电工培训 400 ...查看


热门内容