程式設計思想板塊最主要的內容是資料結構經典題目及解答題目所需的程式設計思想,願對您能有所幫助
四、 樹
1)二叉樹的遍歷
- 中序遍歷(前序的棧用來儲存右子樹的地址):
① 沿著根的左孩子,依次入棧,直到左孩子為空
② 棧頂元素出棧並訪問,若其右孩子為空,繼續執行
③ 若其右孩子不空,將右子樹轉執行①
- 後序遍歷:
① 從根節點開始,將其入棧,後沿著左子樹一直往下搜尋到沒有左孩子的結點
② 再對其右結點一直往下入棧,直至前述操作進行不下去
③ 綜上所述,若棧頂元素想出棧訪問,要麼右子數為空,要麼右子數剛被訪問完(此時左子樹早被訪問完了),這樣就保證了正確的訪問順序
④ 實際上,訪問一個結點時,棧中結點恰好是該結點的所有祖先,從棧底到棧頂結點再加上該結點,剛好構成從根節點到該結點的一條路徑。如在”求根到某結點的路徑“、”求兩個結點的最近公共祖先“等演算法設計都可以用這一思想
- 層次遍歷:
① 先將根結點入隊,然後出隊,訪問出隊結點
② 若其有左子樹,則將左子樹根節點入隊,若其有右子樹,則將右子樹根節點入隊
③ 重複①②操作,直至佇列為空
2)二叉線索樹
- 建立雙向線索連結串列:
① 新增一個頭結點,令其lchild域指標指向二叉樹根結點,其rchild域指標指向中序遍歷時訪問的最後一個結點
② 令二叉樹中序序列中的第一個結點的lchild域指標和最後一個結點的rchild域指標均指向頭結點
-
先序線索二叉樹找前驅/後序線索二叉樹找後繼需知雙親時可設定為三叉連結串列(增加一個指標回指向雙親)
-
先序線索化中,注意容易出現愛的魔力轉圈圈的問題(設立兩個指標,當P指標一直線索化左子樹線索至左子樹為空的結點z後此時pre指標指向z結點的雙親結點x,此時應把z結點的前驅線索為x,即把z左指標指向x,當處理完結點後我們依照程式碼會繼續遍歷z的左子樹,但此時z的左指標已經指向了x,故往回走了。處理方法為加一個if(z->ltag==0)這種情況下才可以繼續遍歷左子樹)
-
後序線索二叉樹(其遍歷仍需要棧的支援)找結點後繼分三種情況:
① 若結點為根,則後繼為空
② 若結點是其雙親的右孩子/其雙親的左孩子且其雙親沒有右子樹,則其後繼為雙親
③ 若結點是其雙親的左孩子且其雙親有右子樹,則其後繼為雙親的右子樹上按後序遍歷出的第一個結點
3)二叉排序樹
- 刪除操作:
① 若被刪除的結點是葉子結點,則直接刪除
② 若被刪除結點z有一棵左子樹/右子樹,則讓z的子樹成為z父結點的子樹,替代z的位置
③ 若被刪除結點z有左、右兩棵子樹,則令z的直接後繼(或直接前驅(左子樹中最大的值(最右下的結點,因為中序遍歷二叉排序樹可得到一個遞增的序列,故左子樹中最右下的結點為最小的結點)))替代z,然後從二叉排序樹中刪去這個直接後繼(或直接前驅),這樣就轉換為①/②種情況
4)哈夫曼編碼(尤其重要)
- 構成哈夫曼樹後:
① 編碼需從葉子結點出發走一條從葉子到根的路徑
(1) 因為如果從根開始編碼,由於葉子不唯一,那麼走的時候向左走或者向右走也就無法不確定,處理起來不容易;而如果是從葉子到根,那麼每次如果是左孩子就編碼為0,右孩子就編碼為1,最後逆序輸出即可
② 譯碼需要從根出發走一條從根到葉於的路徑
(1) 此時對於每個結點而言,需要是每個結點的權值(這個是必須知道的,因為在建造哈夫曼樹時總是選取兩個權值小的)、雙親(在編碼時需要根據雙親來判斷是左孩子還是右孩子)、左孩子、右孩子(左右孩子也是必須的,不然咋構成一棵樹,而且還得用於判斷左右孩子…)和結點的資訊(也是必須的)
5)樹堆的刪除和插入
- 刪除:分三種情況:
① 若為葉子結點直接刪除
② 若為分支結點則比較左兒子和右兒子優先順序,把小的那一個旋轉上去把要刪除的結點旋轉下來
③ 重複②直到刪除結點成為葉子結點為止,後直接刪除即可
- 插入:插入結點首先在最右下角,然後依次旋轉上去直到滿足小/大根堆性質
6)答題(畫圖)格式
答題(畫圖)格式 | |
---|---|
二叉線索樹 | (只需把左/右子樹為空的線索畫出來) |
7)二叉樹經典題目的程式設計思想
1. 已知一棵二叉樹按順序儲存結構進行儲存,設計一個演算法,求編號分別為i和j的兩個結點的最近的公共祖先結點的值。
演算法思想:
2. 編寫後序遍歷二叉樹的非遞迴演算法
程式碼:
3. 假設二叉樹採用二叉連結串列儲存結構,設計一一個非遞迴演算法求二叉樹的高度
演算法思想:
採用層次遍歷的演算法,設定變數level記錄當前結點所在的層數,設定變數last指向當前層的最右結點,每次層次遍歷出隊時與last指標比較,若兩者相等,則層數加1,並讓last
指向下一層的最右結點,直到遍歷完成。level的值即為二叉樹的高度(求某層的結點個數、每層的結點個數(具體程式碼在平板瀏覽器收藏)、樹的最大寬度等,都採用與此題類似的思想)
4. 設一棵二叉樹中各結點的值互不相同,其先序遍歷序列和中序遍歷序列分別存於兩個一維陣列Al..n]和11..n]中,試編寫演算法建立該二叉樹的二叉連結串列
演算法思想:
由先序序列和中序序列可以唯一確定一棵二叉樹。 演算法的實現步驟如下
① 根據先序序列確定樹的根結點
② 根據根結點在中序序列中劃分出二叉樹的左、右子樹包含哪些結點,然後根據左、右子樹結點在先序序列中的次序確定子樹的根結點,即回到步驟1)
③ 如此重複上述步驟,直到每棵子樹僅有一一個結點 (該子樹的根結點)為止
5. 二叉樹按二叉連結串列形式儲存,寫-一個判別給定二叉樹是否是完全二叉樹的演算法
演算法思想:
採用層次遍歷演算法,將所有結點加入佇列(包括空結點)。遇到空結點時,檢視其後是否有非空結點。若有,則二叉樹不是完全二叉樹,即判斷是否為右!=NULL&&左==NULL,即判斷下一個結點是否為空
6. 已知二叉樹以二叉連結串列儲存,編寫演算法完成:對於樹中每個元素值為x的結點,刪去以它為根的子樹,並釋放相應的空間
演算法思想:
① 刪除以元素值x為根的子樹,只要能刪除其左、右子樹,就可以釋放值為x的根結點,因此宜採用後序遍歷
② 刪除值為x的結點,意味著應將其父結點的左(右)子女指標置空,用層次遍歷易於找到某結點的父結點。本題要求刪除樹中每個元素值為x的結點的子樹,因此要遍歷完整棵二叉樹
7. 在二叉樹中查詢值為x的結點,試編寫演算法(用C語言)列印值為x的結點的所有祖先,假設值為x的結點不多於一個
演算法思想:
採用非遞迴後序遍歷,最後訪問根結點,訪問到值為x的結點時,棧中所有元素均為該結點的祖先,依次出棧列印即可
8. 設一棵二叉樹的結點結構為(LLINK, INFO, RLINK),ROOT為指向該二叉樹根結點的指標,P和q分別為指向該二叉樹中任意兩個結點的指標,試編寫演算法ANCESTOR (ROOT,P,q,r),找到p和q的最近公共祖先結點r
演算法思想:
後序遍歷最後訪問根結點,即在遞迴演算法中,根是壓在棧底的。本題要找p和q的最近公共
祖先結點r,不失一般性,設p在q的左邊。演算法思想:採用後序非遞迴演算法,棧中存放二叉樹結點的指標,當訪問到某結點時,棧中所有元素均為該結點的祖先。後序遍歷必然先遍歷到結點p,棧中元素均為P的祖先。先將棧複製到另-輔助棧中。繼續遍歷到結點q時,將棧中元素從棧頂開始逐個到輔助棧中去匹配,第一個匹配(即相等)的元素就是結點p和q的最近公共祖先。
9. 假設二叉樹採用二叉連結串列儲存結構,設計一個演算法,求非空二叉樹b的寬度(即具有結點數最多的那一層的結點個數)
演算法思想:
採用層次遍歷的方法求出所有結點的層次,並將所有結點和對應的層次放在一一個佇列中。 然後通過掃描佇列求出各層的結點總數,最大的層結點總數即為二叉樹的寬度
注:本題佇列中的結點,在出隊後仍需要保留在佇列中,以便求二叉樹的寬度,所以設定
的佇列採用非環形佇列,否則在出隊後可能被其他結點覆蓋,無法再求二叉樹的寬度
10. 設有一棵滿二叉樹(所有結點值均不同),已知其先序序列為pre,設計一個演算法求其後序序列post
演算法思想:
11. 試設計判斷兩棵二叉樹是否相似的演算法。所謂二叉樹T和T2相似,指的是T和T2都是空的二叉樹或都只有一一個根結點;或T的左子樹和T2的左子樹是相似的,且T的右子樹和T2的右子樹是相似的
演算法思想:
12. 寫出在中序線索二叉樹裡查詢指定結點在後序的前驅結點的演算法
演算法思想:
在後序序列中,若結點p有右子女,則右子女是其前驅,若無右子女而有左子女,則左子女是其前驅。若結點p左、右子女均無,設其中序左線索指向某祖先結點f (p是f右子樹中按中序遍歷的第一個結點),若f有左子女,則其左子女是結點P在後序下的前驅;若f無左子女,則順其前驅找雙親的雙親,一直找到雙親有左子女(這時左子女是P的前驅)。還有一種情況,若p是中序遍歷的第一個結點,則結點p在中序和後序下均無前驅
13. 請設計-一個演算法,將給定的表示式樹(二叉樹)轉換為等價的中綴表示式(通過括號反映操作符的計算次序)並輸出
演算法思想:
表示式樹的中序序列加上必要的括號即為等價的中綴表示式。可以基於二叉樹的中序遍歷策
略得到所需的表示式。表示式樹中分支結點所對應的子表示式的計算次序,由該分支結點所處的位置決定。為得到正確的中綴表示式,.需要在生成遍歷序列的同時,在適當位置增加必要的括號。顯然,表示式的最外層(對應根結點)和運算元(對應葉結點)不需要新增括號。
故將二叉樹的中序遍歷遞迴演算法稍加改造即可得本題的答案。除根結點和葉結點外,遍歷到其他結點時在遍歷其左子樹之前加上左括號,遍歷完右子樹後加上右括號即可
14. 已知一棵樹的層次序列及每個結點的度,編寫演算法構造此樹的孩子-兄弟連結串列
演算法思想:
本題與樹的層次序列有關。可設立一個輔助陣列pointer[]儲存新建樹的各結點的地址,再根據層次序列與每個結點的度,逐個連結結點
15. 試編寫一個演算法,判斷給定的二叉樹是否是二叉排序樹(經典)
演算法思想:
對二叉排序樹來說,其中序遍歷序列為-一個遞增有序序列。因此,對給定的二叉樹進行中序諞歷.若始終能保持前一一個值比後一個值小,則說明該二叉樹是一棵二叉排序樹
16. 利用二叉樹遍歷的思想編寫一個判斷二叉樹是否是平衡二叉樹的演算法(經典)
演算法思想:
設定二叉樹的平衡標記balance,標記返回二叉樹bt是否為平衡二叉樹,若為平衡二叉樹,
則返回1,否則返回0; h為二叉樹bt的高度。採用後序遍歷的遞迴演算法:
① 若bt為空,則高度為0, balance=l
② 若bt僅有根結點,則高度為1, balance=1.
③ 否則,對bt的左、右子樹執行遞迴運算,返回左、右子樹的高度和平衡標記,bt的高度為最高子樹的高度加1。若左、右子樹的高度差大於1,則balance=0;若左、右子
樹的高度差小於等於1,且左、右子樹都平衡時,balance=1, 否則balance=0
17. 設計一個演算法,從大到小輸出二叉排序樹中所有值不小於k的關鍵字(記程式碼)
演算法思想:
由二叉排序樹的性質可知,右子樹中所有的結點值均大於根結點值,左子樹中所有的結點值
均小於根結點值。為了從大到小輸出,先遍歷右子樹,再訪問根結點,後遍歷左子樹
18. 編寫一個遞迴演算法,在一棵有n個結點的、隨機建立起來的二叉排序樹上查詢第k(1≤k≤n)小的元案,並返回指向該結點的指標。要求演算法的平均時間複雜度為O(log2n).二叉排序樹的每個結點中除data, lchild, rchild等資料成員外,增加一個count成員,儲存以該結點為根的子樹上的結點個數
演算法思想:
設二叉排序樹的根結點為*t,根據結點儲存的資訊,有以下幾種情況:
① t->lchild 為空時,情況如下:
(1) 若t->rchild非空且k==1, 則*t即為第k小的元素,查詢成功
(2) 若t->rchild非空且k!=1,則第k小的元素必在*t的右子樹
② 若t->lchild非空時,情況如下:
(1) t->lchild->count==k-1, *t即為第k小的元素,查詢成功
(2) t->lchild->count>k-1, 第k小的元素必在t的左子樹,繼續到t的左子樹中查詢
(3) t->lchild->count<k-1,第k小的元素必在右子樹,繼續搜尋右子樹,尋找第k-(t->1child-> count+1) 小的元素
對左右子樹的搜尋採用相同的規則,遞迴實現
19. 設有6個有序表A,B,C,D,E,F,分別含有10, 35, 40, 50, 60和200個資料元素,各表中的元素按升序排列。要求通過5次兩兩合併,將6個表最終合併為1個升序表,並使最壞情況下比較的總次數達到最小。根據你的合併過程,描述n (n≥2)個不等長升序表的合併策咯,並說明理由
各表的合併策略是:
對多個有序表進行兩兩合併時,若表長不同,則最壞情況下總的比較次數依賴於表的合併次序。可以藉助於哈夫曼樹的構造思想,依次選擇最短的兩個表進行合併,此時可以獲得最壞情況下的最佳合併效率
20. 若任意一個字元的編碼都不是其他字元編碼的字首,則稱這種編碼具有字首特性。現有某字符集(字元個數>=2)的不等長編碼,每個字元的編碼均為二進位制的0、1 序列,最長為L位,且具有字首特性。請回答下列問題:
1)基於你所設計的資料結構,簡述從0/1串到字串的譯碼過程
2)簡述判定某字符集的不等長編碼是否具有字首特性的過程
答:
1)從左至右依次掃描0/1串中的各位。從根開始,根據串中當前位沿當前結點的左子指標或
右子指標下移,直到移動到葉結點時為止。輸出葉結點中儲存的字元。然後從根開始重複這個過程,直到掃描到0/1串結束,譯碼完成
- 二叉樹既可用於儲存各字元的編碼,又可用於檢測編碼是否具有字首特性。判定編碼是
否具有字首特性的過程,也是構建二叉樹的過程。初始時,二叉樹中僅含有根結點,其左子指標和右子指標均為空。
依次讀入每個編碼C,建立/尋找從根開始對應於該編碼的一條路徑,過程如下:
① 對每個編碼,從左至右掃描C的各位,根據C的當前位(0或1)沿結點的指標(左子指標或右子指標)向下移動。當遇到空指標時,建立新結點,讓空指標指向該新結點並繼續移動。沿指標移動的過程中,可能遇到三種情況:
(1) 若遇到了葉結點(非根),則表明不具有字首特性,返回
(2) 若在處理C的所有位的過程中,均沒有建立新結點,則表明不具有字首特性,返回
(3) 若在處理C的最後一個編碼位時建立了新結點,則繼續驗證下一個編碼
(4) 若所有編碼均通過驗證,則編碼具有字首特性
8)樹
1.
2. (學會這樣推公式以及如何書寫步驟)
9)哈弗曼樹
- 設哈夫曼樹中共有 99 個結點,則該樹中有 ____ 個葉子結點;若採用二叉連結串列作為儲存結構,則該樹中有___個空指標域
① 對於不同表示法下的空指標域個數:孩子兄弟表示法和雙孩子表示法其實空指標域數都是葉子結點數的兩倍,但是不同的是雙孩子表示法的空指標域都是葉子結點的,而孩子兄弟表示法的空指標域一部分是葉子結點的,另一部分是其他右孩子結點的空指標域
② 本題解釋:哈夫曼樹是指帶權路徑最小的二叉樹 它的非葉子結點只是權重的值 並無其他 它真正有儲存意義的應該只是那些個葉子結點 所以哈夫曼樹轉二叉連結串列的話應該只是用葉子結點來構建二叉連結串列 所以存在51個空指標域