数据结构—树与二叉树
總第119篇
前言
之前談到的線性表、棧和隊列都是一對一的數據結構,但是現實中也存在很多一對多的數據結構,這篇要寫的就是一種一對多的數據結構———樹。全文分為如下幾部分:
樹的一些基本概念
樹的存儲結構
二叉樹
樹與二叉樹相互轉換
樹和森林的遍歷
前文導航:
數據結構—線性表
數據結構-棧和隊列
樹的一些基本概念
樹是n個結點的有限集,n=0時稱為空樹,在任意一顆非空樹中:
有且只有一個特定的稱為根的結點(下圖中的結點A),
當n>1時,其余結點可分為m個互不相交的有限集T1、T2、……、Tm,其中每一個集合本身又是一棵樹,并且稱為根的子樹。
結點:A、B、C、D等都是結點,結點不僅包含數據元素,而且包含指向子樹的分支。比如,結點A不僅包含數據元素A,還包含指向結點B、C、D的指針。
結點的度:結點擁有的子樹個數或者分支的個數。例如,A結點有B、C、D3棵子樹,所以A結點的的度為3。
樹的深度或高度:樹中各結點度的最大值稱為樹的深度。
結點的深度:結點的深度是從根結點到該結點路徑上的結點個數。
結點的高度:從某一結點往下走可能到達的多個葉子結點,對應了多條通往這些葉子節點的路徑,其中最長那條路徑的長度即為該結點在樹中的深度。根節點的高度為樹的高度。
葉子結點:是指結點的度為0的結點,即不指向任何結點的結點。比如結點F、G、M、I、J。
孩子:某一結點指向的結點,比如A結點的孩子就是B、C、D結點。
雙親:與孩子相對應,B、C、D的雙親就是結點A。
兄弟:同一雙親的孩子之間互稱為兄弟。B、C、D結點互稱為兄弟。
堂兄弟:雙親在同一層次的結點互為堂兄弟。G、H、F互為堂兄弟。
祖先:從根結點到具體某節點的路徑上的所有結點,都是這個結點的祖先。結點K的祖先是A、B、E。
子孫:與祖先的概念相對應,以某結點為根的子樹中的所有結點,都是該結點的子孫。結點D的子孫為H、I、J、M。
層次:從根開始,根為第一層,根的孩子為第二層,根的孩子的孩子為第三層,以此類推。
無序樹:如果將樹中結點的各子樹看成是從左至右是有次序的,不能互換的,則稱該樹為有序樹,否則稱為無序樹。
森林:是若干顆互不相交的樹的集合。如果把根節點A去掉,剩下的三棵互不相交樹就是森林。
樹的存儲結構
1.順序存儲結構
樹的順序存儲結構中最簡單直觀的是雙親存儲結構,用一維數組即可實現。雙親結點就是用雙親的信息來存儲數據,比如結點2、3、4的雙親是1,結點5、6、7的雙親是3,結點1是根節點,無雙親,令其等于-1。
2.鏈式存儲結構
樹的鏈式存儲中最常用的兩種結構主要是孩子存儲結構、孩子兄弟存儲結構。
孩子存儲結構就是讓每個結點由一個數據域+若干個指針域組成,指針域的個數等于孩子的個數,讓每個指針指向一個孩子。
孩子兄弟存儲結構是每個結點有兩個指針,一個指針指向該結點的其中一個孩子(長子),另一個指針指向該結點的兄弟。
二叉樹
二叉樹是由n個結點的有限集合,該集合或者為空集,或者由一個根節點和兩顆互不相交的、分別稱為根節點的左子樹和右子樹的二叉樹組成。
二叉樹有如下特征:
每個結點最多只有兩顆子樹,即二叉樹中結點的度最高不能超過2。
子樹的左右順序之分,不能顛倒。
1.特殊二叉樹
滿二叉樹:在一顆二叉樹中,如果所有的分支結點都有左孩子和右孩子結點,并且葉子節點都集中在二叉樹的最下一層,則這樣的二叉樹稱為滿二叉樹。
完全二叉樹:如果對一顆深度為k,有n個結點的二叉樹進行編號后,各結點的編號與深度為k的滿二叉樹中相同位置上的結點的編號均相同,那么這棵二叉樹就是一顆完全二叉樹。
一顆完全二叉樹其實就是由一顆滿二叉樹從右至左從下至上的,挨個刪除結點以后所得到的。
2.二叉樹的存儲結構
2.1順序存儲結構
順序存儲即用一個數組來存儲一顆二叉樹,具體存儲方法為將二叉樹中的結點進行編號,然后按編號依次將結點值存入到一個數組中,即完成了一顆二叉樹的順序存儲。這種存儲結構比較適合存儲完全二叉樹,用于存儲一般的二叉樹會浪費大量的存儲空間。
2.2鏈式存儲結構
順序結構有一定的局限性,不便于存儲任意形態的二叉樹。通過二叉樹的形態,可以發現一個根節點與兩顆子樹有關系,因此設計一個含有一個數據域和兩個指針域的鏈式結點結構,具體如下:
data表示數據域,用于存儲對應的數據元素;lchild和rchild分別表示左指針域和右指針域,分別用于存儲左孩子結點和右孩子結點的位置,如果沒有右孩子結點,則右指針為空。這種存儲結構稱為二叉鏈表存儲結構。定義如下:
typedef?struct?BTNode {char?data;struct?BTNode?*lchild;struct?BTNode?*rchild; }BTNode;3.二叉樹的遍歷
二叉樹的遍歷分為深度優先遍歷和廣度優先遍歷,深度優先遍歷主要就是先序、中序、后序這三種遍歷方式,而層次遍歷屬于廣度優先遍歷。下面幾種遍歷的前提是樹不為空,如果樹為空,則停止遍歷。
3.1先序遍歷
先訪問根節點,然后遍歷左子樹,最后遍歷右子樹。代碼如下:
void?preorder(BTNode?*p) {if(p?!=?NULL){Visit(p);????//Visit為訪問函數,其中包含了對結點p的各種訪問操作preorder(p?->?lchild);????//遍歷左子樹preorder(p?->?rchild);????//遍歷右子樹} }3.2中序遍歷
先遍歷左子樹,然后訪問根節點,最后遍歷右子樹。
void?preorder(BTNode?*p) {if(p?!=?NULL){inorder(p?->?lchild);????//遍歷左子樹Visit(p);????//訪問根結點inorder(p?->?rchild);????//遍歷右子樹} }3.3后序遍歷
先遍歷左子樹,然后遍歷右子樹,最后訪問根結點。
void?preorder(BTNode?*p) {if(p?!=?NULL){posorder(p?->?lchild);????//遍歷左子樹posorder(p?->?rchild);????//遍歷右子樹Visit(p);????//訪問根結點} }3.4層次遍歷
層次遍歷是將樹分為若干層,然后每一層(互為兄弟的結點為一層)每一層去遍歷。如下圖所示:
具體代碼如下:void?level(BTNode?*p) {int?front,rear;BTNode?*que[maxsize];front?=?rear?=?0;BTNode?*q;if(p?!=?NULL){rear?=?(rear?+?1)%maxsize;que[rear]?=?p;while(front?!=?rear){front?=?(front?+?1)%maxsize;q?=?que[front];Visit(q);if(q?->?lchild?!=?NULL){rear?=?(rear?+1)%maxsize;que[rear]?=?q?->?lchild;}if(q?->?rchild?!=?NULL){rear?=?(rear?+?1)%maxsize;que[rear]?=?q?->?rchild;}}} }
樹和森林與二叉樹的相互轉換
樹的鏈式存儲結構和二叉樹存儲結構的指針域表示的意義不同,在二叉樹鏈式存儲結構中,結點的左指針域指向左孩子,右指針域指向右孩子;而在樹的鏈式存儲中,結點的一個指針用來指向一個孩子,另一個指針用來指向自己的兄弟結點,我們把這種方式稱為孩子兄弟存儲法。
關于樹、森林、二叉樹之間的相互轉化,這篇博客寫的很清晰,很詳細,這里直接引用該博主的文章。
博客地址:https://blog.csdn.net/u011240016/article/details/52823925
1.樹轉化為二叉樹
樹變二叉樹的規則:每個結點的左指針指向它的第一個孩子結點。右指針指向它在樹中的相鄰兄弟結點。也即:左孩子右兄弟。根沒有兄弟,所以轉換以后的樹沒有右子樹。
具體操作:
在兄弟之間連線
對每一個結點,只保持它與第一個子結點(長子)的連線,與其他子結點的連線全部抹去。
以樹根為軸心,順時針旋轉45度。
2.二叉樹轉化為樹
二叉樹轉化為樹這個過程是樹轉化為二叉樹的一個逆過程,具體轉化過程如下:
加線。如果某個結點有左孩子,那么把這個左孩子的右孩子,右孩子的右孩子,一直右下去,全部連接到這個結點。這個對應樹變二叉樹算法中的抹掉與長子以外的結點的連線,現在要找回自己的全部兒子。
去線。刪除樹中所有結點與這些右孩子的連線。找回了自己的兒子,不容許別人還和它們有瓜葛。
層次調整。
3.森林轉化為二叉樹
森林轉化為二叉樹的規則是:
先將每一棵樹化為二叉樹
第一棵樹的根作為轉換后的二叉樹的根
第一棵樹的左子樹作為轉換后的二叉樹的根的左子樹
第二棵樹作為二叉樹的右子樹
第三棵樹作為二叉樹右子樹的右子樹
…
這里主要運用的點是:轉換為二叉樹后,這個二叉樹沒有右子樹,因此騰出右手可以接一棵新的樹,因此這樣可以連接很多由樹化來的二叉樹。
對照這個算法去想二叉樹變森林,就很容易明白,先砍掉根和它的左子樹作為一個整體,再對剩下的部分同樣操作。
4.二叉樹轉化為森林
轉化規則如下:
遞歸出口:若二叉樹非空,則二叉樹根及其左子樹作為第一棵樹的二叉樹形式。
遞歸:操作二叉樹的右子樹。即拿掉一棵樹后,再對剩下的二叉樹部分進行同樣的操作,直到無右子樹為止。
砍成一堆二叉樹后,還得注意并沒結束,重點不是二叉樹,而是樹,因此用上面的二叉樹化樹的算法化成森林。
樹和森林的遍歷
1.樹的遍歷
樹的遍歷有兩種方式:先序遍歷和后序遍歷。
先序遍歷是先訪問根結點,再依次訪問根結點的每棵子樹,訪問子樹時仍然遵循先根結點再子樹的原則。
后序遍歷是先依次訪問根結點的每顆子樹,再訪問根結點,訪問子樹時仍然遵循先子樹再根的規則。
2.森林的遍歷
森林的遍歷方式也有兩種,一種是先序遍歷、一種是后序遍歷。
先序遍歷的過程:先訪問森林中第一顆樹的根結點,然后先序遍歷第一顆樹中根結點的子樹,最后先序遍歷森林中除第一顆樹以外的其他樹。
后續遍歷的過程:先后序遍歷第一顆樹中根結點的子樹,然后訪問第一顆樹的根結點,最后后序遍歷森林中除去第一顆樹以外的森林。
總結
以上是生活随笔為你收集整理的数据结构—树与二叉树的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 中国信通院公布 5G 标准必要专利全球最
- 下一篇: 机器学习优化算法(一)