1. 程式人生 > >微軟等資料結構+演算法面試100題全部答案集錦

微軟等資料結構+演算法面試100題全部答案集錦

引言
無私分享造就開源的輝煌。

 今是二零一一年十月十三日,明日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 than98, #2 won’t agree. But he can give #3 1 and #4 or #52, so this is a (97, 0, 1, 2, 0) solution.

2.一道關於飛機加油的問題,已知:
每個飛機只有一個油箱,
飛機之間可以相互加油(注意是相互,沒有加油機)
一箱油可供一架飛機繞地球飛半圈,
問題:
為使至少一架飛機繞地球一圈回到起飛時的飛機場,至少需要出動幾架飛機?
(所有飛機從同一機場起飛,而且必須安全返回機場,不允許中途降落,中間沒有飛機場)

Pass。ok,微軟面試全部100題答案至此完。