數據結構(十九) -- C語言版 -- 樹 - 樹、森林、二叉樹的江湖愛恨情仇、相互轉換

零、讀前說明

  • 本文中所有設計的代碼均通過測試,並且在功能性方面均實現應有的功能。
  • 設計的代碼並非全部公開,部分無關緊要代碼並沒有貼出來。
  • 如果你也對此感興趣、也想測試源碼的話,可以私聊我,非常歡迎一起探討學習。
  • 由於時間、水平、精力有限,文中難免會出現不準確、甚至錯誤的地方,也很歡迎大佬看見的話批評指正。
  • 嘻嘻。。。。 。。。。。。。。收!
摘要
  

  在滿足樹的條件下,樹可以是任意形狀,一個節點可以有任意多個孩子。在前面的存儲結構中,提到了樹的孩子兄弟方法可以將一個樹用二叉鏈表進行存儲,所以藉助二叉鏈表,樹和二叉樹可以進行相互轉換,從物理結構來看,他們的二叉鏈表是相同的。因此,只要我們設定一定的規則,用二叉樹來表示樹甚至森林是可以,森林與二叉樹也可以相互轉換。(摘自《大話數據結構》 P196

  關於樹、森林、二叉樹等基礎知識等,可以查看博文:數據結構(十) – C語言版 – 樹 - 基礎知識。

一、樹轉換爲二叉樹

  在前面的博文 數據結構(十) – C語言版 – 樹 - 基礎知識 裏面已經說過了樹的 左孩子-右兄弟表示法,他就可以將一個轉化爲一個類二叉樹(沒有區分左右孩子節點)。具體轉換的方法可以總結爲:

  1、加入連接線 :在樹中所有的兄弟節點(同一雙親下的同一層的節點(孩子之間互稱兄弟))之間加入一條連接線
  2、刪除連接線 :對樹中每一個節點,只保留他與最左邊第一個孩子節點的連接點,刪掉他與其他孩子節點的連接線
  3、調整層次順序:以樹的根節點爲根節點,依次按照連接線的形式將節點調整到層次分明,左右清楚

  注意第一個孩子節點是二叉樹根節點的左孩子,兄弟節點轉換過來的孩子節點是右孩子

  舉個栗子:用下圖中表示的樹進行舉例說明,其中因爲是一個簡單的樹的結構,在一個節點下面可能會有任意多個節點,所以也就是沒有左右節點的說法了, 這裏我們使用左右的順序關係,從左依次到右來表示。
  
在這裏插入圖片描述

圖1.1 樹的簡單表示形式

  
  從上面的圖片在中,我們可以看到,節點 B、C、D 爲一組兄弟節點,節點 E、F 爲一組兄弟節點、節點 H、I、J 爲一組兄弟節點,那麼首先將各個兄弟節點連接起來,下圖左側表示( 紅色 線條)。

  然後將除了長子(最左邊節點)節點之間的連接線刪除(下圖中右側用 灰色虛線 表示)。

在這裏插入圖片描述

圖1.2 兄弟加線、刪線的圖例

  
  然後進行節點層次的調整,圖中節點 B 爲節點 A長子,節點 E 爲節點 B長子,節點 H 爲節點 D長子,那麼在調整時,將長子調整爲節點的左孩子,然後將兄弟節點調整爲其右孩子。所以,節點 B、E、H 分別爲其雙親節點的左孩子其他兄弟節點爲右孩子。並且節點 H、I、J兄弟節點,在調整過程中,將節點 I 調整爲節點 H右孩子,節點 GI右孩子,調整效果如下圖所示。
  
在這裏插入圖片描述

圖1.3 層次調整示意圖

  

二、二叉樹轉換爲樹

  這個相對比較簡單了,其實就是上面的一個逆過程,但是實際的操作過程還是先加線、再刪線、後調整次序的過程,只是操作的對象略有不同。那麼其轉換的過程可以描述爲:

  1、連接節點:如果節點存在左孩子,那麼將這個左孩子的右孩子節點、右孩子的右孩子節點,右孩子的右孩子的右孩子節點…都和此節點連接起來
  2、刪除連接:刪除原本二叉樹中的所有節點與其右孩子節點的連接線
  3、調整層次:依次按照連接線的形式將節點調整到層次分明,左右清楚

  過程可以用下面的圖片來一次表示。

  首先如下圖左邊的二叉樹爲例,我們分別將右孩子、右孩子的右孩子…都與爺爺節點連接起來(下圖中右邊圖例中 紅色 線條表示)。

在這裏插入圖片描述

圖2.1 節點連接示意圖

  然後將二叉樹中所有節點和其右孩子的連接線刪除,如下圖所示(灰色虛線表示)。

在這裏插入圖片描述

圖2.2 刪除節點連接示意圖

  
  最後調整完層次後,可以得到和上圖1.1中一模一樣的樹。詳情 點我可以查看圖1.1

  但是有一個問題:上面的二叉樹的根節點是沒有右孩子的,所有可以將二叉樹轉換成樹。那麼如果根節點存在右孩子,那麼應該怎麼轉換呢?看看下面。

三、二叉樹轉換爲森林

  首先來看看什麼是森林?

  森林:指的是多個不相交的樹的集合

  根據前面的標識。可以將一個二叉樹轉換爲樹,那麼應該怎麼轉換爲森林呢?

  判斷一個二叉樹能夠轉換成樹還是森林,需要判斷根節點是否存在右孩子,存在右孩子就是森林,不存在右孩子就是樹。

  所以,將一個二叉樹轉換爲森林就可以總結爲:

  1、從根節點開始,如果存在右孩子,則將其與右孩子的連接線刪除,然後在判斷分離後的二叉樹,如果存在右孩子則刪除連接線…直到所有的與右孩子的連接線刪除爲止。

  2、將每一個分離出來的二叉樹轉換爲樹即可。具體可以 點我可以查看詳細步驟

  過程可以用下面的圖片來一次表示。

  首先將右孩子的連接線斷開(下圖中虛線表示)。

在這裏插入圖片描述

圖3.1 斷開右孩子連線

  
  然後將二叉樹轉換爲樹即可,如下圖表示。
  
在這裏插入圖片描述

圖3.1 斷開右孩子連線

  

四、森林轉換爲二叉樹

  在森林中,有若干個獨立的樹組成,下圖所示。
  
在這裏插入圖片描述

圖4.1 層次調整示意圖

  
  那麼將一個森林轉換成一個二叉樹,那麼可以現需要將每個獨立的樹先轉換成二叉樹,然後將多個二叉樹在組合成二叉樹接口。所以,具體的方法可以描述爲:

  1、將樹中每一個樹轉換成二叉樹,轉換方式與前面的一致。
  2、第一個二叉樹不動,從第二個二叉樹開始,依次把後面的一個二叉樹的根節點作爲前一個二叉樹的根節點的右孩子鏈接起來(也就是將各個樹的根節點依次從左到右鏈接)。直到所有二叉樹鏈接完成。

  所以,具體的步驟可以用下面來表示。

  首先將每一個樹轉換成二叉樹,還是使用左孩子有兄弟的方式來轉換,轉換完成如下圖所示。

圖4.2 每個樹轉換爲二叉樹

  然後依次拼接二叉樹,作爲根節點的右孩子,其實也就是將各個樹的根節點從左到右依次鏈接起來,然後稍微調整層次即可完成,所以如下圖所示。

圖4.3 依次鏈接各個樹

  

五、樹與森林的遍歷

  樹和森林的遍歷是比較複雜和困難的,所以前面才大量的說明二叉樹的相關的操作。

5.1、樹的遍歷

  樹的遍歷分爲兩種方式:

    一種是先根遍歷:先訪問樹的根節點,然後依次在先根遍歷每一個子樹,同樣也是一個遞歸的定義
    一種是後根遍歷:先依次遍歷每一個子樹,然後依在遍歷根節點,同樣也是遞歸的定義。

  先根遍歷的順序如下圖所示。
在這裏插入圖片描述

圖5.1 先根遍歷示意圖

  
  按照圖中 p1、p2、p3、p4、p5、… 依次進行遍歷,其中節點 B 爲節點 A孩子節點,但同時也是節點E 和節點F雙親結點。所以可以得到其遍歷結果爲:

A->B->E->F->C->G->D->H->I->J

  
  後根遍歷的順序和先跟正好相反,如下圖所示。

在這裏插入圖片描述

圖5.2 後根遍歷示意圖

  
  按照圖中 p1、p2、p3、p4、p5、… 依次進行遍歷,所以可以得到其遍歷結果爲:

E->F->B->G->C->H->I->J->D->A

5.2、森林的遍歷

  森林的遍歷也是有兩種遍歷順序,分別爲:前序遍歷、後序遍歷。

  1、先將森林中的每一個樹都按照先根遍歷、後根遍歷的順序遍歷
  2、然後依次從左到右將每一個樹的遍歷結果合併到一起即爲森林的遍歷。
  3、樹的先根遍歷對應於森林的先序遍歷,樹的後根遍歷對應森林的後序遍歷

  總結:

    森林,樹的前序(根)遍歷和二叉樹的前序遍歷的結果相同
    森林、樹的後根(序)遍歷和二叉樹的中序遍歷的結果相同

  所以,當以二叉鏈表作爲樹的存儲結構時,樹的先根遍歷和後根遍歷完全可以借用二叉樹的先序遍歷和中序遍歷的算法來實現,這其實也證實,我們找到了對樹和森林這種複雜問題的簡單解決辦法。(摘自《大話數據結構》P200)

  
  好啦,廢話不多說,總結寫作不易,如果你喜歡這篇文章或者對你有用,請動動你發財的小手手幫忙點個贊,當然關注一波那就更好了,好啦,就到這兒了,麼麼噠(*  ̄3)(ε ̄ *)。

上一篇:數據結構(十八) – C語言版 – 樹 - 二叉樹的線索化及遍歷 – 線索化後的直接前驅、後繼獲取
下一篇:數據結構(二十) – C語言版 – 樹 - 霍夫曼樹(哈夫曼樹、赫夫曼樹、最優二叉樹)、霍夫曼編碼

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章