微软等数据结构+算法面试100题全部答案集锦
转载自http://blog.csdn.net/v_july_v/article/details/6870251
作者:July、阿财。
时间:二零一一年十月十三日。
引言
无私分享造就开源的辉煌。
今是二零一一年十月十三日,明日14日即是本人刚好开博一周年。在一周年之际,特此分享出微软面试全部100题答案的完整版,以作为对本博客所有读者的回馈。
一年之前的10月14日,一个名叫July (头像为手冢国光)的人在一个叫csdn的论坛上开帖分享微软等公司数据结构+算法面试100题,自此,与上千网友一起做,一起思考,一起解答这些面试题目,最终成就了一个名为:结构之法算法之道的编程面试与算法研究并重的博客,如今,此博客影响力逐步渗透到海外,及至到整个互联网。
在此之前,由于本人笨拙,这微软面试100题的答案只整理到了前60题(第1-60题答案可到本人资源下载处下载:http://v_july_v.download.csdn.net/),故此,常有朋友留言或来信询问后面40题的答案。只是因个人认为:一、答案只是作为一个参考,不可太过依赖;二、常常因一些事情耽搁(如在整理最新的今年九月、十月份的面试题:九月腾讯,创新工场,淘宝等公司最新面试十三题、十月百度,阿里巴巴,迅雷搜狗最新面试十一题);三、个人正在针对那100题一题一题的写文章,多种思路,不断优化,即成程序员编程艺术系列(详情,参见文末)。自此,后面40题的答案迟迟未得整理。且个人已经整理的前60题的答案,在我看来,是有诸多问题与弊端的,甚至很多答案都是错误的。
(微软10题永久讨论地址:http://topic.csdn.net/u/20101126/10/b4f12a00-6280-492f-b785-cb6835a63dc9_9.html)
互联网总是能给人带来惊喜。前几日,一位现居美国加州的名叫阿财的朋友发来一封邮件,并把他自己做的全部100题的答案一并发予给我,自此,便似遇见了知己。十分感谢。
任何东西只有分享出来才更显其价值。本只需贴出后面40题的答案,因为前60题的答案本人早已整理上传至网上,但多一种思路多一种参考亦未尝不可。特此,把阿财的答案再稍加整理番,然后把全部100题的答案现今都贴出来。若有任何问题,欢迎不吝指正。谢谢。
上千上万的人都关注过此100题,且大都都各自贡献了自己的思路,或回复于微软100题维护地址上,或回复于本博客内,人数众多,无法一一标明,特此向他们诸位表示敬意和感谢。谢谢大家,诸君的努力足以影响整个互联网,咱们已经迎来一个分享互利的新时代。
微软面试100题全部答案
更新:有朋友反应,以下的答案中思路过于简略,还是这句话,一切以程序员编程艺术系列(多种思路,多种比较,细细读之自晓其理)为准(我没怎么看阿财的这些答案,因为编程艺术系列已经说得足够清晰了。之所以把阿财的这份答案分享出来,一者,编程艺术系列目前还只写到了第二十二章,即100题之中还只详细阐述了近30道题;二者,他给的答案全部是用英文写的,这恰好方便国外的一些朋友参考;三者是为了给那一些急功近利的、浮躁的人一份速成的答案罢了)。July、二零一一年十月二十四日更新。
当然,读者朋友有任何问题,你也可以跟阿财联系,他的邮箱地址是:kevinn9#gmail.com (把#改成@)。
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
};
ANSWER:
This is a traditional problem that can be solved using recursion.
For each node, connect the double linked lists created from left and right child node to form a full list.
/**
* @param root The root node of the tree
* @return The head node of the converted list.
*/
BSTreeNode * treeToLinkedList(BSTreeNode * root) {
BSTreeNode * head, * tail;
helper(head, tail, root);
return head;
}
void helper(BSTreeNode & head, BSTreeNode & tail, BSTreeNode *root) {
BSTreeNode *lt, *rh;
if (root == NULL) {
head = NULL, tail = NULL;
return;
}
helper(head, lt, root->m_pLeft);
helper(rh, tail, root->m_pRight);
if (lt!=NULL) {
lt->m_pRight = root;
root->m_pLeft = lt;
} else {
head = root;
}
if (rh!=NULL) {
root->m_pRight=rh;
rh->m_pLeft = root;
} else {
tail = root;
}
}
2.设计包含min 函数的栈。
定义栈的数据结构,要求添加一个min 函数,能够得到栈的最小元素。
要求函数min、push 以及pop 的时间复杂度都是O(1)。
ANSWER:
Stack is a LIFO data structure. When some element is popped from the stack, the status will recover to the original status as before that element was pushed. So we can recover the minimum element, too.
struct MinStackElement {
int data;
int min;
};
struct MinStack {
MinStackElement * data;
int size;
int top;
}
MinStack MinStackInit(int maxSize) {
MinStack stack;
stack.size = maxSize;
stack.data = (MinStackElement*) malloc(sizeof(MinStackElement)*maxSize);
stack.top = 0;
return stack;
}
void MinStackFree(MinStack stack) {
free(stack.data);
}
void MinStackPush(MinStack stack, int d) {
if (stack.top == stack.size) error(“out of stack space.”);
MinStackElement* p = stack.data[stack.top];
p->data = d;
p->min = (stack.top==0?d : stack.data[top-1]);
if (p->min > d) p->min = d;
top ++;
}
int MinStackPop(MinStack stack) {
if (stack.top == 0) error(“stack is empty!”);
return stack.data[–stack.top].data;
}
int MinStackMin(MinStack stack) {
if (stack.top == 0) error(“stack is empty!”);
return stack.data[stack.top-1].min;
}
3.求子数组的最大和
题目:
输入一个整形数组,数组里有正数也有负数。
数组中连续的一个或多个整数组成一个子数组,每个子数组都有一个和。
求所有子数组的和的最大值。要求时间复杂度为O(n)。
例如输入的数组为1, -2, 3, 10, -4, 7, 2, -5,和最大的子数组为3, 10, -4, 7, 2,
因此输出为该子数组的和18。
ANSWER:
A traditional greedy approach.
Keep current sum, slide from left to right, when sum < 0, reset sum to 0.
int maxSubarray(int a[], int size) {
if (size<=0) error(“error array size”);
int sum = 0;
int max = – (1 << 31);
int cur = 0;
while (cur < size) {
sum += a[cur++];
if (sum > max) {
max = sum;
} else if (sum < 0) {
sum = 0;
}
}
return max;
}
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
};
ANSWER:
Use backtracking and recurison. We need a stack to help backtracking the path.
struct TreeNode {
int data;
TreeNode * left;
TreeNode * right;
};
void printPaths(TreeNode * root, int sum) {
int path[MAX_HEIGHT];
helper(root, sum, path, 0);
}
void helper(TreeNode * root, int sum, int path[], int top) {
path[top++] = root.data;
sum -= root.data;
if (root->left == NULL && root->right==NULL) {
if (sum == 0) printPath(path, top);
} else {
if (root->left != NULL) helper(root->left, sum, path, top);
if (root->right!=NULL) helper(root->right, sum, path, top);
}
top –;
sum += root.data; //….
}
5.查找最小的k 个元素
题目:输入n 个整数,输出其中最小的k 个。
例如输入1,2,3,4,5,6,7 和8 这8 个数字,则最小的4 个数字为1,2,3 和4。
ANSWER:
This is a very traditional question…
O(nlogn): cat I_FILE | sort -n | head -n K
O(kn): do insertion sort until k elements are retrieved.
O(n+klogn): Take O(n) time to bottom-up build a min-heap. Then sift-down k-1 times.
So traditional that I don’t want to write the codes…
Only gives the siftup and siftdown function.
/**
*@param i the index of the element in heap a[0…n-1] to be sifted up
void siftup(int a[], int i, int n) {
while (i>0) {
int j=(i&1==0 ? i-1 : i+1);
int p=(i-1)>>1;
if (j
define HBASE 127
int rc_strstr(char * str, char * sub) {
int dest= 0;
char * p = sub;
int len = 0;
int TO_REDUCE = 1;
while (*p!=’\0’) {
dest = HBASE * dest + (int)(*p);
TO_REDUCE *= HBASE;
len ++;
}
int hash = 0;
p = str;
int i=0;
while (*p != ‘\0’) {
if (i++
define T(X, Y, i) (Y & (1<
define MAX_NUM 201
int inDegree[MAX_NUM];
int longestConcat(char ** strs, int m, int n) {
int graph[MAX_NUM][MAX_NUM];
int prefixHash[MAX_NUM];
int suffixHash[MAX_NUM];
int i,j;
for (i=0; i
define MAX_PATH 0
int d[MAX_NUM];
int longestPath(int graph[], int n) {
memset(visit, 0, n*sizeof(int));
if (topSort(graph) == 0) return -1; //topological sort failed, there is cycle.
int min = 0;
for (int i=0; i
Have done this before.
51.和为n 连续正数序列。
题目:输入一个正数n,输出所有和为n 连续正数序列。
例如输入15,由于1+2+3+4+5=4+5+6=7+8=15,所以输出3 个连续序列1-5、4-6 和7-8。
分析:这是网易的一道面试题。
ANSWER:
It seems that this can be solved by factorization. However, factorization of large n is impractical!
Suppose n=i+(i+1)+…+(j-1)+j, then n = (i+j)(j-i+1)/2 = (j*j – i*i + i + j)/2
=> j^2 + j + (i-i^2-2n) = 0 => j=sqrt(i^2-i+1/4+2n) – 1/2
We know 1 <= i < j <= n/2 + 1
So for each i in [1, n/2], do this arithmetic to check if there is a integer answer.
int findConsecutiveSequence(int n) {
int count = 0;
for (int i=1; i<=n/2; i++) {
int sqroot = calcSqrt(4*i*i+8*n-4*i+1);
if (sqroot == -1) continue;
if ((sqroot & 1) == 1) {
System.out.println(i+”-” + ((sqroot-1)/2));
count ++;
}
}
return count;
}
Use binary search to calculate sqrt, or just use math functions.
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; // left child of node
SBinaryTreeNode *m_pRight; // right child of node
};
分析:这道题本质上还是考查二元树的遍历。
ANSWER:
Have done this.
53.字符串的排列。
题目:输入一个字符串,打印出该字符串中字符的所有排列。
例如输入字符串abc,则输出由字符a、b、c 所能排列出来的所有字符串
abc、acb、bac、bca、cab 和cba。
分析:这是一道很好的考查对递归理解的编程题,
因此在过去一年中频繁出现在各大公司的面试、笔试题中。
ANSWER:
Full permutation generation. I will use another technique that swap two neighboring characters each time. It seems that all the characters are different. I need to think about how to do it when duplications is allowed. Maybe simple recursion is better for that.
void generatePermutation(char s[], int n) {
if (n>20) { error(“are you crazy?”); }
byte d[n];
int pos[n], dpos[n]; // pos[i], the position of i’th number, dpos[i] the number in s[i] is the dpos[i]’th smallest
qsort(s); // I cannot remember the form of qsort in C…
memset(d, -1, sizeof(byte)*n);
for (int i=0; i
define aswap(ARR, X, Y) {int t=ARR[X]; ARR[X]=ARR[y]; ARR[Y]=t;}
void swap(char s[], int pos[], int dpos[], byte d[], int r, int s) {
aswap(s, r, s);
aswap(d, r, s);
aswap(pos, dpos[r], dpos[s]);
aswap(dpos, r, s);
}
Maybe full of bugs. Please refer to algorithm manual for explansion.
Pros: Amotized O(1) time for each move. Only two characters change position for each move.
Cons: as you can see, very complicated. Extra space needed.
54.调整数组顺序使奇数位于偶数前面。
题目:输入一个整数数组,调整数组中数字的顺序,使得所有奇数位于数组的前半部分,
所有偶数位于数组的后半部分。要求时间复杂度为O(n)。
ANSWER:
This problem makes me recall the process of partition in quick sort.
void partition(int a[], int n) {
int i=j=0;
while (i < n && (a[i] & 1)==0) i++;
if (i==n) return;
swap(a, i++, j++);
while (i
连续来几组微软公司的面试题,让你一次爽个够:
97.第1 组微软较简单的算法面试题
1.编写反转字符串的程序,要求优化速度、优化空间。
ANSWER
Have done this.
2.在链表里如何发现循环链接?
ANSWER
Have done this.
3.编写反转字符串的程序,要求优化速度、优化空间。
ANSWER
Have done this.
4.给出洗牌的一个算法,并将洗好的牌存储在一个整形数组里。
ANSWER
Have done this.
5.写一个函数,检查字符是否是整数,如果是,返回其整数值。
(或者:怎样只用4 行代码编写出一个从字符串到长整形的函数?)
ANSWER
Char or string?
have done atoi;
98.第2 组微软面试题
1.给出一个函数来输出一个字符串的所有排列。
ANSWER
Have done this…
2.请编写实现malloc()内存分配函数功能一样的代码。
ANSWER
Way too hard as an interview question…
Please check wikipedia for solutions…
3.给出一个函数来复制两个字符串A 和B。字符串A 的后几个字节和字符串B 的前几个字节重叠。
ANSWER
Copy from tail to head.
4.怎样编写一个程序,把一个有序整数数组放到二叉树中?
ANSWER
Have done this.
5.怎样从顶部开始逐层打印二叉树结点数据?请编程。
ANSWER
Have done this…
6.怎样把一个链表掉个顺序(也就是反序,注意链表的边界条件并考虑空链表)?
ANSWER
Have done this…
99.第3 组微软面试题
1.烧一根不均匀的绳,从头烧到尾总共需要1 个小时。现在有若干条材质相同的绳子,问如何用烧绳的方法来计时一个小时十五分钟呢?
ANSWER
May have done this… burn from both side gives ½ hour.
2.你有一桶果冻,其中有黄色、绿色、红色三种,闭上眼睛抓取同种颜色的两个。抓取多少个就可以确定你肯定有两个同一颜色的果冻?(5 秒-1 分钟)
ANSWER
4.
3.如果你有无穷多的水,一个3 公升的提捅,一个5 公升的提捅,两只提捅形状上下都不均
匀,问你如何才能准确称出4 公升的水?(40 秒-3 分钟)
ANSWER
5 to 3 => 2
2 to 3, remaining 1
5 to remaining 1 => 4
一个岔路口分别通向诚实国和说谎国。
来了两个人,已知一个是诚实国的,另一个是说谎国的。
诚实国永远说实话,说谎国永远说谎话。现在你要去说谎国,
但不知道应该走哪条路,需要问这两个人。请问应该怎么问?(20 秒-2 分钟)
ANSWER
Seems there are too many answers.
I will pick anyone to ask: how to get to your country? Then pick the other way.
100.第4 组微软面试题,挑战思维极限
1.12 个球一个天平,现知道只有一个和其它的重量不同,问怎样称才能用三次就找到那个
球。13 个呢?(注意此题并未说明那个球的重量是轻是重,所以需要仔细考虑)(5 分钟-1 小时)
ANSWER
Too complicated. Go find brain teaser answers by yourself.
2.在9 个点上画10 条直线,要求每条直线上至少有三个点?(3 分钟-20 分钟)
3.在一天的24 小时之中,时钟的时针、分针和秒针完全重合在一起的时候有几次?都分别是什么时间?你怎样算出来的?(5 分钟-15 分钟)
30
终结附加题:
微软面试题,挑战你的智商
说明:如果你是第一次看到这种题,并且以前从来没有见过类似的题型,
并且能够在半个小时之内做出答案,说明你的智力超常..)
1.第一题. 五个海盗抢到了100 颗宝石,每一颗都一样大小和价值连城。他们决定这么分:
抽签决定自己的号码(1、2、3、4、5)
首先,由1 号提出分配方案,然后大家表决,当且仅当超过半数的人同意时,
按照他的方案进行分配,否则将被扔进大海喂鲨鱼
如果1 号死后,再由2 号提出分配方案,然后剩下的4 人进行表决,
当且仅当超过半数的人同意时,按照他的方案进行分配,否则将被扔入大海喂鲨鱼。
依此类推
条件:每个海盗都是很聪明的人,都能很理智地做出判断,从而做出选择。
问题:第一个海盗提出怎样的分配方案才能使自己的收益最大化?
Answer:
A traditional brain teaser.
Consider #5, whatever #4 proposes, he won’t agree, so #4 must agree whatever #3 proposes. So if there are only #3-5, #3 should propose (100, 0, 0). So the expected income of #3 is 100, and #4 and #5 is 0 for 3 guy problem. So whatever #2 proposes, #3 won’t agree, but if #2 give #4 and #5 1, they can get more than 3-guy subproblem. So #2 will propose (98, 0, 1, 1). So for #1, if give #2 less than 1, they can get more than 3-guy subproblem. So #2 will propose (98, 0, 1, 1). So for #1, if give #2 less than 98, #2 won’t agree. But he can give #3 1 and #4 or #5 1 and #4 or #5 2, so this is a (97, 0, 1, 2, 0) solution.
2.一道关于飞机加油的问题,已知:
每个飞机只有一个油箱,
飞机之间可以相互加油(注意是相互,没有加油机)
一箱油可供一架飞机绕地球飞半圈,
问题:
为使至少一架飞机绕地球一圈回到起飞时的飞机场,至少需要出动几架飞机?
(所有飞机从同一机场起飞,而且必须安全返回机场,不允许中途降落,中间没有飞机场)
Pass。ok,微软面试全部100题答案至此完。