《微软等数据结构+算法面试100题》自娱自解(完结)
这段日子工作不忙,心情也不错,为了娱乐和干点什么,把网上的一篇贴《微软等数据结构+算法面试100题》做了一遍。感觉不错,大部分是比较基础。除了个别语法题由于对该语言不熟无法下手之外,其余的连查带想地全部搞定!边做的同时也做了些记录工作,大都是写出了思路或算法。觉得个别有必要编码验证的也实验了,当然,有一部分是编码验证计划中的,回来有时间慢慢完成。
此刻,想起前辈的那句话:先了解一些XXX、掌握XXX等知识,一般的题就不在话下了。
什么叫一般的题?这些算吗?虽然略有些小成就感,但自知对某些知识掌握还是不透。有时瞪大了双眼也看不出问题所在,有种文盲看报的感觉,自知水平还是有待提高的。
不管如何,路还很长,慢慢走吧。这也算一个小测验吧,在此处留个存照。
为了尊重版权,这是原贴地址:http://blog.csdn.net/v_JULY_v/archive/2010/12/06/6057286.aspx
下面是我的记录,黑字是原题,蓝色部分是我敲的。
===============================================
《微软等数据结构+算法面试100题》自娱自解(完结)
1.把二元查找树转变成排序的双向链表
题目:
输入一棵二元查找树,将该二元查找树转换成一个排序的双向链表。
要求不能创建任何新的结点,只调整指针的指向。
10
/ /
6 14
/ / / /
4 8 12 16
转换成双向链表
4=6=8=10=12=14=16。
首先我们定义的二元查找树 节点的数据结构如下:
struct BSTreeNode
{
int m_nValue; // value of node
BSTreeNode *m_pLeft; // left child of node
BSTreeNode *m_pRight; // right child of node
};
中序遍历
2.设计包含min函数的栈。
定义栈的数据结构,要求添加一个min函数,能够得到栈的最小元素。
要求函数min、push以及pop的时间复杂度都是O(1)。
栈上加链形堆
3.求子数组的最大和
题目:
输入一个整形数组,数组里有正数也有负数。
数组中连续的一个或多个整数组成一个子数组,每个子数组都有一个和。
求所有子数组的和的最大值。要求时间复杂度为O(n)。
例如输入的数组为1, -2, 3, 10, -4, 7, 2, -5,和最大的子数组为3, 10,-4, 7, 2,
因此输出为该子数组的和18。
动态规划,end[i]表示以i结尾的和最大子数组,end[i]=max{end[i-1]+a[i],a[i]}
4.在二元树中找出和为某一值的所有路径
题目:输入一个整数和一棵二元树。
从树的根结点开始往下访问一直到叶结点所经过的所有结点形成一条路径。
打印出和与输入整数相等的所有路径。
例如 输入整数22和如下二元树
10
/ /
5 12
/ /
4 7
则打印出两条路径:10, 12和10, 5, 7。
二元树节点的数据结构定义为:
struct BinaryTreeNode // a node in the binary tree
{
int m_nValue; // value of node
BinaryTreeNode *m_pLeft; // left child of node
BinaryTreeNode *m_pRight; // right child of node
};
前序遍历树。每遇到一个新节点,把值与父节点的值加起来。到叶子时,加完后叶子上的值如果等于给出数,则认为存在一条可用路径。
5.查找最小的k个元素
题目:输入n个整数,输出其中最小的k个。
例如输入1,2,3,4,5,6,7和8这8个数字,则最小的4个数字为1,2,3和4。
建个大小为K的大根堆
第6题
腾讯面试题:
给你10分钟时间,根据上排给出十个数,在其下排填出对应的十个数
要求下排每个数都是先前上排那十个数在下排出现的次数。
上排的十个数如下:
【0,1,2,3,4,5,6,7,8,9】
举一个例子,
数值: 0,1,2,3,4,5,6,7,8,9
分配: 6,2,1,0,0,0,1,0,0,0
0在下排出现了6次,1在下排出现了2次,
2在下排出现了1次,3在下排出现了0次....
以此类推..
十个数n=10,下排表示数字个数,因此下排的和必定为10。而全为1的情况是不成立的,那么也就是说最少有一个大于1或者说至少有一个0存在。设下排0下的数字是x(x>0),表示下排有x个0。
那么1~9中必定有x个0,下排1~9的数字和为10-x,而又有x个0存在,那么不是0的位置有9-x个。那么,把10-x个数放到9-x位置上,应该有1个2,8-x个1,而0对应的下排数x应该也对应其中一个1有:
0 1 2…x…
x 8-x 1…1…
如果8-x=2,则x=6恰好有:
0 1 2 3 4 5 6 7 8 9
6 2 1 0 0 0 1 0 0 0
一般地,n>6的时候都可以用这种方法求得。小于6的情况还没想到通用的方法。。。
第7题
微软亚院之编程判断俩个链表是否相交
给出俩个单向链表的头指针,比如h1,h2,判断这俩个链表是否相交。
为了简化问题,我们假设俩个链表均不带环。
分别遍历两个链表,遍历到尾时,最后一个元素地址相同则相交。
问题扩展:
1.如果链表可能有环列?
两个链表任取一个链表头开始遍历,每遍历一个元素都与两个表头指针对比。如果能遍历到尾则不存在环,解答如上。如果碰到指针等于自己的表头,则放弃此链表遍历,开始遍历另一个链表。如果遇到另一个表头,则两链表相交。发生其他情况(两表都到自己表头,一表到尾一表回到自己表头,两表到不同的尾),则不相交。
2.如果需要求出俩个链表相交的第一个节点列?
如果不带环,则分别遍历两表,记录各自长度x、y,说明其中一表比另一表长x-y,让其先走x-y步。再两表同步前进至两指针相同,则为交点。
如果带环,分别遍历。如果一表遇到另一表头,记录长度x,开始遍历另一表,至自己表头记y,再让记录长度为x的表从头开始先走x-y步,再两表同步前进至两指针相同,则为交点。如果先遍历到自己则记y,开始遍历另一表,至同一位置记x,再让记录长度为x的表从头开始先走x-y步,再两表同步前进至两指针相同,则为交点。
第8题
此贴选一些 比较怪的题,,由于其中题目本身与算法关系不大,仅考考思维。特此并作一题。
1.有两个房间,一间房里有三盏灯,另一间房有控制着三盏灯的三个开关,
这两个房间是 分割开的,从一间里不能看到另一间的情况。
现在要求受训者分别进这两房间一次,然后判断出这三盏灯分别是由哪个开关控制的。
有什么办法呢?
开一盏灯,过十分钟再开一盏,此时进去,可辨别未开的灯对应未按的开关,用手摸摸,热的对应第一个开关
2.你让一些人为你工作了七天,你要用一根金条作为报酬。金条被分成七小块,每天给出一块。
如果你只能将金条切割两次,你怎样分给这些工人?
切成124。第一天给一块,第二天,用二换一,第三天给一,第四天用三换四,第五天给一,第六天用二换一,第七天给一。
<!--[if !supportLists]-->3. <!--[endif]-->★用一种算法来颠倒一个链接表的顺序。现在在不用递归式的情况下做一遍。
声明三个指针分别指向当前节点,下一节点,下下节点。不递归就用循环。
★用一种算法在一个循环的链接表里插入一个节点,但不得穿越链接表。
什么叫穿越链接表?用最普通的指针重定向不行么?
★用一种算法整理一个数组。你为什么选择这种方法?
要整理成什么样?排序的话,那就得看数组的状况,选择合适的排序算法。
★用一种算法使通用字符串相匹配。
只实现点和星?从头至尾匹配就行。遇到点就跳过该字符,遇到星就寻找星后面的字符,然后从星后面的字符重新开始匹配。
★颠倒一个字符串。优化速度。优化空间。
首尾交换并向中间推进,直到尾指针不大于首指针。时间O(n/2),空间为O(1)。
★颠倒一个句子中的词的顺序,比如将“我叫克丽丝”转换为“克丽丝叫我”,
实现速度最快,移动最少。
怎么样的移动最少,是打印出来,还是赋给原字符串?打印出来的话,字符串完全可以移动量为0。赋给原字符串,首地址能变不,不能变那移动量可能会达到n。首地址可变的话,把谓语和主语放在宾语后面,首地址指向宾语就可以。
★找到一个子字符串。优化速度。优化空间。
KMP算法。
★比较两个字符串,用O(n)时间和恒量空间。
每次取一个字符相比,直至取到的两个字符不同停止。
★假设你有一个用1001个整数组成的数组,这些整数是任意排列的,但是你知道所有的整数都在1到1000(包括1000)之间。此外,除一个数字出现两次外,其他所有数字只出现一次。假设你只能对这个数组做一次处理,用一种算法找出重复的那个数字。如果你在运算中使用了辅助的存储方式,那么你能找到不用这种方式的算法吗?
取第一个数,如果是6,把它与第6位交换,再看原来第6位(放手里)数字,如果是9,把原来第6位数字与第9位交换,再看原来第9位数字(放手里)……直到某位上已经放了某个数,而手里的数与之相同,则该数为所求。
★不用乘法或加法增加8倍。现在用同样的方法增加7倍。
左移三位即乘8,左移三位乘8再减去原数即是7倍
第9题
判断整数序列是不是二元查找树的后序遍历结果
题目:输入一个整数数组,判断该数组是不是某二元查找树的后序遍历的结果。
如果是返回true,否则返回false。
例如输入5、7、6、9、11、10、8,由于这一整数序列是如下树的后序遍历结果:
8
/ /
6 10
/ / / /
5 7 9 11
因此返回true。
如果输入7、4、6、5,没有哪棵树的后序遍历的结果是这个序列,因此返回false。
分治。从给定串最后一个数开始,如果它前面的数能分为连续的两段,一段比它大,一段比这,则以此数为根的树该层上后序存在。再分别对分成的前后两段分别重复上面的操作,如果所有点都可以如此分割,则认为该序列是一个后序遍历序列,否则不是。
第10题
翻转句子中单词的顺序。
题目:输入一个英文句子,翻转句子中单词的顺序,但单词内字符的顺序不变。
句子中单词以空格符隔开。为简单起见,标点符号和普通字母一样处理。
例如输入“I am a student.”,则输出“student.a am I”。
开辟一块新空间,原字符串从后往前遍历,遇到空格就往新空间里写,直至原符串遍历完毕。
第11题
求二叉树中节点的最大距离...
如果我们把二叉树看成一个图,父子节点之间的连线看成是双向的,
我们姑且定义"距离"为两节点之间边的个数。
写一个程序,
求一棵二叉树中相距最远的两个节点之间的距离。
动态规划
maxlength(i)=max{ maxlength (i->left), maxlength(i->right),high(i->left)+high(i->right)+1}
第12题
题目:求1+2+…+n,
要求不能使用乘除法、for、while、if、else、switch、case等关键字以及条件判断语句(A?B:C)。
这题还没有想到好的解法,不知道原题是想用什么语言。C++、JAVA可以用模板来实现。至于C,只想到嵌汇编来做。
第13题:
题目:输入一个单向链表,输出该链表中倒数第k个结点。链表的倒数第0个结点为链表的尾指针。
链表结点定义如下:
struct ListNode
{
int m_nKey;
ListNode* m_pNext;
};
某个指针先走K-1步,再加一个指针从头开始,两个指针同步进行,当前一个指针到尾,后一指针指向倒数第K个节点。
第14题:
题目:输入一个已经按升序排序过的数组和一个数字,
在数组中查找两个数,使得它们的和正好是输入的那个数字。
要求时间复杂度是O(n)。如果有多对数字的和等于输入的数字,输出任意一对即可。
例如输入数组1、2、4、7、11、15和数字15。由于4+11=15,因此输出4和11。
两头夹啊,两头分别设一个指针,两数相加,大于15则,右边指针的下标减一,小于15则左边指针的下标加一,直至两数相加为15。
第15题:
题目:输入一颗二元查找树,将该树转换为它的镜像,
即在转换后的二元查找树中,左子树的结点都大于右子树的结点。
用递归和循环两种方法完成树的镜像转换。
例如输入:
8
/ /
6 10
// //
5 7 9 11
输出:
8
/ /
10 6
// //
11 9 7 5
定义二元查找树的结点为:
struct BSTreeNode // a node in the binary search tree (BST)
{
int m_nValue; // value of node
BSTreeNode *m_pLeft; // left child of node
BSTreeNode *m_pRight; // right child of node
};
递归时先求递归调用子问题,再换左右指针,即类似后序遍历。用循环时好像用层次遍历和后序遍历都可以。
第16题:
题目(微软):
输入一颗二元树,从上往下按层打印树的每个结点,同一层中按照从左往右的顺序打印。
例如输入
8
/ /
6 10
/ / / /
5 7 9 11
输出8 6 10 5 7 9 11。
树的层次遍历,或叫图的广度遍历,定义一个队列即可。
第17题:
题目:在一个字符串中找到第一个只出现一次的字符。如输入abaccdeff,则输出b。
分析:这道题是2006年google的一道笔试题。
定义一个128长的数组(每个字符按ASCII码对应相应的数组元素),初值为0。遍历字符串,把数组相应的位置上的数加1,第二次遍历字符串时,读数组上的数,为1的则为所求。实际上就是用数组做了个哈希表。
第18题:
题目:n个数字(0,1,…,n-1)形成一个圆圈,从数字0开始,
每次从这个圆圈中删除第m个数字(第一个为当前数字本身,第二个为当前数字的下一个数字)。
当一个数字删除后,从被删除数字的下一个继续删除第m个数字。
求出在这个圆圈中剩下的最后一个数字。
July:我想,这个题目,不少人已经 见识过了。
约瑟夫问题,模拟实现,记得求余就可以。听说还有O(n)算法,
第19题:
题目:定义Fibonacci数列如下:
/ 0 n=0
f(n)= 1 n=1
/ f(n-1)+f(n-2) n=2
输入n,用最快的方法求该数列的第n项。
分析:在很多C语言教科书中讲到递归函数的时候,都会用Fibonacci作为例子。
因此很多程序员对这道题的递归解法非常熟悉,但....呵呵,你知道的。。
循环吧,从第1项开始计算,一直到n。还有一种更省事的方法,求出通项。
如果没记错通项应该是f(n)= (((sqrt(5)+1)/2)^n-((sqrt(5)-1)/2)^n) /sqrt(5)
第20题:
题目:输入一个表示整数的字符串,把该字符串转换成整数并输出。
例如输入字符串"345",则输出整数345。
字符串一个个取。每次取之前把现有的数(初值为0)乘10再加上每个字符减48即为现有的新数。同时注意正负号。
第21题
2010年中兴面试题
编程求解:
输入两个整数 n 和 m,从数列1,2,3.......n 中随意取几个数,
使其和等于 m ,要求将其中所有的可能组合列出来.
这是一个01背包问题。同时加入一个二维辅助数组,记录每次选择的情况。K!轻描淡写几句话,这题挺累人的,特别是求所有组合,得分析那个辅助数组。
第22题:
有4张红色的牌和4张蓝色的牌,主持人先拿任意两张,再分别在A、B、C三人额头上贴任意两张牌,
A、B、C三人都可以看见其余两人额头上的牌,看完后让他们猜自己额头上是什么颜色的牌,
A说不知道,B说不知道,C说不知道,然后A说知道了。
请教如何推理,A是怎么知道的。
如果用程序,又怎么实现呢?
红计0,蓝计1。首轮三个人都没猜出来,那么每个人就不可能看到四个0或四个1。
对首轮最后一个人C来说,他应该看出来三个人至少有一个人头上出现01,否则必定有人首轮能猜出,而C最终没猜出,所以01必定出现在AB至少一个头上。A如果看到B是00或11,则必能猜到自己是01,这是一种可能,不是充分条件。还有其他可能吗?
觉得出题人没表述清楚,题中说“然后A知道”,那么BC是否知道呢?BC如果知道是否是因为A知道了他们才知道的呢?这都会影响后续的结果。如果我答题,就蒙A一红一蓝。
编程实现,穷举可能可以实现吧。
第23题:
用最简单,最快速的方法计算出下面这个圆形是否和正方形相交。"
3D坐标系 原点(0.0,0.0,0.0)
圆形:
半径r = 3.0
圆心o = (*.*, 0.0, *.*)
正方形:
4个角坐标;
1:(*.*, 0.0, *.*)
2:(*.*, 0.0, *.*)
3:(*.*, 0.0, *.*)
4:(*.*, 0.0, *.*)
圆的方程可以写出,如果正方形的四个点代入圆的方程都大于0或是都小于则认为不相交,其余情况认为二者相交。
第24题:
链表操作,
(1).单链表就地逆置,
声明三个指针分别指向当前节点,下一节点,下下节点,从头到尾换指针就可以。
(2)合并链表
想怎么合并?如果是排好序的两个链表合成一个排好序的。分别比较两个链表当前取到的值即可,谁小把指针指向谁,被指的链表,指针向前推继续比,直到一个表遍历到尾,再把它的尾指针直接指向未遍历到尾的表的剩余部分。
第25题:
写一个函数,它的原形是int continumax(char*outputstr,char *intputstr)
功能:
在字符串中找出连续最长的数字串,并把这个串的长度返回,
并把这个最长数字串付给其中一个函数参数outputstr所指内存。
例如:"abcd12345ed125ss123456789"的首地址传给intputstr后,函数将返回9,
outputstr所指的值为123456789
遍历呗,遇到非数字无视,每次遇到新数字开始计数,而定义某一指针永远指向长度最大的数字开始位置。
26.左旋转字符串
题目:
定义字符串的左旋转操作:把字符串前面的若干个字符移动到字符串的尾部。
如把字符串abcdef左旋转2位得到字符串cdefab。请实现字符串左旋转的函数。
要求时间对长度为n的字符串操作的复杂度为O(n),辅助内存为O(1)。
每个字符的位置是可以计算的,必定是当前位置(0起始)+总长度-2,算出来不小于总长度,则再减去一个总长度即可。
27.跳台阶问题
题目:一个台阶总共有n级,如果一次可以跳1级,也可以跳2级。
求总共有多少总跳法,并分析算法的时间复杂度。
这道题最近经常出现,包括MicroStrategy等比较重视算法的公司
都曾先后选用过个这道题作为面试题或者笔试题。
动态规划,f(n)=f(n-1)+f(n-2)、f(1)=1、f(0)=1。
28.整数的二进制表示中1的个数
题目:输入一个整数,求该整数的二进制表达中有多少个1。
例如输入10,由于其二进制表示为1010,有两个1,因此输出2。
分析:
这是一道很基本的考查位运算的面试题。
包括微软在内的很多公司都曾采用过这道题。
和1做与操作,如果结果为1计数加1,再右移,直到数字等于0。
29.栈的push、pop序列
题目:输入两个整数序列。其中一个序列表示栈的push顺序,
判断另一个序列有没有可能是对应的pop顺序。
为了简单起见,我们假设push序列的任意两个整数都是不相等的。
比如输入的push序列是1、2、3、4、5,那么4、5、3、2、1就有可能是一个pop系列。
因为可以有如下的push和pop序列:
push 1,push 2,push 3,push 4,pop,push 5,pop,pop,pop,pop,
这样得到的pop序列就是4、5、3、2、1。
但序列4、3、5、1、2就不可能是push序列1、2、3、4、5的pop序列。
模拟实现即可
30.在从1到n的正数中1出现的次数
题目:输入一个整数n,求从1到n这n个整数的十进制表示中1出现的次数。
例如输入12,从1到12这些整数中包含1 的数字有1,10,11和12,1一共出现了5次。
分析:这是一道广为流传的google面试题。
有点难度,不过可以穷举。当然还是应该按不同位数求出分段函数表达式来,再找规律。
31.华为面试题:
一类似于蜂窝的结构的图,进行搜索最短路径(要求5分钟)
怎么个蜂窝法?无图无真相。可能用迪杰斯特拉算法或是拓扑排序就行。
32.
有两个序列a,b,大小都为n,序列元素的值任意整数,无序;
要求:通过交换a,b中的元素,使[序列a元素的和]与[序列b元素的和]之间的差最小。
例如:
var a=[100,99,98,1,2, 3];
var b=[1, 2, 3, 4,5,40];
二维背包问题。把所有数加起来再求一半。实际上就是求最接近和一半的序列,这个序列长度还得是n。就是说包的容量为n,可承重量为和的一半,每个物品的价值等于其重量,求价值最大的方案。
33.
实现一个挺高级的字符匹配算法:
给一串很长字符串,要求找到符合要求的字符串,例如目的串:123
1******3***2 ,12*****3这些都要找出来
其实就是类似一些和谐系统。。。。。
通配符问题?参考第8题。
34.
实现一个队列。
队列的应用场景为:
一个生产者线程将int类型的数入列,一个消费者线程将int类型的数出列
PV原语问题?关键是控制线程同步。一次只能入列一个数,入列的同时不能出列,出列同时不入列,出列也只能一次一个。
35.
求一个矩阵m*n中最大的二维矩阵 (元素和最大).如:
1 2 0 3 4
2 3 4 5 1
1 1 5 3 0
中最大的是:
4 5
5 3
要求:(1)写出算法;(2)分析时间复杂度;(3)用C写出关键代码
遍历一遍,先只计算每个元素和其下面的元素的和,再遍历一遍,把每个元素计算的值与后一个元素计算的值相加,同时取最大值即为所求。时间应该为n*(m-1)+(n-1)(m-1),也就是O(mn)。
第36题-40题(有些题目搜集于CSDN上的网友,已标明):
36.引用自网友:longzuo
谷歌笔试:
n支队伍比赛,分别编号为0,1,2。。。。n-1,已知它们之间的实力对比关系,
存储在一个二维数组w[n][n]中,w[i][j] 的值代表编号为i,j的队伍中更强的一支。
所以w[i][j]=i 或者j,现在给出它们的出场顺序,并存储在数组order[n]中,
比如order[n] = {4,3,5,8,1......},那么第一轮比赛就是 4对3, 5对8。.......
胜者晋级,败者淘汰,同一轮淘汰的所有队伍排名不再细分,即可以随便排,
下一轮由上一轮的胜者按照顺序,再依次两两比,比如可能是4对5,直至出现第一名
编程实现,给出二维数组w,一维数组order 和 用于输出比赛名次的数组result[n],
求出result。
编程模拟即可
37.
有n个长为m+1的字符串,
如果某个字符串的最后m个字符与某个字符串的前m个字符匹配,则两个字符串可以联接,
问这n个字符串最多可以连成一个多长的字符串,如果出现循环,则返回错误。
先把各个字符串看成节点,前后可匹配的就首尾相联,形成DAG图,再求用拓扑序列求最长路径,没有完整的拓扑序列就报错出现循环。
38.
百度面试:
1.用天平(只能比较,不能称重)从一堆小球中找出其中唯一一个较轻的,使用x次天平,
最多可以从y个小球中找出较轻的那个,求y与x的关系式。
用一次天平可称出三个小球,用两次天平可称三个三即九个,用次可称三个九,y=3^x
2.有一个很大很大的输入流,大到没有存储器可以将其存储下来,
而且只输入一次,如何从这个输入流中随机取得m个记录。
应该知道输入流的大小吧,然后分m次输入,每次随机生成一个小于m的整数,取该次输入中相应的数据。
3.大量的URL字符串,如何从中去除重复的,优化时间空间复杂度
遍历一遍,用散列表记录数量,对应的散列表位置为空则记下该串,并该串散列表位置上自增。
39.
网易有道笔试:
(1).
求一个二叉树中任意两个节点间的最大距离,
两个节点的距离的定义是 这两个节点间边的个数,
比如某个孩子节点和父节点间的距离是1,和相邻兄弟节点间的距离是2,优化时间空间复杂度。
参见第11题
(2).
求一个有向连通图的割点,割点的定义是,如果除去此节点和与其相关的边,
有向图不再连通,描述算法。
图的割点问题,深度优先遍历,如果某点的所有子孙节点与其祖先节点之间没有相连的边。
40.百度研发笔试题
引用自:zp155334877
1)设计一个栈结构,满足一下条件:min,push,pop操作的时间复杂度为O(1)。
参见第2题
2)一串首尾相连的珠子(m个),有N种颜色(N<=10),
设计一个算法,取出其中一段,要求包含所有N中颜色,并使长度最短。
并分析时间复杂度与空间复杂度。
模拟实现即可。
先不考虑环,只考虑线性的情况。
申请一数组记录当前所包含的颜色的数量。两个指针,i指向起点,j移动。当当前颜色数为N时,即形成一种方案,记录当前长度,j停止移动,而移动i,相应去除i指向的颜色数量减一。减后数量不为0就一直向前移,直到碰到一个减完后为0,则说明此时颜色已不全了。此时继续移动j,直到下一次颜色数为N,如此反复,移动j后每次颜色数为N时,记录当前方案长度,保留最小长度即为所求。
如果带环呢?环不环的只是多一个求余问题。(已编码验证)
3)设计一个系统处理词语搭配问题,比如说中国 和人民可以搭配,
则中国人民 人民中国都有效。要求:
*系统每秒的查询数量可能上千次;
*词语的数量级为10W;
*每个词至多可以与1W个词搭配
当用户输入中国人民的时候,要求返回与这个搭配词组相关的信息。
感觉问题与前面举的例子挨不上。什么叫搭配词组相关的信息?中国、人民、人民中国、中华人民共和国……都可以算吗?凭一些猜想试着写几句。
词库肯定得排序或建索引,方便查找。搭配信息也应该有相应的数据结构,比如用图来存储,无论是邻接表还是邻接阵保证有序。
再对给定词语先做分词,把词语分出来,怎么分呢?假设词语最少两个字,当然是每两个字地截取输入串,到词库里去查。如果查出是某字的前缀,就再读下面的字进一步查词,直至匹配到,或查得完全不是个词。对截串原则上使用贪心,一方面按最长匹配,另一方面匹配上了,直接取后两个字进行匹配,未匹配上就从第二个字开始再取一个认为是一个词到词库里去查。由此题题意,应该不考虑未匹配上的词,也就是说用贪心必定能匹配上。如果只考虑问题“中国人民”,则可分成两个词中国、人民。
分好了词,对分出的词排序,再读匹配信息,也就是取得了一个由若干节点构成的一张有向图。在中国的节点上能找到人民的话,则说明二者可匹配,说明可以进行词语组合,即得到中国人民和人民中国。
对同时输入更多的词,分词工作并不是很难,关键是匹配怎么输出?如果只两两匹配,就两两组合输出,如果是多词匹配就得考虑组合的种类了(不考虑)。
另外分析一下性能:
词应该两个字的据多,四个字节,假设平均5个字节,10W个词不超过0.5M。搭配信息用图来存,10W个词每个词至多可以与1W个词搭配,邻接表按每位存一个搭配不超过120M,用字节存则要1G左右。因此空间上是可以接受的。如果用邻接阵存则是不到邻接表5倍(本来是10倍,但可以用上三角阵存储,取半)。
时间上分词主要用排好序了的二分查找或用索引,很方便。分出n个词排好序后,进行匹配信息的查询,只需n^2/2次图查找,用邻接阵可一次定位,用邻接表每次查找也可以用二分查找。
一次应用大概是nlog10W+nlogn+n^2/2*log1W(邻接表则不乘最后的log1W)。代入一些数值计算后,一般的PC机,1秒钟可以不低于一千次应用,时间上应该可以达标。
41.求固晶机的晶元查找程序
晶元盘由数目不详的大小一样的晶元组成,晶元并不一定全布满晶元盘,
照相机每次这能匹配一个晶元,如匹配过,则拾取该晶元,
若匹配不过,照相机则按测好的晶元间距移到下一个位置。
求遍历晶元盘的算法 求思路。
没太看明白
42.请修改append函数,利用这个函数实现:
两个非降序链表的并集,1->2->3 和 2->3->5 并为 1->2->3->5
另外只能输出结果,不能修改两个链表的数据。
参见24题
43.递归和非递归俩种方法实现二叉树的前序遍历。
递归时先输出再调左右的子问题。非递归就用循环加栈,初始根节点进栈,然后每次从栈中取一个指针,再把它的右节点左节点先后进栈。
44.腾讯面试题:
1.设计一个魔方(六面)的程序。
应该不是让求魔方的解法,求魔方解法也是有公式的,不同的情况按分支套公式就行。
依题意可能是让实现一个模拟魔方的程序。不讨论UI,只讨论魔方算法的实现。
六个3*3矩阵,各矩阵初值内部相同两两间互不相同,对应每个面。
魔方转动只有为两种:正对该面时顺时针转动或逆时针转动。
先声明各面排列,比如1对3、2对4、5对6。因为魔方的主要操作是对某面转动,所以为了尽量的快应该直接使用分支判断。写六个分支对应六个面,每面再写两个分支(顺时针转、逆时针转)。每种转动,自己和相邻面的矩阵数据如何交换。只对矩阵进行更新,再把更新后的矩阵返回到UI,即实现了魔方的转换。
2.有一千万条短信,有重复,以文本文件的形式保存,一行一条,有重复。
请用5分钟时间,找出重复出现最多的前10条。
遍历同时用哈希表计数,每更新一次哈希表计数,试着更新大小为10的小根堆,求数量最大的10条。
3.收藏了1万条url,现在给你一条url,如何找出相似的url。(面试官不解释何为相似)
什么叫相似?是指属于同一服务,还是指URL字串上大都相同呢?
先考虑前者,如果一个URL是另一个URL的前缀,是不是应该叫相似?或者两个URL的前面的目录相同,是不是也应该叫相似?所以可以先左对齐求前段地址和部分目录的字符串是否一致,如果一致则认为相似。
再考虑后者,如果不满足前面的结果。考虑相似是指匹配字符较多。那就用动态规划求字符对齐匹配数最大的。那么较多是多少呢?这个标准可以人为取,我想怎么也得80%以上才叫相似吧。那就人为规定了字符动态匹配数的标准了,达标的就认为是相似的。
45.雅虎:
1.对于一个整数矩阵,存在一种运算,对矩阵中任意元素加一时,需要其相邻(上下左右)
某一个元素也加一,现给出一正数矩阵,判断其是否能够由一个全零矩阵经过上述运算得到。
这是一个最大流问题,把矩阵看成黑白相间的点阵,题目等价于,每次对相邻黑白两点数量加1。
先判断黑白点上各自的数据和是否相等,如果二者数据不等,就没必要往下进行了,必定无解。
如果相等,再转化最大流来解。假设存在一个源点指向所有的黑点每条边的容量为黑点上的数据,又有一个汇点,所有白点都联向它,其边上的容量也为无穷大。所有黑点指向与其相邻的白点,其容量为白点上的数据。求从源到汇的最大流,如果最后源的所有边都饱和,即最大流等于黑点上数据之和则说明该矩阵有解。
2.一个整数数组,长度为n,将其分为m份,使各份的和相等,求m的最大值
比如{3,2,4,3,6} 可以分成{3,2,4,3,6} m=1;
{3,6}{2,4,3} m=2
{3,3}{2,4}{6} m=3 所以m的最大值为3
m的值不可能超过sum(a)/max(ai)。从sum(a)/max(ai)到1开始试,同时m得是sum(a)的因子。
再转成01背包问题。包容量为sum(a)/m,每个物品价值等于其体积,求包可装最大价值的方案。
特别地,如果sum(a)是素数,而数组内存在不等的两个数,则m只能为1。
46.搜狐:
四对括号可以有多少种匹配排列方式?比如两对括号可以有两种:()()和(())
14种
()()()() (()())() ()(()()) (())()() ()(())() ()()(()) (())(())
((()))() ()((())) (((()))) (()()()) ((())()) (()(())) ((()()))
卡特兰数
47.创新工场:
求一个数组的最长递减子序列 比如{9,4,3,2,5,4,3,2}的最长递减子序列为{9,5,4,3,2}
经典的动态规划问题。f(i)=max{f(j)}+1 (当j<i且a[i]<a[j])。如果使用二分查找,理论上会更快。
48.微软:
一个数组是由一个递减数列左移若干位形成的,比如{4,3,2,1,6,5}
是由{6,5,4,3,2,1}左移两位形成的,在这种数组中查找某一个数。
二分查找吧。虽然左移了若干位,不影响用二分查找。只是判断向前还是向后查找时注意一下该段起中尾三个数的大小关系。
49.一道看上去很吓人的算法面试题:
如何对n个数进行排序,要求时间复杂度O(n),空间复杂度O(1)
这题八成是蒙人的,数学上都证明了用比较排序是不可能达到O(n)的。这题让达到这个时间,必定是要讨论数组的状况,比如范围不大可以使用计数排序,再如只有整数可以使用基数排序,等等一些特殊情况才能在线性时间内完成排序。
50.网易有道笔试:
1.求一个二叉树中任意两个节点间的最大距离,两个节点的距离的定义是 这两个节点间边的个数,
比如某个孩子节点和父节点间的距离是1,和相邻兄弟节点间的距离是2,优化时间空间复杂度。
参见39题。
2.求一个有向连通图的割点,割点的定义是,
如果除去此节点和与其相关的边,有向图不再连通,描述算法。
参见39题。
-------------------------------------------------------------------
51.和为n连续正数序列。
题目:输入一个正数n,输出所有和为n连续正数序列。
例如输入15,由于1+2+3+4+5=4+5+6=7+8=15,所以输出3个连续序列1-5、4-6和7-8。
分析:这是网易的一道面试题。
等差数列n=(2*a1*x+(x-1)x)/2,a1为首项,x为长度,x从1开始自增到n反求a1,如果求出a1为正整数则是一个可行序列,输出。时间应该是O(n)。
52.二元树的深度。
题目:输入一棵二元树的根结点,求该树的深度。
从根结点到叶结点依次经过的结点(含根、叶结点)形成树的一条路径,最长路径的长度为树的深度。
例如:输入二元树:
10
/ /
6 14
/ / /
4 12 16
输出该树的深度3。
二元树的结点定义如下:
struct SBinaryTreeNode // a node of the binary tree
{
int m_nValue; // value of node
SBinaryTreeNode *m_pLeft; // leftchild of node
SBinaryTreeNode *m_pRight; // right childof node
};
分析:这道题本质上还是考查二元树的遍历。
动态规划,High(i)=max{high(i->left),high(i->right)}+1,本质上是后序遍历。
53.字符串的排列。
题目:输入一个字符串,打印出该字符串中字符的所有排列。
例如输入字符串abc,则输出由字符a、b、c所能排列出来的所有字符串
abc、acb、bac、bca、cab和cba。
分析:这是一道很好的考查对递归理解的编程题,
因此在过去一年中频繁出现在各大公司的面试、笔试题中。
经典的排列输出。得考虑一种情况,如果字符串内有重复字符怎么办,所以不能用全排列的算法,得用不重复排列算法。
54.调整数组顺序使奇数位于偶数前面。
题目:输入一个整数数组,调整数组中数字的顺序,使得所有奇数位于数组的前半部分,
所有偶数位于数组的后半部分。要求时间复杂度为O(n)。
两头扫,左边碰到偶数就停,再扫右边,右边碰到奇数就跟左边的偶数换,再继续扫左边,直至两边相碰。类似快速排序的一种实现方式。
55.
题目:类CMyString的声明如下:
class CMyString
{
public:
CMyString(char* pData = NULL);
CMyString(const CMyString& str);
~CMyString(void);
CMyString& operator = (const CMyString&str);
private:
char* m_pData;
};
请实现其赋值运算符的重载函数,要求异常安全,即当对一个对象进行赋值时发生异常,对象的状态不能改变。
C++不太懂。
56.最长公共字串。
题目:如果字符串一的所有字符按其在字符串中的顺序出现在另外一个字符串二中,
则字符串一称之为字符串二的子串。
注意,并不要求子串(字符串一)的字符必须连续出现在字符串二中。
请编写一个函数,输入两个字符串,求它们的最长公共子串,并打印出最长公共子串。
例如:输入两个字符串BDCABA和ABCBDAB,字符串BCBA和BDAB都是是它们的最长公共子串,
则输出它们的长度4,并打印任意一个子串。
分析:求最长公共子串(Longest Common Subsequence, LCS)是一道非常经典的动态规划题,
因此一些重视算法的公司像MicroStrategy都把它当作面试题。
经典动态规划
a[i]=b[i]时,f(i,j)=f(i-1,j-1)+1.
a[i]!=b[i]时,f(i,j)=max{f(i-1,j), f(i,j-1)}
57.用俩个栈实现队列。
题目:某队列的声明如下:
template<typename T> class CQueue
{
public:
CQueue() {}
~CQueue() {}
void appendTail(const T&node); // append a element to tail
voiddeleteHead(); // remove a element from head
private:
T> m_stack1;
T> m_stack2;
};
分析:从上面的类的声明中,我们发现在队列中有两个栈。
因此这道题实质上是要求我们用两个栈来实现一个队列。
相信大家对栈和队列的基本性质都非常了解了:栈是一种后入先出的数据容器,
因此对队列进行的插入和删除操作都是在栈顶上进行;队列是一种先入先出的数据容器,
我们总是把新元素插入到队列的尾部,而从队列的头部删除元素。
AB两个栈,存数时先把B栈的数出栈依次放到A中,取数时先把A栈的数出栈依次放到B中。
58.从尾到头输出链表。
题目:输入一个链表的头结点,从尾到头反过来输出每个结点的值。链表结点定义如下:
struct ListNode
{
int m_nKey;
ListNode* m_pNext;
};
分析:这是一道很有意思的面试题。
该题以及它的变体经常出现在各大公司的面试、笔试题中。
用栈输出。也可以用递归,先调子问题再输出本节点。
59.不能被继承的类。
题目:用C++设计一个不能被继承的类。
分析:这是Adobe公司2007年校园招聘的最新笔试题。
这道题除了考察应聘者的C++基本功底外,还能考察反应能力,是一道很好的题目。
final?C++不太懂。
60.在O(1)时间内删除链表结点。
题目:给定链表的头指针和一个结点指针,在O(1)时间删除该结点。链表结点的定义如下:
struct ListNode
{
int m_nKey;
ListNode* m_pNext;
};
函数的声明如下:
void DeleteNode(ListNode* pListHead, ListNode* pToBeDeleted);
分析:这是一道广为流传的Google面试题,能有效考察我们的编程基本功,还能考察我们的反应速度,
更重要的是,还能考察我们对时间复杂度的理解。
把下一节点的值拷到本节点,再删下一节点。
-------------------------------------------------------------------------
61.找出数组中两个只出现一次的数字
题目:一个整型数组里除了两个数字之外,其他的数字都出现了两次。
请写程序找出这两个只出现一次的数字。要求时间复杂度是O(n),空间复杂度是O(1)。
分析:这是一道很新颖的关于位运算的面试题。
先求异或得出一个结果是这两个数字的异或,必不为0。其二进制至少有一位为1,以这位为准把原数组分为两部分,一部分该位为1数,一部分该位为0数。这两个数必分别在这两个小数组里,且每个小数组除此数之外其他数出现两次,再用异或就可以分别求得。
62.找出链表的第一个公共结点。
题目:两个单向链表,找出它们的第一个公共结点。
链表的结点定义为:
struct ListNode
{
int m_nKey;
ListNode* m_pNext;
};
分析:这是一道微软的面试题。微软非常喜欢与链表相关的题目,
因此在微软的面试题中,链表出现的概率相当高。
见第7题
63.在字符串中删除特定的字符。
题目:输入两个字符串,从第一字符串中删除第二个字符串中所有的字符。
例如,输入”They are students.”和”aeiou”,
则删除之后的第一个字符串变成”Thy r stdnts.”。
分析:这是一道微软面试题。在微软的常见面试题中,与字符串相关的题目占了很大的一部分,
因为写程序操作字符串能很好的反映我们的编程基本功。
从头到尾扫,每个字符都与第二个串中的所有字符比较,相同就略去。O(mn)
64. 寻找丑数。
题目:我们把只包含因子2、3和5的数称作丑数(Ugly Number)。例如6、8都是丑数,
但14不是,因为它包含因子7。习惯上我们把1当做是第一个丑数。
求按从小到大的顺序的第1500个丑数。
分析:这是一道在网络上广为流传的面试题,据说google曾经采用过这道题。
每一丑数必定是其前面某个数直接乘2、3、5所得。从第一个开始算到第1500个就行。每次算一个,把该次要计算的数之前所有丑数乘以2、3、5,算出来之后的所有结果与此前最后一个丑数比较,大于最后一个丑数且最小即为要取的丑数。为了确保不溢出有必要使用64位计算。取几个数最小值的时候用堆应该可以更快一些,那就得建个小根堆。算出来是859963392。(已编码验证)
65.输出1到最大的N位数
题目:输入数字n,按顺序输出从1最大的n位10进制数。比如输入3,
则输出1、2、3一直到最大的3位数即999。
分析:这是一道很有意思的题目。看起来很简单,其实里面却有不少的玄机。
有啥玄机?从1输出到10^n-1不就成了,注意别溢出就成了。
66.颠倒栈。
题目:用递归颠倒一个栈。例如输入栈{1, 2, 3, 4, 5},1在栈顶。
颠倒之后的栈为{5, 4, 3, 2, 1},5处在栈顶。
F(i,bool)//bool==1表示出栈,0表示进栈
{
if(bool==1){
F(i-1,1);
i出栈;
}else{
F(i-1,0);
i进栈;
}
}
Main(){F(5,1);F(5,0);}
67.俩个闲玩娱乐。
1.扑克牌的顺子
从扑克牌中随机抽5张牌,判断是不是一个顺子,即这5张牌是不是连续的。
2-10为数字本身,A为1,J为11,Q为12,K为13,而大小王可以看成任意数字。
遍历一遍,看有几张王,且除王外最大最小值。
无王时,最大最小差为4,
一王时,最大最小差为3,
俩王时,最大最小差为2
2.n个骰子的点数。
把n个骰子扔在地上,所有骰子朝上一面的点数之和为S。输入n,
打印出S的所有可能的值出现的概率。
一个骰子六面。最小全是一,S=n;最大全是六,S=6n。那么S的取值应该是n至6n的所有整数。
对首尾的概率好计算,都是(1/6)^n,其他取值不容易求解。
分析一下,对某一S,其所有可能的情形,就是n个数,每个数取值在1~6之间,把这些数拼成S。问题类似多重背包问题,包大小为S,物品大小为1,价值为1,n个物品,每个物品个只有6个,求价值最大的方案总数。不同的是对所有方案必须每件物品至少装一件,那么这个多重背包就改成:包大小为S-6,物品大小为1,价值为1,n个物品,每个物品个只有5个,求价值最大的方案总数。
同时注意到n+1与6n-1的概率应该是一样的,n+2与6n-2也一样……因此,计算量可以减少一半。
因此, S从n+1遍历到7n/2,对每个S解上面的多重背包问题,对某S求得的方案数除以6^n即为其概率。
不太困难,只是复杂度高点。
68.把数组排成最小的数。
题目:输入一个正整数数组,将它们连接起来排成一个数,输出能排出的所有数字中最小的一个。
例如输入数组{32, 321},则输出这两个能排成的最小数字32132。
请给出解决问题的算法,并证明该算法。
分析:这是09年6月份百度的一道面试题,
从这道题我们可以看出百度对应聘者在算法方面有很高的要求。
这题有点意思,应该是道经典题,以前见过类似的,不过当时只认识了题目关键所在,没有思考解决方案。一般可以先按字符串大小排序,贪心输出。但关键在于,如果一个数(32)是另一数(321)的前缀(32),且后缀(1)比前缀(32)小。简单地按贪心输出来的数可能不是最小的。对此问题可以如下解决:
如果1号数(排好序了)是2号数的前缀,且2号数的后缀比前缀小(比前缀大则直接把1号数输出了),则向下扫描。如果2号数是3号数前缀,且3号数的后缀比前缀小,继续,直到i号数不是i+1号数的前缀或i+1号数前缀比后缀小。输出i号数(就认为删了)。再看i+1号数是否是i-1的前缀且i+1号数的后缀比前缀小,如果是则重复上面的过程,如果不是则输出i-1号数(就认为删了)。如此依次执行就可以实现。
测试数据:35 351 3512 35124 3522 3523
69.旋转数组中的最小元素。
题目:把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。输入一个排好序的数组的一个旋转,
输出旋转数组的最小元素。例如数组{3, 4, 5, 1, 2}为{1, 2, 3, 4, 5}的一个旋转,该数组的最小值为1。
分析:这道题最直观的解法并不难。从头到尾遍历数组一次,就能找出最小的元素,
时间复杂度显然是O(N)。但这个思路没有利用输入数组的特性,我们应该能找到更好的解法。
二分查找。先取首尾两数比较,如果首小于尾,则第一个数即为所求。如果首大于尾,二分查找得到一个新数,把原数组分为两段。总取首大于尾的那段(取后段首指针加1、取前段为尾指针减1),新指针对比,如果首小于尾或者首尾指针重叠,首指针所指为所求,否则重复二分查找迭代。
70.给出一个函数来输出一个字符串的所有排列。
ANSWER 简单的回溯就可以实现了。当然排列的产生也有很多种算法,去看看组合数学,
还有逆序生成排列和一些不需要递归生成排列的方法。
印象中Knuth的<TAOCP>第一卷里面深入讲了排列的生成。这些算法的理解需要一定的数学功底,
也需要一定的灵感,有兴趣最好看看。
参照第53题
71.数值的整数次方。
题目:实现函数double Power(double base, int exponent),求base的exponent次方。
不需要考虑溢出。
分析:这是一道看起来很简单的问题。可能有不少的人在看到题目后30秒写出如下的代码:
double Power(double base, int exponent)
{
double result = 1.0;
for(int i = 1; i <= exponent; ++i)
result *=base;
return result;
}
上面给出的算法不是我给的,可以算出结果,这种做法效率不是很好,让人一看就是不是正规军出身。体现水平的解法应该是用循环或递归实现:x^y={(x^(y/2))^2 、y为偶数时|x*(x^(y/2))^2 、y为奇数时}
72.
题目:设计一个类,我们只能生成该类的一个实例。
分析:只能生成一个实例的类是实现了Singleton模式的类型。
C++不太懂,是不是得定义静态变量然后在构造函数里动点手脚?
73.对策字符串的最大长度。
题目:输入一个字符串,输出该字符串中对称的子字符串的最大长度。
比如输入字符串“google”,由于该字符串里最长的对称子字符串是“goog”,因此输出4。
分析:可能很多人都写过判断一个字符串是不是对称的函数,这个题目可以看成是该函数的加强版。
扫描啊,如果碰到连续两个一样的字母,就另外左右开弓的扫,求出对称的长度同时更新最大值。再继续向前扫,直至字符串扫完。
74.数组中超过出现次数超过一半的数字
题目:数组中有一个数字出现的次数超过了数组长度的一半,找出这个数字。
分析:这是一道广为流传的面试题,包括百度、微软和Google在内的多家公司都
曾经采用过这个题目。要几十分钟的时间里很好地解答这道题,
除了较好的编程能力之外,还需要较快的反应和较强的逻辑思维能力。
从头扫,前后两个指针,初始时只动后指针向后移,如果两个指针指向不同的数字,则两个数字都删除,遇到相同的数字,后指针向后移。扫完后剩下的数必定一样且是所求的数。
75.二叉树两个结点的最低共同父结点
题目:二叉树的结点定义如下:
struct TreeNode
{
int m_nvalue;
TreeNode* m_pLeft;
TreeNode* m_pRight;
};
输入二叉树中的两个结点,输出这两个结点在数中最低的共同父结点。
分析:求数中两个结点的最低共同结点是面试中经常出现的一个问题。这个问题至少有两个变种。
从根开始后序遍历,至某个节点时,开始记数为0,每向上走一层记数减1,向下走一层记数加1,同时记录记数最小值,当遇到另一个节点时,继续遍历记数,当记数值更新到前面记录的最小值时,当前节点即为最近共同祖先。思路比较简单,编码得好好设计一下。
76.复杂链表的复制
题目:有一个复杂链表,其结点除了有一个m_pNext指针指向下一个结点外,
还有一个m_pSibling指向链表中的任一结点或者NULL。其结点的C++定义如下:
struct ComplexNode
{
int m_nValue;
ComplexNode* m_pNext;
ComplexNode* m_pSibling;
};
下图是一个含有5个结点的该类型复杂链表。
图中实线箭头表示m_pNext指针,虚线箭头表示m_pSibling指针。为简单起见,
指向NULL的指针没有画出。
请完成函数ComplexNode* Clone(ComplexNode* pHead),以复制一个复杂链表。
分析:在常见的数据结构上稍加变化,这是一种很新颖的面试题。
要在不到一个小时的时间里解决这种类型的题目,我们需要较快的反应能力,
对数据结构透彻的理解以及扎实的编程功底。
如果仅仅是为了复制一个链表还是不困难的。首先遍历一遍所有节点,把每个节点的值都抹掉,当然不是真抹,按序存在别处,用从0开始的数字自增写入原链表。新分配一块连续的节点空间用来存储新复制后的链表,这样每个节点的物理位置是可以由首地址计算出来的,同时遍历原链表可以得到每个节点与哪些节点相连,再在新链表上把各节点与其相连节点的地址算出来,把指针指向它即可。
77.关于链表问题的面试题目如下:
1.给定单链表,检测是否有环。
使用两个指针p1,p2从链表头开始遍历,p1每次前进一步,p2每次前进两步。如果p2到达链表尾部,
说明无环,否则p1、p2必然会在某个时刻相遇(p1==p2),从而检测到链表中有环。
2.给定两个单链表(head1, head2),检测两个链表是否有交点,如果有返回第一个交点。
参见第7题
3.给定单链表(head),如果有环的话请返回从头结点进入环的第一个节点。
参见第7题
4.只给定单链表中某个结点p(并非最后一个结点,即p->next!=NULL)指针,删除该结点。
见第60题
5.只给定单链表中某个结点p(非空结点),在p前面插入一个结点。
再它后面加一个节点,把它的值拷到新加的节点中。
78.链表和数组的区别在哪里?
分析:主要在基本概念上的理解。
但是最好能考虑的全面一点,现在公司招人的竞争可能就在细节上产生,
谁比较仔细,谁获胜的机会就大。
一般都是线性的,存储空间链表是不连续的数组是连续的。检索的时候数组可以随机检索,链表只能顺序检索。增删改查操作的时间复杂度不一样。
79.
1.编写实现链表排序的一种算法。说明为什么你会选择用这样的方法?
插入排序可能会好点,插入新节点时时间复杂度为O(1)
2.编写实现数组排序的一种算法。说明为什么你会选择用这样的方法?
快排、堆排、归并都可以,时间上差不多,同时各有各的好处。
3.请编写能直接实现strstr()函数功能的代码。
KMP算法
80.阿里巴巴一道笔试题
问题描述:
12个高矮不同的人,排成两排,每排必须是从矮到高排列,而且第二排比对应的第一排的人高,问排列方式有多少种?
这个笔试题,很YD,因为把某个递归关系隐藏得很深。
定义一个有序数组0~11,0表示该人排在前排,1表示该人排在后排。只要确定了数组元素的值是0还是1,就必定能确定一种排序方案。这是因为六个0或1必须保证从小到大排列,而这排列只有一种。
数组为000000111111 表示方案:
0 1 2 3 4 5
6 7 8 9 10 11
数组为010011010011 表示方案:
0 2 3 6 8 9
1 4 5 7 10 11
那么问题就变为求六个0六个1的不重复排列,同时保证,每个1前面的0的数量大于1的数量。因此这变成一个不重复排列数的问题。
最后求得方案总数为132(已编码验证)
先来几组百度的面试题:
===================
81.第1组百度面试题
1.一个int数组,里面数据无任何限制,要求求出所有这样的数a[i],
其左边的数都小于等于它,右边的数都大于等于它。
能否只用一个额外数组和少量其它空间实现。
从左到右扫,边扫边更新当前遇到所有元素的最大值。比如说遇到i,此时最大值就是i,则把最大值更新,且把额外数组i位置上的置1。
再从右往左扫,边扫边更新当前遇到所有元素的最大值。对额外数组对应位置上为1的数据,比如j对应额外数组为1,拿最大值与j比较,此时最大值就是j,则把最大值更新,且认为j就是一个所求元素。
2.一个文件,内含一千万行字符串,每个字符串在1K以内,
要求找出所有相反的串对,如abc和cba。
用HASH。设字符串从左到右读出,匹配一个标识1,其逆串标识0。下面开始:
对每个字符串先到HASH里查是否存在,如果不存在,把自己和标识1放入HASH,同时它的逆转和标识0放入HASH。如果存在,且标识为1,说明以前遇见过相同的字符串,忽略这个字符串。如果存在且标识为0,说明有与它对称的字符串,把相应的标识从0改为1,而该字符串和其逆串为一个所求串对。
复杂度为:遍历每个串,只对不重复出现的串求逆串。空间上可能是一个较大的HASH。
3.STL的set用什么实现的?为什么不用hash?
SET里不会出现相同的元素,HASH里可以出现相同元素。假如用两层HASH,即HASH再HASH,就可以实现SET那样去除相同元素。
或者SET前先HASH一下,如果存在相同的数据,则不另HASH而直接指向它。
具体STL里的SET是怎么实现的不太清楚。
82.第2组百度面试题
1.给出两个集合A和B,其中集合A={name},
集合B={age、sex、scholarship、address、...},
要求:
问题1、根据集合A中的name查询出集合B中对应的属性信息;
关键是建好A与B的映射关系,AB都设自己ID,且按ID有序排列,然后把ID匹配好都扔到HASH表里头吧。如果不想设ID直接匹配也行,把AB以两个不同的匹配对象扔到HASH里面,开销大点就是。
问题2、根据集合B中的属性信息(单个属性,如age<20等),查询出集合A中对应的name。
最简单的考虑当然是把它扔DB里头,一条SELECT就可以实现。
或者存文件里,对文件建索引。
或者对待查属性排好序,遍历或二分查找,不过数据量一大,效率就太低。
至于具体算法的实现就要考虑多些了。如果只按单个属性查询的话,可以对不同的属性建多个不同线段树,不同属性的线段树可以实现按不同属性的区间取值。如果想实现多个属性一起查询用线段树可能就有点困难,但也不是没法实现,效率不好说。不知道多维线段树能不能实现多个属性一起查询,没用过,这些都是传说中的高级数据结构。
2.给出一个文件,里面包含两个字段{url、size},
即url为网址,size为对应网址访问的次数,
要求:
问题1、利用Linux Shell命令或自己设计算法,
查询出url字符串中包含“baidu”子字符串对应的size字段值;
grep baidu file.txt | awk '{print $2}'
设计算法的话,只能读文件挨行扫描,用KMP匹配,如果成功则输出size
问题2、根据问题1的查询结果,对其按照size由大到小的排列。
(说明:url数据量很大,100亿级以上)
先对查询出的size排好序,快排就可以,特别地,排序的同时记录每一个size排序前后的位置。
扫描文件,生成相应多的指针,对每个size被查询出来的URL安放一个指针,然后逐次做交换(1放到8,8放到13,13放到9……)。对这种大数据量的题,减少扫描就是对复杂度的最好保证。
83.第3组百度面试题
1.今年百度的一道题目
百度笔试:给定一个存放整数的数组,重新排列数组使得数组左边为奇数,右边为偶数。
要求:空间复杂度O(1),时间复杂度为O(n)。
见第54题
2.百度笔试题
用C语言实现函数void * memmove(void *dest, const void *src, size_t n)。
memmove函数的功能是拷贝src所指的内存内容前n个字节到dest所指的地址上。
分析:
由于可以把任何类型的指针赋给void类型的指针
这个函数主要是实现各种数据类型的拷贝。
语法不太懂。
84.第4组百度面试题
2010年3道百度面试题[相信,你懂其中的含金量]
1.a~z包括大小写与0~9组成的N个数
用最快的方式把其中重复的元素挑出来。
用哈希表。也可以定义一个128长的数组(每个字符按ASCII码对应相应的数组元素)。
.已知一随机发生器,产生0的概率是p,产生1的概率是1-p,现在要你构造一个发生器,
使得它构造0和1的概率均为1/2;构造一个发生器,使得它构造1、2、3的概率均为1/3;...,
构造一个发生器,使得它构造1、2、3、...n的概率均为1/n,要求复杂度最低。
对n=2,认为01表示0、10表示1,等概率,其他情况放弃
对n=3,认为001表示1、010表示2,100表示3,等概率,其他情况放弃
对n=4,认为0001表示1、0010表示2,0100表示3,1000表示4,等概率,其他情况放弃
……
3.有10个文件,每个文件1G,
每个文件的每一行都存放的是用户的query,每个文件的query都可能重复。
要求按照query的频度排序.
可以用HASH来保存不同query的频度,再把HASH按频度排序,按频度排好序的HASH每个query依次按频度次数写入文件,每至1G就写下一个文件,直至10个文件写完。这个要求HASH表足够大,还要能快速对HASH表内的数据进行排序。
85.又见字符串的问题
1.给出一个函数来复制两个字符串A和B。
字符串A的后几个字节和字符串B的前几个字节重叠。
分析:记住,这种题目往往就是考你对边界的考虑情况。
想实现什么样的复制,A->B?遍历到尾,从尾开始往回拷贝。
2.已知一个字符串,比如asderwsde,寻找其中的一个子字符串比如sde的个数,
如果没有返回0,有的话返回子字符串的个数。
KMP算法
86.
怎样编写一个程序,把一个有序整数数组放到二叉树中?
分析:本题考察二叉搜索树的建树方法,简单的递归结构。
关于树的算法设计一定要联想到递归,因为树本身就是递归的定义。
而,学会把递归改称非递归也是一种必要的技术。
毕竟,递归会造成栈溢出,关于系统底层的程序中不到非不得以最好不要用。
但是对某些数学问题,就一定要学会用递归去解决。
关键是想放到一个什么样树里头,一个所有节点只有右节点的树也叫二叉树。
如果放一个高度最低的二叉查找树,按二分法查找,同时前序生成一棵二叉树。
87.
1.大整数数相乘的问题。(这是2002年在一考研班上遇到的算法题)
大数乘法,模拟竖乘计算的实现,乘数每一位与被乘数相乘,其积乘以10的幂,幂次为该位在原乘数里的倍数。再累加。累加也可能用到大数相加,也是模拟竖加计算实现。
2.求最大连续递增数字串(如“ads3sl456789DF3456ld345AA”中的“456789”)
扫描一遍,记录递增起始点,每次递增序列结束,记录最长长度。令一指针永远指向长度最大的递增序列。
3.实现strstr功能,即在父串中寻找子串首次出现的位置。
(笔试中常让面试者实现标准库中的一些函数)
KMP算法
88.2005年11月金山笔试题。编码完成下面的处理函数。
函数将字符串中的字符'*'移到串的前部分,
前面的非'*'字符后移,但不能改变非'*'字符的先后顺序,函数返回串中字符'*'的数量。
如原始串为:ab**cd**e*12,
处理后为*****abcde12,函数并返回值为5。(要求使用尽量少的时间和辅助空间)
舍得用空间的话,就开辟一个同样大小的字符串,扫一遍把非星写进来,同时记录n个数,再把原数组前n位置为星,n+1位起把新拼成的串赋回来。
省点空间的话就定义一个小数组,记录每个星的位置,总长度n。遍历从1开始原串,遇到非星字符,到小数组中二分查找,找出有多少个星在其后,用当前下标加上找出的星的数量,即为其目标位置。再取目标位置上原来的数进行迭代,当某点后不再有星回到原串位置1的下一个位置即2开始迭代,直至原始串前n个字符都迭代完毕,串即整理完毕。
再想省空间就得移多次,可能会耗点时。
89.神州数码、华为、东软笔试题
1.2005年11月15日华为软件研发笔试题。实现一单链表的逆转。
见第8题。。
2.编码实现字符串转整型的函数(实现函数atoi的功能),据说是神州数码笔试题。如将字符
串 ”+123”123,”-0123”-123, “123CS45”123, “123.45CS”123, “CS123.45”0
CS是什么意思?atoi的实现参照第20题。如果CS表示冗余字符,则遇到非数字或首位的加减号则停。
3.快速排序(东软喜欢考类似的算法填空题,又如堆排序的算法等)
随便一本讲排序的书上都有介绍
4.删除字符串中的数字并压缩字符串。
如字符串”abc123de4fg56”处理后变为”abcdefg”。注意空间和效率。
(下面的算法只需要一次遍历,不需要开辟新空间,时间复杂度为O(N))
遇到数字就记录其位置,继续往后碰到字符就覆盖数字的位置,然后继续,字符串完了,就把最后数字的位置上写’/0’
5.求两个串中的第一个最长子串(神州数码以前试题)。
如"abractyeyt","dgdsaeactyey"的最大子串为"actyet"。
动态规划。
f[i][j]表示到ai,bj公共最长子串长度
s[i][j]表示以ai,bj结尾的连续公共子串长度
s[i][j]=f[i][j]=0;
a[i]==b[i]时、s[i][j]=s[i-1][j-1]+1 f[i][j]=max{s[i][j]、f[i-1][j-1]}
a[i]!=b[i]时、f[i][j]=max{f[i-1][j]、f[i][j-1]};(已编码验证)
90.
1.不开辟用于交换数据的临时空间,如何完成字符串的逆序
(在技术一轮面试中,有些面试官会这样问)。
参照第8题。谁说交换数据一定得临时空间?a=a+b;b=a-b;a=a-b;
2.删除串中指定的字符
(做此题时,千万不要开辟新空间,否则面试官可能认为你不适合做嵌入式开发)
参照89题
3.判断单链表中是否存在环。
参照第7题
91.
1.一道著名的毒酒问题
有1000桶酒,其中1桶有毒。而一旦吃了,毒性会在1周后发作。
现在我们用小老鼠做实验,要在1周内找出那桶毒酒,问最少需要多少老鼠。
把1000转为二进制,需十位,所以选十只老鼠编好号,分别对应每位。每位上是1的对应那只老鼠喝该桶酒。比如第6桶,对应110,二号三号老鼠喝这桶酒。最后把十个老鼠一排,死了的被认为1,未死的是0,读出这个十位二进制,即为有毒的桶号。
2.有趣的石头问题
有一堆1万个石头和1万个木头,对于每个石头都有1个木头和它重量一样,
把配对的石头和木头找出来。
如果只能比较重量大小,先对石头排序,再拿木头到石头里二分查找。复杂度为O(nlogn)。
如果能称出重量,就把所有重量称出,重量相同的匹配,有点类似于用哈希表了。虽说像HASH表,但人工操作的时候,还是得排序才能找到重量相同的。
92.
1.多人排成一个队列,我们认为从低到高是正确的序列,但是总有部分人不遵守秩序。
如果说,前面的人比后面的人高(两人身高一样认为是合适的),
那么我们就认为这两个人是一对“捣乱分子”,比如说,现在存在一个序列:
176, 178, 180, 170, 171
这些捣乱分子对为
<176, 170>, <176, 171>, <178, 170>, <178, 171>,<180, 170>, <180, 171>,
那么,现在给出一个整型序列,请找出这些捣乱分子对的个数(仅给出捣乱分子对的数目即可,不用具体的对)
要求:
输入:
为一个文件(in),文件的每一行为一个序列。序列全为数字,数字间用”,”分隔。
输出:
为一个文件(out),每行为一个数字,表示捣乱分子的对数。
详细说明自己的解题思路,说明自己实现的一些关键点。
并给出实现的代码 ,并分析时间复杂度。
限制:
输入每行的最大数字个数为100000个,数字最长为6位。程序无内存使用限制。
所谓捣乱分子,就是求逆序对。最直接地,借用归并排序,归并时发生交换则记数。更高级的做法,可以用树状数组。
93.在一个int数组里查找这样的数,它大于等于左侧所有数,小于等于右侧所有数。
直观想法是用两个数组a、b。a[i]、b[i]分别保存从前到i的最大的数和从后到i的最小的数,
一个解:这需要两次遍历,然后再遍历一次原数组,
将所有data[i]>=a[i-1]&&data[i]<=b[i]的data[i]找出即可。
给出这个解答后,面试官有要求只能用一个辅助数组,且要求少遍历一次。
见第81题
94.微软笔试题
求随机数构成的数组中找到长度大于=3的最长的等差数列
输出等差数列由小到大:
如果没有符合条件的就输出
格式:
输入[1,3,0,5,-1,6]
输出[-1,1,3,5]
要求时间复杂度,空间复杂度尽量小
实现起来不是很复杂。先排序,再求两个数的差,如果它们是等差数列,那么必定能写出第三个数,如果在序列中存在第三个数,则是一个等差数列,再查第四个数,直至找不到下一个数,记录长度公差起始点。长度有更长的,则更新记录。可能复杂度高点,不知道有没有较高效的解法。(已编码验证)
95.华为面试题
1 判断一字符串是不是对称的,如:abccba
两头夹挨个比,直到两指针相碰或发现两个字符不等。
2.用递归的方法判断整数组a[N]是不是升序排列
f(n)递增:{a[n]大于a[n-1];f[n-1]递增}
96.08年中兴校园招聘笔试题
1.编写strcpy 函数
已知strcpy 函数的原型是
char *strcpy(char *strDest, const char *strSrc);
其中strDest 是目的字符串,strSrc 是源字符串。不调用C++/C 的字符串库函数,请
编写函数 strcpy
挨个字符拷,遇’/0’停止。
最后压轴之戏,终结此微软等100题系列V0.1版。
那就,
连续来几组微软公司的面试题,让你一次爽个够:
======================
97.第1组微软较简单的算法面试题
1.编写反转字符串的程序,要求优化速度、优化空间。
参照第8题
2.在链表里如何发现循环链接?
参照第7题
3.编写反转字符串的程序,要求优化速度、优化空间。
参照第8题
4.给出洗牌的一个算法,并将洗好的牌存储在一个整形数组里。
洗牌关键是随机性。比如54张牌按概率1/54生成一张放在数组首位,再按1/53在剩余53张牌生成放在下一位。
随机函数可以调用标准库。原数组共54个数,每生成一个就把生成的数与最后一个交换,然后随机数范围减一,这样就不用删除原数组元素同时又能保证剩余元素的随机性。
5.写一个函数,检查字符是否是整数,如果是,返回其整数值。
(或者:怎样只用4行代码编写出一个从字符串到长整形的函数?)
参照20题
98.第2组微软面试题
1.给出一个函数来输出一个字符串的所有排列。
参照第53题
2.请编写实现malloc()内存分配函数功能一样的代码。
语法不太懂,参照一下标准库吧。
3.给出一个函数来复制两个字符串A和B。字符串A的后几个字节和字符串B的前几个字节重叠。
参见第85题
4.怎样编写一个程序,把一个有序整数数组放到二叉树中?
参见第86题
5.怎样从顶部开始逐层打印二叉树结点数据?请编程。
参见第16题
6.怎样把一个链表掉个顺序(也就是反序,注意链表的边界条件并考虑空链表)?
参见第8题。对空链表单独处理。
99.第3组微软面试题
1.烧一根不均匀的绳,从头烧到尾总共需要1个小时。
现在有若干条材质相同的绳子,问如何用烧绳的方法来计时一个小时十五分钟呢?
拿三根绳。第一根烧两头,第二根烧一头,第一根烧完则过了30分钟,第二根同时点另一头,烧完第二根则过了15分钟,再烧第三根,两头点,烧完又过了30分钟。加一起一小时十五分钟。
2.你有一桶果冻,其中有黄色、绿色、红色三种,闭上眼睛抓取同种颜色的两个。
抓取多少个就可以确定你肯定有两个同一颜色的果冻?(5秒-1分钟)
4个
3.如果你有无穷多的水,一个3公升的提捅,一个5公升的提捅,两只提捅形状上下都不均匀,
问你如何才能准确称出4公升的水?(40秒-3分钟)
有N种方法,就是求二元一次方程整数解3X+5Y=4。
把5公升桶水到至3公升桶里,还剩2公升,3公升桶倒干,把2公升倒至3公升桶,再装满5公升倒满3公升桶,剩4公升水。
一个岔路口分别通向诚实国和说谎国。
来了两个人,已知一个是诚实国的,另一个是说谎国的。
诚实国永远说实话,说谎国永远说谎话。现在你要去说谎国,
但不知道应该走哪条路,需要问这两个人。请问应该怎么问?(20秒-2分钟)
“如果我问他左边这条路到说谎国,他会告诉我是。是这样吗?”
对方回答“是”,则这条路通诚实国,反之则通说谎国。
100.第4组微软面试题,挑战思维极限
1.12个球一个天平,现知道只有一个和其它的重量不同,问怎样称才能用三次就找到那个球。
第一步分三份,每份四个,称一次可以找出那份不同的。
第二步取任意两个称,如果相同,再找一个换掉其中一个称。如果还相同,则未称的是有问题的,第二次称不同,则新拿进来的是有问题的球。如果第一次称就不同,换一个称,如果相同则拿掉的那个是有问题的,如果不同没拿下来的那个有问题。
13个呢?(注意此题并未说明那个球的重量是轻是重,所以需要仔细考虑)(5分钟-1小时)
分成445,先称44。
如果一样,问题球在5中。任取三个,再取三个正常球,如果平衡则剩下两个中存在一个有问题,拿其中一个与一个正常球比即可。如果不平衡可知取得的三个球必有一重(或轻)。三球中任取两个称,相等则剩下的球为问题球,不等则重的(或轻的)即为所求。
如果不一样,44分左重右轻两组,左边去除三留一,再加上右边取三个过来组成左边四个球,右边剩一个加上三个正常球。如果相等,则左边去除的三个球必定有一重球,再一次可求得这个球。如果左边重,则左边原来留下的球为问题球。如果左边高起右边低下,则从右边取来的三球中必有一轻球,再一次可求得这个球。
2.在9个点上画10条直线,要求每条直线上至少有三个点?(3分钟-20分钟)
坐标:(0,0)、(2,0)、(4,0)、(1,1)、(1,2)、(1,3)、(0,2)、(2,2)、(4,2)
3.在一天的24小时之中,时钟的时针、分针和秒针完全重合在一起的时候有几次?
都分别是什么时间?你怎样算出来的?(5分钟-15分钟)
两次。0点和12点
终结附加题:
微软面试题,挑战你的智商
==========
说明:如果你是第一次看到这种题,并且以前从来没有见过类似的题型,
并且能够在半个小时之内做出答案,说明你的智力超常..)
1.第一题 . 五个海盗抢到了100颗宝石,每一颗都一样大小和价值连城。他们决定这么分:
抽签决定自己的号码(1、2、3、4、5)
首先,由1号提出分配方案,然后大家表决,当且仅当超过半数的人同意时,
按照他的方案进行分配,否则将被扔进大海喂鲨鱼
如果1号死后,再由2号提出分配方案,然后剩下的4人进行表决,
当且仅当超过半数的人同意时,按照他的方案进行分配,否则将被扔入大海喂鲨鱼。
依此类推
条件:每个海盗都是很聪明的人,都能很理智地做出判断,从而做出选择。
问题:第一个海盗提出怎样的分配方案才能使自己的收益最大化?
著名的海盗分金问题。
如果只剩下45,那么无论4提出什么方案,5必定反对,所以4为了保命,无论如何也不会让他前面的人死完。
那么3就可以利用这一点,以(100.0.0)的方案分配,由于4为了保命,必定支持,而5的反对无效,于是超过半数同意。
2应该知道3的方案,他要想通过自己的方案必须三个人同意,于是他要拉拢45两个人,同时为了自己利润最大化,他考虑只各给一枚就可以保证他们同意,因为此方案45得利大于3号所给方案。于是2的方案是(98.0.1.1)。
1也知道2的方案,他要想通过自己的方案,必须另两个人同意,于是考虑拉拢34或35或45。于是对应方案应该是(97.0.1.2.0)、(97.0.1.0.2)、(96.0.0.2.2)。显然,1的最优方案是前两种。
2.一道关于飞机加油的问题,已知:
每个飞机只有一个油箱,
飞机之间可以相互加油(注意是相互,没有加油机)
一箱油可供一架飞机绕地球飞半圈,
问题:
为使至少一架飞机绕地球一圈回到起飞时的飞机场,至少需要出动几架飞机?
(所有飞机从同一机场起飞,而且必须安全返回机场,不允许中途降落,中间没有飞机场)
五架飞机。首先地球是圆的,可以反向飞行。先起动三架,到1/8处,3把12加满,3返航;;到1/4处,2把1加满,2返航;到1/2处,反向起动4;到3/4处,14相遇,4给1加1/4箱油,4返航,此时反向起动5;到7/8处,145相遇,5给14各加1/4箱油,5返航;最后一起回到起点。
http://m.blog.csdn.net/blog/lzc52151/6221852
posted on 2014-02-20 13:17 berkeleysong 阅读(642) 评论(0) 编辑 收藏 举报