微软等数据结构+算法面试100题系列之网友精彩回复 [二]
作者:July mimo9527
完整100题,请参见,
[珍藏版]微软等数据结构+算法面试100题全部出炉[100题首次完整亮相]
http://blog.csdn.net/v_JULY_v/archive/2010/12/06/6057286.aspx
以下所有的思路、答案选自网友mimo9527和我个人在这帖子上的回复:
本微软等100题系列V0.1版,永久维护(网友,思路回复)地址:
http://topic.csdn.net/u/20101126/10/b4f12a00-6280-492f-b785-cb6835a63dc9.html
继续欢迎,各位把自己针对此100题中任何一道,给出自己的思路,回复于上述帖子上,或于本博客上留言。:D。
========
为了表示对mimo9527的尊重,我尽量不对他的回复做修改与改动。
且尽量只贴思路,少或不贴代码。文末附上我个人对部分题、简单的思路回复。
欢迎,各位,毫不犹豫的,对以下的思路、或解法提出质疑、批评、指正。谢谢。
---------------------------------
mimo9527:
第8 题
此贴选一些比较怪的题,,由于其中题目本身与算法关系不大,仅考考思维。特此并作一题
1.非常有名的老题
2。 7= 1+2+4
3。
★假设你有一个用1001 个整数组成的数组,这些整数是任意排列的,但是你知道所有
的整数都在1 到1000(包括1000)之间。此外,除一个数字出现两次外,其他所有数字只出
现一次。假设你只能对这个数组做一次处理,用一种算法找出重复的那个数字。如果你在运
算中使用了辅助的存储方式,那么你能找到不用这种方式的算法吗?
★不用乘法或加法增加8 倍。现在用同样的方法增加7 倍。
-----------------------------------
重复的那个数字 = 数组之和 - 500500(1到1000之和)
8 倍:左移3位
7倍: 8倍-1倍
第10 题
翻转句子中单词的顺序。
题目:输入一个英文句子,翻转句子中单词的顺序,但单词内字符的顺序不变。
句子中单词以空格符隔开。为简单起见,标点符号和普通字母一样处理。
例如输入“I am a student.”,则输出“student. a am I”。
-----------------------
分析:
可以利用堆栈后进先出的特性,把输入先push到堆栈中,然后再pop出来,即反序。
第12 题
题目:求1+2+…+n,
要求不能使用乘除法、for、while、if 、else、switch、case 等关键字以及条件判断语句(A?B:C)
-----------------------------------
分析:
主要是能周期性的执行某一语句就可以了;
如启动一个周期性定时器之类的,每次超时调用一次;只是还没找到合适的定时器函数;
。。。。。。。。。
34.
实现一个队列。
队列的应用场景为:
一个生产者线程将int类型的数入列,一个消费者线程将int类型的数出列。
生产者消费者问题答案代码,有几个问题,
1。是生产和消费线程没有做互斥,如队列就是一个共享资源,需要互斥使用。
2。sleep没有必要。
3。WaitForMultipleObjects(2, handles, TRUE, INFINITE);应该没有用吧?
这位哥们,指的是我之前上传的资源,答案V0.3版关于第34题的答案。
47.创新工场:
求一个数组的最长递减子序列
比如{9,4,3,2,5,4,3,2}的最长递减子序列为{9,5,4,3,2}
-------------
分析:
相当于寻找和“9876543210”的公共子串,可以使用《算法导论》中的“最长公共子序列算法”。
-------------
前面没考虑数字大于10的情况,数字大于10的时候,不能直接作为字符串处理,而应该作为一个数组序列
,需要对程序做一点调整。
56.最长公共字串。
题目:如果字符串一的所有字符按其在字符串中的顺序出现在另外一个字符串二中,
则字符串一称之为字符串二的子串。
注意,并不要求子串(字符串一)的字符必须连续出现在字符串二中。
请编写一个函数,输入两个字符串,求它们的最长公共子串,并打印出最长公共子串。
例如:输入两个字符串BDCABA 和ABCBDAB,字符串BCBA 和BDAB 都是是它们的最
长公共子串,
则输出它们的长度4,并打印任意一个子串。
分析:求最长公共子串(Longest Common Subsequence, LCS)是一道非常经典的动态规划题,
因此一些重视算法的公司像MicroStrategy 都把它当作面试题。
-----------
与上楼相同,两个字符串BDCABA 和ABCBDAB都是《算法导论》例子里面的。。。。
void LongestCommonSubsequence()
{
char acStringX[]="abractyeyt";
char acStringY[]="dgdsaeactyey";
char acStringTemp[MAX_STRING_LEN];
int iStringXLen = strlen(acStringX);
int iStringYLen = strlen(acStringY);
int iStringTempLen;
if (iStringXLen >= MAX_STRING_LEN-1 ||iStringXLen >= MAX_STRING_LEN-1)
{
printf("string is too long!/n");
return;
}
memset(gaiComSubseqLen,0,MAX_STRING_LEN*MAX_STRING_LEN*sizeof(int));
LCS_Length(acStringX,acStringY);
LSC_Print(acStringX,iStringXLen,iStringYLen);
printf("/n");
return;
}
--------------------------
57.用俩个栈实现队列。
题目:某队列的声明如下:
template<typename T> class CQueue
{
public:
CQueue() {}
~CQueue() {}
void appendTail(const T& node); // append a element to tail
void deleteHead(); // remove a element from head
private:
T> m_stack1;
T> m_stack2;
};
分析:从上面的类的声明中,我们发现在队列中有两个栈。
因此这道题实质上是要求我们用两个栈来实现一个队列。
相信大家对栈和队列的基本性质都非常了解了:栈是一种后入先出的数据容器,
因此对队列进行的插入和删除操作都是在栈顶上进行;队列是一种先入先出的数据容器,
我们总是把新元素插入到队列的尾部,而从队列的头部删除元素。
-----------------------
分析:
这个自能是游戏,一点用也没有。
appendTail()简单,直接push到m_stack1中;
deleteHead():
pop m_stack1的每一个元素并push到m_stack2,但最后一个因为要删除不需要push到m_stack2,
然后再pop m_stack2的每一个元素并push到m_stack1。
这个过程画图比较清晰。
60.在O(1)时间内删除链表结点。
题目:给定链表的头指针和一个结点指针,在O(1)时间删除该结点。链表结点的定义如下:
struct ListNode
{
int m_nKey;
ListNode* m_pNext;
};
函数的声明如下:
void DeleteNode(ListNode* pListHead, ListNode* pToBeDeleted);
分析:这是一道广为流传的Google 面试题,能有效考察我们的编程基本功,
还能考察我们的反应速度,更重要的是,还能考察我们对时间复杂度的理解。
-----------------------
分析:
pToBeDeleted为尾节点或唯一节点时,比较简单;
否则,因为是单向链表,找不到pToBeDeleted的上一个节点,故删除比较麻烦,
所以可以将pToBeDeleted的下一个节点的内容复制到pToBeDeleted中,然后把pToBeDeleted的下一个节点删除。
62.找出链表的第一个公共结点。
题目:两个单向链表,找出它们的第一个公共结点。
链表的结点定义为:
struct ListNode
{
int m_nKey;
ListNode* m_pNext;
};
分析:这是一道微软的面试题。
微软非常喜欢与链表相关的题目,因此在微软的面试题中,链表出现的概率相当高。
-----------------------
分析:
两个链表公共节点之后的部分是完全相同的。因此从后往前找比较高效,但是单向链表做不到,
故将两个链表转换成2个数组,从2个数组的末尾开始一一对应比较,最后一个相同的点就是公共节点。
64. 寻找丑数。
题目:我们把只包含因子2、3 和5 的数称作丑数(Ugly Number)。
例如6、8 都是丑数,但14 不是,因为它包含因子7。习惯上我们把1 当做是第一个丑数。
求按从小到大的顺序的第1500 个丑数。
-----------------------
分析:
丑数=2^i*3^j*5^k(^表示多少次方),通过变换i,j,k的取值来计算;
但是我没有找到i,j,k如何取值的规律,只能不停的判断谁大谁小了。
65.输出1 到最大的N 位数
题目:输入数字n,按顺序输出从1 最大的n 位10 进制数。
比如输入3,则输出1、2、3 一直到最大的3 位数即999。
分析:这是一道很有意思的题目。看起来很简单,其实里面却有不少的玄机。
-----------------
循环长度为10的n次方,在循环之前把这个长度计算出来,别在循环里计算;
除此之外愣是没看出来有什么玄机,等待看别人的解答。
66.颠倒栈。
题目:用递归颠倒一个栈。例如输入栈{1, 2, 3, 4, 5},1 在栈顶。
颠倒之后的栈为{5, 4, 3, 2, 1},5 处在栈顶。
---------------------
分析:这个简单,
递归时把取出来的存在一个全局的数组里,然后根据要求再push回去;
pop();
存储;
判断是否是最后一个:
是:push(数组中最后一个,并在数组中删掉);
否:递归;
push(数组中最后一个,并在数组中删掉);
71.数值的整数次方。
题目:实现函数double Power(double base, int exponent),求base 的exponent 次方。
不需要考虑溢出。
-------------------------------
分析:
为了提高效率,考虑采用分治的思想:
1. 对exponent进行分解,如分解为2的i,j,k次方的和,然后对每一个递归计算;
2. 1中还有重复计算,可以增加一些处理,提高效率。
74.数组中超过出现次数超过一半的数字
题目:数组中有一个数字出现的次数超过了数组长度的一半,找出这个数字。
分析:这是一道广为流传的面试题,包括百度、微软和Google 在内的多家公司
都曾经采用过这个题目。要几十分钟的时间里很好地解答这道题,除了较好的编程能力之外,
还需要较快的反应和较强的逻辑思维能力。
----------------------------------
分析:
1. 出现次数大于数组长度N的一半的数字,一定在数组中的前一半中至少出现一次;
2. 先遍历数组的开始一半(注:如果N为奇数,则前一半多放一个),得到出现的每一个数及其出现次数的列表List;
3.开始遍历数组的后一半 FOR J=N/2; J<=N;J++:
3.1 如果是List中的数,则将List中该数出现的次数 +1;
3.2 遍历List中的每一个数:
3.2.1 如果该数出现的次数+(N-J)<=N/2,则说明该数不可能是所求数字,从List中删除该数;
3.2.1 如果该数出现的次数 > N/2,则说明该数就是所求数字;
3.3 如果List中只剩下一个数,则该数即为所求的数字;
75.二叉树两个结点的最低共同父结点
题目:二叉树的结点定义如下:
struct TreeNode
{
int m_nvalue;
TreeNode* m_pLeft;
TreeNode* m_pRight;
};
输入二叉树中的两个结点,输出这两个结点在数中最低的共同父结点。
------------------------------
分析:
进行二叉树遍历(递归),并在遍历中加入如下内容:
1.如果当前节点等于某一个输入节点,则返回1;如果不是则继续向左、右子节点遍历,如果一直到叶子节点仍未找到,则返回0;
2.如果节点的左子节点遍历和右子节点遍历都返回1,则该节点就是所求的最低的共同父结点;
80.阿里巴巴一道笔试题
引自baihacker
问题描述:
12 个高矮不同的人,排成两排,每排必须是从矮到高排列,而且第二排比对应的第一排的人高,
问排列方式有多少种?
这个笔试题,很YD,因为把某个递归关系隐藏得很深.
----------------------------
这个比较简单,思路如下:
1.从12个人中任意选取2个人出来,组成一列,根据组合数学有C(12,2)中方法;
2.再从剩余的10个人中任意选取2个人出来,组成下一列,根据组合数学有C(10,2)中方法;
3.以此类推;
4.根据组合数学的乘法原理,总的排列方式数= C(12,2)*C(10,2)*C(8,2)*C(6,2)*C(4,2)*C(2,2)=748440
5.根据上面的内容,很容易写出递归程序。
------------------------------------
结果是132,
可以抽象成将12个数填写到一个二维数组中,按从小到大的顺序填写(从大到小也一样),
填写的条件就是左边不能为空,下边不能为空。
---------------------------------------
最后附上我个人关于一些题的思路、简单回复:
July:
今天,暂写一下,前20题的,我个人的思路:
1.把二元树转化成排序的双向链表
中序遍历二元树,然后,把二元树各结点连接,转化成List。
2.设计包含min函数的栈。
这题,见的实在太多了。
我上传的答案V0.2版,已花了不少篇幅,阐述它(可参考,画个图,可能会更清晰点)。
3.求子数组的最大和
不考虑数组中全为负数的情况。
即遍历一次,就找到那些变量,然后相加。
可设俩个变量,b(数组元素为负,不加,把下一个元素赋给b,数组元素非负,相加),
sum,并始终与b比较,sum<b,则更新sum(把b赋给sum)...最后,return sum。
4.在二元树中找出和为某一值的所有路径
相当于给定一个数值,然后,在树上,找出那些结点相加的和,等于这个数值的所有结点。
还是遍历这棵二元树(递归),每访问一个叶结点,结点值相加,同时把结点进栈,
当最后的和,等于这个数值,打印此条路径。
否则,退回到父结点,出栈,重新查找。
5.查找最小的k个元素
参考答案V0.2版,上有完整源程序。
6.腾讯面试题
此题,只要找出规律后,就能很快写出下排的数。
当然,编程求解,有一定难度。请参考答案V0.3版。
7.判断俩个链表是否相交
问题转化为:
1.先判断带不带换
2.如果都不带环,判断尾结点是否相等
3.如果都带环,判断一链表上俩指针相遇的那个结点,
是否在另一条链表上。
如果在,则相交,不在,则不相交。
8.微软面试题。
此题较繁琐,略。
9.判断整数序列是不是二元查找树的后序遍历结果
考察树的后序遍历。
10.翻转句子中单词的顺序。
这题,很多人,都想到了。略。
11.求二叉树中结点的最大距离
若不清楚题意,网上搜下,看下图。
且答案V0.2版,已花了不小的篇幅阐述了此题,请参考。
12.求1+2+....n(诸多限制)
循环本质,即让同样的代码执行n次而已。
很多方法:
1.利用构造函数的特殊性质
2.模板
...
13.输出链表中倒数第k个结点
此题的思路,用俩指针,一指head,一指k-1个元素,相隔k的距离
很多人,都知道。
14.给定一个数值,查找相加的和,等于此数值的俩个数
此题与第21题差不多,但难度不及第21题。且还有序数列。
15.把树翻转,转化为它的镜像
利用递归,借助栈。
16.从上到下按层,打印树的每个结点
非树的,前、中、后序遍历,乃树的层次遍历。
利用队列(本质即是一种BFS,不知各位,是否察觉)。
其实,此题,还可以出的更难点。:D..
17.在一个字符串中找到第一个只出现一次的字符
hash表。
18.约瑟夫循环
相信,大家对这个问题,都明了,不用我啰嗦。
19.斐波拉契数列。
注意细节。略。
20.字符串转换成整数输出。
同样,注意细节问题。:D..
//下面的,待续。Copy right@July 2010年11月28日。
针对262 楼 litaoye 的回复:
26.左旋转字符串
跟panda所想,是一样的,即,
以abcdef为例
1. ab->ba
2. cdef->fedc
原字符串变为bafedc
3. 整个翻转:cdefab
//时间复杂度为O(n)
在此,奉献另外一种思路:
abc defghi,要abc移动至最后
abc defghi->def abcghi->def ghiabc
一俩指针,p1指向ch[0],p2指向[ch m-1],
p2每次移动m 的距离,p1 也跟着相应移动,
每次移动过后,交换。
如上第一步,交换abc 和def ,就变成了 abcdef->defabc
第一步,
abc defghi->def abcghi
第二步,继续交换,
def abcghi->def ghiabc
整个过程,看起来,就是abc 一步一步 向后移动
abc defghi
def abcghi
def ghi abc
//最后的 复杂度是O(m+n)
再举一个例子,
如果123 4567890要变成4567890 123:
123 4567890
1. 456 123 7890
2. 456789 123 0
3. 456789 12 0 3
4. 456789 1 0 23
5. 4567890 123 //最后三步,相当于0前移,p1已经不动。
欢迎,就此第26题,继续讨论。
40.百度研发笔试题
40.1.设计min函数的栈。
这个问题,我已经在答案V0.2,V0.3版里,讨论过很多次了。
欢迎,各位大侠,批评指正。
此题的第1小题,即是借助辅助栈,保存最小值,
且随时更新辅助栈中的元素。
如先后,push 2 6 4 1 5
stack A stack B(辅助栈)
4: 5 1 //push 5,min=p->[3]=1 ^
3: 1 1 //push 1,min=p->[3]=1 | //此刻push进A的元素1小于B中栈顶元素2
2: 4 2 //push 4,min=p->[0]=2 |
1: 6 2 //push 6,min=p->[0]=2 |
0: 2 2 //push 2,min=p->[0]=2 |
push第一个元素进A,也把它push进B,
当向Apush的元素比B中的元素小, 则也push进B,即更新B。否则,不动B,保存原值。
向栈A push元素时,顺序由下至上。
辅助栈B中,始终保存着最小的元素。
然后,pop栈A中元素,5 1 4 6 2
A B ->更新
4: 5 1 1 //pop 5,min=p->[3]=1 |
3: 1 1 2 //pop 1,min=p->[0]=2 |
2: 4 2 2 //pop 4,min=p->[0]=2 |
1: 6 2 2 //pop 6,min=p->[0]=2 |
0: 2 2 NULL //pop 2,min=NULL v
当pop A中的元素小于B中栈顶元素时,则也要pop B中栈顶元素。
40.2.一串首尾相连的珠子(m个),有N种颜色(N<=10),
设计一个算法,取出其中一段,要求包含所有N中颜色,并使长度最短。
一网友给的思路:
比如,字符集是a,b,c,字符串是abdcaabcx,则最短子串为abc
用两个变量 front,rear 指向一个的子串区间的头和尾
用一个int cnt[255]={0}记录当前这个子串里 字符集a,b,c 各自的个数,
一个变量sum记录字符集里有多少个了。
rear 一直加,更新cnt[]和sum的值,直到 sum等于字符集个数
然后front++,直到cnt[]里某个字符个数为0,这样就找到一个符合条件的字串了
继续前面的操作,就可以找到最短的了。
//http://www.gevgb.com/bbs/viewthread.php?tid=21&extra=page%3D1
40.3.欢迎,其他人,继续共享自己的思路。
49.一道看上去很吓人的算法面试题:
如何对n个数进行排序,要求时间复杂度O(n),空间复杂度O(1)
题目,没有问题,再想想。
//Copy right@July 2010年11月30日。
==============
1.关于本微软等公司数据结构+算法面试100题系列V0.1版的郑重声明
http://blog.csdn.net/v_JULY_v/archive/2010/12/02/6050133.aspx
2.完整100题,请参见,
[珍藏版]微软等数据结构+算法面试100题全部出炉[100题首次完整亮相]
http://blog.csdn.net/v_JULY_v/archive/2010/12/06/6057286.aspx
3.更多详情,请参见,本人博客:
My Blog:
http://blog.csdn.net/v_JULY_v
4.所有的资源(题目+部分答案)下载地址:
http://v_july_v.download.csdn.net/
5.本微软等100题系列V0.1版,永久维护(网友,思路回复)地址:
http://topic.csdn.net/u/20101126/10/b4f12a00-6280-492f-b785-cb6835a63dc9.html
作者声明:
本人July对本博客所有任何内容和资料享有版权,转载请注明作者本人July及出处。
永远,向您的厚道致敬。谢谢。July、二零一零年十二月三十日凌晨三点。