《算法设计手册》面试题解答 第三章:数据结构
3-18.
你查字典時用的是什么方法?
解析:
既然要和算法相關,除了隨機嘗試,明顯應該用二分查找。
不要嫌它簡單,其實這是道MS和Google用過的面試題。
?
3-19.
假設你有一個裝滿T恤衫的衣柜,如何組織這些T恤衫以便以后取衣服?
解析:
由于衣柜是線性存儲,肯定要用到排序從而應用二分查找。具體到T恤衫,可以按顏色排序。
當然,如果這是個商場中的衣柜,那么還應該在同樣式內部按尺碼排序。
?
3-20.
寫一個函數,找出單鏈表的中間結點。
解析:
比較常見,最簡單的解法是用兩個指針,一個每次后移1個,另一個每次后移2個,速度快的達到末尾時,慢的正好在中間。實際編寫的時候注意邊界條件(最好把NULL做第0個結點,考慮結點數奇偶性不要把快的指針移動出界),略。
?
3-21.
寫一個函數判斷兩個二叉樹是否全等。樹全等包括結構相同和對應結點數據相同。
解析:
二叉樹遍歷的變形,只是寫函數返回值時可能迷惑??梢赃@樣寫:
int compare(struct node* a, struct node* b) {// 1. both empty -> trueif (a==NULL && b==NULL) return(true); // 2. both non-empty -> compare themelse if (a!=NULL && b!=NULL) {return(a->data == b->data &&compare(a->left, b->left) &&compare(a->right, b->right));}// 3. one empty, one not -> falseelse return 0; }?
3-22.
寫一個把二叉搜索樹轉化為鏈表的程序。
解析:
如果允許使用額外的輔助數據結構,可以遍歷樹時構造鏈表,也可以遍歷樹時把結點壓入隊列最后出隊構造。
如果不允許使用額外的存儲空間,把二叉搜索樹原地轉化為雙鏈表,思想還是樹的遞歸遍歷:把根的左子樹和右子樹分別轉化為雙鏈表,再與根相連接。只不過連接時右子樹,要注意是把原先的根作為雙鏈表末尾來連接。下面貼的代碼來自于何海濤《劍指Offer》面試題27:二叉搜索樹與雙向鏈表:
void ConvertNode(BinaryTreeNode* pNode, BinaryTreeNode** pLastNodeInList);BinaryTreeNode* Convert(BinaryTreeNode* pRootOfTree) {BinaryTreeNode *pLastNodeInList = NULL;ConvertNode(pRootOfTree, &pLastNodeInList);// pLastNodeInList指向雙向鏈表的尾結點,// 我們需要返回頭結點BinaryTreeNode *pHeadOfList = pLastNodeInList;while(pHeadOfList != NULL && pHeadOfList->m_pLeft != NULL)pHeadOfList = pHeadOfList->m_pLeft;return pHeadOfList; }void ConvertNode(BinaryTreeNode* pNode, BinaryTreeNode** pLastNodeInList) {if(pNode == NULL)return;BinaryTreeNode *pCurrent = pNode;if (pCurrent->m_pLeft != NULL)ConvertNode(pCurrent->m_pLeft, pLastNodeInList);pCurrent->m_pLeft = *pLastNodeInList; if(*pLastNodeInList != NULL)(*pLastNodeInList)->m_pRight = pCurrent;*pLastNodeInList = pCurrent;if (pCurrent->m_pRight != NULL)ConvertNode(pCurrent->m_pRight, pLastNodeInList); } ConvertBinarySearchTree既然來自于《劍指Offer》,這里也附注下好了,設計的測試用例為:
- 功能測試:完全二叉樹、所有節點都沒有左/右子樹、只有一個結點的二叉樹。
- 特殊輸入測試:指向根節點指針為NULL。
?
3-23.
寫一個逆置鏈表的函數。再寫一個非遞歸實現。
解析:
遞歸實現只要將當前結點作為已處理部分的最后一個結點附加上去即可。
//http://nbl.cewit.stonybrook.edu:60128/mediawiki/index.php/TADM2E_3.23 Node* Reverse(Node*head) {Node* temp=NULL;if(head==NULL){return NULL;}if(head->next==NULL)return head;temp=Reverse(head->next);head->next->next=head;head->next=NULL;return temp; } 遞歸實現非遞歸實現需要保存當前操縱結點的下一個結點以免斷鏈,一次遍歷即可。下面的代碼來自于何海濤《劍指Offer》面試題16:反轉鏈表:
ListNode* ReverseList(ListNode* pHead) {ListNode* pReversedHead = NULL;ListNode* pNode = pHead;ListNode* pPrev = NULL;while(pNode != NULL){ListNode* pNext = pNode->m_pNext;if(pNext == NULL)pReversedHead = pNode;pNode->m_pNext = pPrev;pPrev = pNode;pNode = pNext;}return pReversedHead; } 非遞歸實現測試用例:
- 鏈表頭指針是NULL。
- 輸入鏈表只有一個頭結點。
- 輸入鏈表有多個結點。
?
3-24.
為網頁爬蟲設計一個數據結構,來判斷URL是否被訪問過。要求時間空間都最優。
解析:
我一開始想到的是URL分段進行hash,有人說用前綴樹完成匹配,http://nbl.cewit.stonybrook.edu:60128/mediawiki/index.php/TADM2E_3.24提到使用布隆過濾器。簡單了解下吧,看上去和分段hash的思路很類似。
布隆過濾器的原理是,當一個元素被加入集合時,通過K個Hash函數將這個元素映射成一個位陣列(Bit array)中的K個點,把它們置為1。檢索時,我們只要看看這些點是不是都是1就(大約)知道集合中有沒有它了:如果這些點有任何一個0,則被檢索元素一定不在;如果都是1,則被檢索元素很可能在。這就是布隆過濾器的基本思想。
優點
相比于其它的數據結構,布隆過濾器在空間和時間方面都有巨大的優勢。布隆過濾器存儲空間和插入/查詢時間都是常數()。另外, Hash函數相互之間沒有關系,方便由硬件并行實現。布隆過濾器不需要存儲元素本身,在某些對保密要求非常嚴格的場合有優勢。
布隆過濾器可以表示全集,其它任何數據結構都不能;
和相同,使用同一組Hash函數的兩個布隆過濾器的交并差運算可以使用位操作進行。
缺點
但是布隆過濾器的缺點和優點一樣明顯。誤算率是其中之一。隨著存入的元素數量增加,誤算率隨之增加。但是如果元素數量太少,則使用散列表足矣。
另外,一般情況下不能從布隆過濾器中刪除元素. 我們很容易想到把位列陣變成整數數組,每插入一個元素相應的計數器加1, 這樣刪除元素時將計數器減掉就可以了。然而要保證安全地刪除元素并非如此簡單。首先我們必須保證刪除的元素的確在布隆過濾器里面. 這一點單憑這個過濾器是無法保證的。另外計數器回繞也會造成問題。
在降低誤算率方面,有不少工作,使得出現了很多布隆過濾器的變種。
?
3.25
給定一個字符串和一本雜志,需要你從雜志上剪下來字母從而拼成這個字符串。給出高效率的算法來判斷這本雜志是否能拼成這個字符串。
解析:
對雜志逐個字母進行統計,當種類和個數都達到給定字符串中對應字母的個數時結束,否則判斷不能拼成。即將字符串按字母序hash,其值是在字符串的出現次數;檢索雜志時,每個字母與hash表進行判斷。如果值非0,則減1。
為加速確定是否結束,還可以存儲字符串內各不相同的字母數,每當hash表中某項由1變0,則計數減1,減到0時表示可以拼成。遍歷結束時計數仍為正數則表示不能拼成。
?
3.26
翻轉句子。"My name is Chris" 變成"Chris is name My"。要求最優化時間和空間復雜度。
解析:
常見的序列旋轉問題??梢詤⒖?#xff1a;http://www.cnblogs.com/wuyuegb2312/p/3139925.html#title013
?
3.27
判斷一個鏈表是否有環,并找出環的入口。要求不使用額外存儲空間。
解析:
鏈表找環問題。解法和原理請見:http://www.cnblogs.com/wuyuegb2312/p/3183214.html
?
3.28
給定一個n元數組X,求出n元數組M,滿足M[i] = X[1]*X[2]* ... *X[i-1]*X[i+1]*...*X[n]。不允許用除法,可以用額外的存儲空間。(提示:可以快于O(n^2))
解析:
很常見的題目,《編程之美》2.13"子數組最大乘積"利用到了這個算法。
求M[i]需要左右兩段X[1...i-1]和X[i+1...n]的積,那么分別構造數組保存即可。即L[i] =?X[1]*X[2]* ... *X[i],R[i] =?X[i]*...*X[n]。
構造L時從1開始構造,總時間O(n);R從n開始倒序構造,同樣是O(n)。構造X[i]只需L[i-1]和R[i+1]的乘積即可(為便于計算令L[0]=R[n+1]=1),也只需O(n)。算法整體復雜度是O(n)。
?
3.29
寫出算法來獲取一個頁面上出現頻率最高的英文詞組(僅限兩個單詞組成的詞組,如New York)。使用什么數據結構?要求時間和空間最優。
解析:
在《程序設計實踐》(Practise of Programming)上有一段二元馬爾科夫鏈文本生成器做了類似的工作:讀入所有詞對構成哈希表。解決這個問題也是一樣:從頭至尾讀入每個詞對(一次后移一個單詞)并放入hash表,記錄詞對出現的次數,然后求得值最大的詞對。
本文轉自五岳博客園博客,原文鏈接:http://www.cnblogs.com/wuyuegb2312/p/3260011.html,如需轉載請自行聯系原作者
總結
以上是生活随笔為你收集整理的《算法设计手册》面试题解答 第三章:数据结构的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Kurento架构
- 下一篇: 二进制日志和数据更新的关系