数据结构 非线性结构 树 介绍及存储方法
?????????? 所謂樹, 其實跟鏈表有類似的地方,? 就是都是由節(jié)點和指針構(gòu)成的數(shù)據(jù)結(jié)構(gòu).
?????????? 在鏈表中,? 每1個節(jié)點(尾節(jié)點除外)只有1個指針指向下1個節(jié)點. 所以鏈表各個節(jié)點可以由一條線鏈接起來, 就是一種線性結(jié)構(gòu).
?????????? 而在樹中, 每1個節(jié)點可以有1個或多個指針指向下1個節(jié)點,? 如下圖:
????????????? 所以樹是一種非線性結(jié)構(gòu), 對于這種結(jié)構(gòu), 有很多算法都要依賴遞歸來實現(xiàn), 否則就相當復雜, 所以很多教程都把遞歸作為1個專題放在樹這章之前講解, 就是這個原因.
????????????? 其實樹也可以利用連續(xù)存儲來實現(xiàn), 也就是說節(jié)點不需具有指向別的節(jié)點的指針, 但是這種樹必須是完全二叉樹。 下面會詳細提到。
1. 樹的定義
???????? 樹的定義有兩種, 一種是專業(yè)的定義, 跟遞歸有關(guān).
???????? 專業(yè)定義:
???????????????? 1. 只有1個稱為根的節(jié)點.
???????????????? 2. 有若干個互不相交的子樹, 這些子樹本身也是樹.
???????????????? 解析下,子樹本身也是樹, 就如上上圖的樹A, 根就是是A了, 它具有2個子樹, 分別是B1 和 B2, 而B1和B2也是1棵樹啊, 所以, 這個定義跟遞歸有關(guān).
?????????? 通俗定義:
???????????????? 1. 樹是有節(jié)點和邊組成.(為什么不用指針這個字眼,用邊?因為樹是可以利用連續(xù)存儲來實現(xiàn)的,下面會提到)
???????????????? 2. 每1個節(jié)點只有1個父節(jié)點, 但是可以有多個子節(jié)點.
???????????????? 3. 但是有1個節(jié)點例外, 該節(jié)點沒有父節(jié)點, 此節(jié)點就是根節(jié)點.
2. 樹的一些專業(yè)術(shù)語.
??????????? 節(jié)點:? ? ? ? ? ? ? ? ? ? 構(gòu)成樹的元素, 通常節(jié)點里會包含指向其他節(jié)點的指針.? 在C語言中, 節(jié)點通常是由結(jié)構(gòu)體來表示的.
???????????? 父節(jié)點:
??????????????????? 節(jié)點的上1級節(jié)點,? 通常1個節(jié)點有且只有1個父節(jié)點, 根節(jié)點例外, 根節(jié)點沒有父節(jié)點.
???????????? 子節(jié)點:
??????????????????? 節(jié)點的下一級節(jié)點, 1個節(jié)點可以有1個或多個子節(jié)點, 也可以沒有子節(jié)點.
????????????? 子孫:
??????????????????? 1個節(jié)點的子節(jié)點或者子節(jié)點的子節(jié)點....,等所有下級節(jié)點都可以成為這個節(jié)點的子孫,? 1棵樹的所有節(jié)點都是根節(jié)點的子孫.?? 上圖C2 是B1的子孫, 但不是B2的子孫.
????????????? 兄弟:
??????????????????? 擁有同1個父節(jié)點的若干個節(jié)點成為兄弟, 上圖B1 就是 B2的兄弟
????????????? 堂兄弟:
???????????????????? 不屬于同1個父節(jié)點, 但是它們的父節(jié)點屬與同1個父節(jié)點的若干個節(jié)點就是堂兄弟. 上圖C1 就是 C3的堂兄弟.
????????????? 深度:
???????????????????? 1棵樹從根節(jié)點到最底層節(jié)點的層數(shù)稱之為深度.?? 根節(jié)點是第1層.
?????????????? 葉子節(jié)點:
????????????????????? 沒有子節(jié)點的節(jié)點, 因為葉子不同于樹干, 不能再分叉了.? 當1個棵樹只有1個節(jié)點, 那么這個節(jié)點是根節(jié)點. 也是葉子節(jié)點.
?????????????? 非終端節(jié)點:
?????????????????????? 實際就是非葉子節(jié)點,? 就是具有子節(jié)點的節(jié)點.
??????????????? 度:
??????????????????????? 1個節(jié)點的子節(jié)點個數(shù)成為這個節(jié)點的度
??????????????????????? 1個樹中擁有最大度的節(jié)點的度就是這個樹的度.
???????????????
? ? ? ? ? ? ?? 森林:
? ? ? ? ? ? ? ? ? ? ? ? ? n個互不相交的樹的集合, 成為森林. ????????
3. 樹的分類
??????????????? 一般的樹:
????????????????????????? 任意1個節(jié)點的子節(jié)點的個數(shù)都不受限制的樹.
??????????????? 二叉樹: (binary tree)
?????????????????????????? 任意1個節(jié)點的個數(shù)最多有連個, 且子節(jié)點的位置不能被更改, 這種樹就是二叉樹.
?
??????????????????????????? 第一句容易理解, 關(guān)鍵是第二句, 子節(jié)點的位置不能更改??? 其實意思就是二叉樹是有序的, 它的節(jié)點最多只有2個子節(jié)點, 分別是左子節(jié)點和右子節(jié)點,? 它們的順序不能被更改. 也不能更改節(jié)點的父子點(移動子樹),我們稱為這種樹是有序的樹
??????????????????????????? 一般的樹沒有這種要求, 可以是有序的樹, 也可以是無序的樹(子節(jié)點位置可更改),??? 但是2叉樹必須是有序的樹.
??????????????
???????????????????????????? 二叉樹還有分類
???????????????
???????????????????????????滿二叉樹: (full binary tree)
????????????????????????????????????? 在不增加層數(shù)的情況下,? 無法再多添加1個節(jié)點的二叉樹, 就是滿二叉樹. 例如上圖那個樹就是非滿2叉樹, 因為還可以添加1個C4節(jié)點在B2節(jié)點下.
????????????????????????
??????????????????????????????????????? 添加了C4節(jié)點后, 就是1個滿二叉樹, 如下圖:
???????????????????????????????????????? 假如1個滿二叉樹的層數(shù)是k, 那么它的節(jié)點個數(shù)必定是 (2^k-1).
???????????????????????????????????????? 這里順便貼個等比數(shù)列求和的推導公式:
(1)Sn=a1+a2+a3+...+an(公比為q) (2)q*Sn=a1*q+a2*q+a3*q+...+an*q =a2+a3+a4+...+a(n+1) (3)Sn-q*Sn=a1-a(n+1) (4)(1-q)Sn=a1-a1*q^n (5)Sn=(a1-a1*q^n)/(1-q) (6)Sn=(a1-an*q)/(1-q) (7)Sn=a1(1-q^n)/(1-q)
????????????????????? 完全二叉樹: (Complete binary tree)
???????????????????????????????? 完全二叉樹就厲害了,之前提到滿二叉樹的目的就是為了引出完全二叉樹. 滿二叉樹是完全二叉樹的一種。
???????????????????????????????? 若設(shè)二叉樹的深度為h,除第 h 層外,其它各層 (1~h-1) 的結(jié)點數(shù)都達到最大個數(shù),第 h 層所有的結(jié)點都連續(xù)集中在最左邊,這就是完全二叉樹。
???????????????????????????????? 又有1個通俗定義: 當1棵層數(shù)為k滿二叉樹, 從它的最底層的最右邊的節(jié)點開始,連續(xù)向左刪除n個節(jié)點(2k >= n >= 0), 得到的樹就是1棵完全二叉樹.
???????????????????????????????? 如下圖, 左邊兩個是完全二叉樹, 而右邊那個不是,因為最下層的節(jié)點不是向右連續(xù)的啊!
??????????????????????????? 這里補充一點:完全二叉樹是效率很高的數(shù)據(jù)結(jié)構(gòu),堆是一種完全二叉樹,所以效率極高,像十分常用的排序算法、Dijkstra算法、Prim算法等都要用堆才能優(yōu)化,幾乎每次都要考到的二叉排序樹的效率也要借助平衡性來提高,而平衡性基于完全二叉樹。
??????????????????????????? 這里為什么要特別提到完全二叉樹呢?? 因為完全二叉樹是一種重要的數(shù)據(jù)結(jié)構(gòu), 跟棧,對列一樣, 二叉樹也可以用鏈式和數(shù)組內(nèi)核來實現(xiàn), 但是因為數(shù)組必須是連續(xù)存儲的, 所以數(shù)組實現(xiàn)的二叉樹必須是完全二叉樹。
??????????????????????????? 完全二叉樹的性質(zhì):
?????????????????????????????????? 1. 如果知道完全二叉樹的節(jié)點個數(shù), 就必然可以退出完全二叉樹的形狀(包括層數(shù), 最后一層的節(jié)點排布)。? 也就是說兩個節(jié)點樹相同的完全二叉樹的形狀是一樣的。
?????????????????????????????????? 2. 根據(jù)第1個性質(zhì), 只需要知道1個確定節(jié)點個數(shù)的完全二叉樹的節(jié)點編號, 就可以推出這個節(jié)點的父節(jié)點和字節(jié)點! 這個是1個非常重要的優(yōu)點!
???????????????????????????
3. 樹的存儲(內(nèi)存)
????? ??? 3.1 二叉樹的存儲:
??????????????? 3.1.1. 連續(xù)存儲 :
?????????????????????? 上面提過了, 要利用連續(xù)存儲(數(shù)組)來實現(xiàn)的二叉樹必須是完全二叉樹, 因為它們的節(jié)點必須是連續(xù)的啊! 如果要利用連續(xù)存儲來存儲普通的二叉樹, 就必須把這個二叉樹轉(zhuǎn)化為完全二叉樹。
?????????????????????? 關(guān)鍵是如何轉(zhuǎn)化? 很簡單, 就是添加必要的空節(jié)點把1個非完全二叉樹補成1個完全二叉樹, 如下圖:
????????????????????? 藍色的樹就是1個普通的2叉樹,? 白色的節(jié)點就是必要的不存放實際數(shù)據(jù)的節(jié)點, 添加上去組成1個完全二叉樹, 就可以利用連續(xù)存儲了。
????????????????????? 至于要轉(zhuǎn)成完全二叉樹的原因:
????????????????????? 假如只按一定的順序只存儲有效的節(jié)點,? 內(nèi)存物理上是1個線性結(jié)構(gòu), 如果把非線性的樹存放在線性的內(nèi)存中,存放后就變成線性結(jié)構(gòu)了,例如上圖中的藍色樹在內(nèi)存里存放: A,B1,C1,C2,D3,D4? 的確是能存放, 但是不能將存放的數(shù)據(jù)還原成原來的樹啊! 所以就必須添加一些無效的節(jié)點令它轉(zhuǎn)化成1個完全二叉樹!
????????????????????? 缺點:
??????????????????????? 其實也可以看出, 如過利用連續(xù)存儲來存放普通的二叉樹, 首先需要一定量的連續(xù)內(nèi)存空間,而且必然會造成1些空間浪費。 而且普通二叉樹的層數(shù)越大, 耗費的內(nèi)存越大!
??????????????????? ? 優(yōu)點:
??????????????????????? 就是上面完全二叉樹的第2個性質(zhì),? 只需要知道1個節(jié)點在數(shù)組的位置, 就可以推出它的父節(jié)點和子節(jié)點, 時間復雜度是0啊!
????????????? 3.1.2. 鏈式存儲 :
???????????????????? 這個跟鏈表有點類似。
???????????????????? 我們會把每1個節(jié)點分成4個域, 分別是數(shù)據(jù)域, 父節(jié)點指針域, 左子節(jié)點指針域, 右字節(jié)域指針域。(或者不包含父節(jié)點指針域,則只有3個域)
???????????????????? 如下圖:
???????????????????? 可以見到,這種存儲方式浪費的空間很少, 無非就是一些子節(jié)點的空指針, 但是這些空指針個數(shù)仍是線性增長的, 但是我們認為浪費的空間已經(jīng)很少啦, 比起連續(xù)存儲來講, 因為鏈式存儲不需存儲多余的節(jié)點啊。
????????????????????
??????????????????
????????
????? 3.2 一般樹的存儲:
????????????? 一般樹的特點是節(jié)點的度(子節(jié)點個數(shù))無限制, 用上面連續(xù)存儲根本沒可能, 如果用鏈式存儲也很困難, 因為無法決定子節(jié)點的子節(jié)點指針域的個數(shù)啊!
??????????? 3.2.1 雙親表示法(數(shù)組):
??????????????????? 雙親表示法實際上也是利用連續(xù)存儲的, 只不過數(shù)組內(nèi)的元素必須分為兩個域, 1個是數(shù)據(jù)域, 另1個是父節(jié)點的下標(注意不是指針啊). 根節(jié)點的父節(jié)點下標是·-1.
??????????????????? 雙親表示法對于元素(節(jié)點)在數(shù)組內(nèi)的順序無要求。
? ? ? ? ? ? ? ? ? ? 如下圖:
???????????????? 如上圖右邊的數(shù)組, 基本上可以把1棵樹的結(jié)構(gòu)表示出來了, 但是有個問題, 就是無法通過數(shù)組得到同1個父子點兄弟之間的順序, 例如無法知道C1,C2,C3這個3兄弟在樹中的順序, 所以雙親表示法只適合表示無序的樹。
? ? ? ? ? ? ? ?? 優(yōu)點:
? ? ? ? ? ? ? ? ? ?? 幾乎不浪費內(nèi)存空間, 查找父節(jié)點很容易。
? ? ? ? ? ? ? ?? 缺點:
? ? ? ? ? ? ? ? ? ?? 不能表示有序的樹, 查找子節(jié)點相對困難, 查找任何1個節(jié)點的子節(jié)點都必須遍歷整個數(shù)組。 不能表示有序的樹。
? ? ? ? ??
?????????? 3.2.2 孩子表示法(數(shù)組):
???????????????? 孩子表示法也是利用連續(xù)存儲來實現(xiàn)的, 它的節(jié)點同樣具有兩個域, 1個是數(shù)據(jù)域, 另1個是1個子節(jié)點下標鏈表的頭節(jié)點地址。
???????????????? 關(guān)鍵就是這個鏈表了, 鏈表的節(jié)點有兩個域, 1個是int類型,用于表示節(jié)點下標, 另1個是指針。
???????????????? 如下圖:
??????????? 可以見到, 因為可以利用子節(jié)點下標鏈表的順序來表示子節(jié)點兄弟的順序, 所以這種表示法是能表示有序樹的。
??????????? 優(yōu)點:
????????????????? 查找子節(jié)點算法很方便, 可以表示有序樹
??????????? 缺點:
???????????????? 查找父節(jié)點算法很困難, 必須遍歷每1個節(jié)點的子節(jié)點下標鏈表, 如上圖, 甚至判斷哪個是根節(jié)點都需要遍歷啊(O(n^2))!?
??????????
??????? 3.2.3 雙親孩子表示法(數(shù)組):
???????? 由上面的例子可以看出, 其實雙親表示法和孩子表示法的優(yōu)缺點是互補的, 所以把它們合拼在一起就是雙親孩子表示法了。
???????? 所謂雙親孩子法就是指表示樹的數(shù)組內(nèi)的節(jié)點有3個域, 數(shù)組域, 父節(jié)點下標域,及子節(jié)點下標鏈表域。
???????? 如下圖:
????????
???????? 優(yōu)點:
????????????? 具有雙親表示法和孩子表示法的所有優(yōu)點, 關(guān)鍵是方便地查找父節(jié)點和子節(jié)點。
???????? 缺點:
????????????? 占用相對更多的內(nèi)存空間。
???????
?????? 3.2.4 二叉樹表示法 :
??????? 這個的原理就是把一般樹轉(zhuǎn)化為二叉樹, 然后再利用二叉樹的存儲方法來存儲。
??????? 關(guān)鍵是如何把1個一般樹轉(zhuǎn)化為二叉樹?? 一句話:? 每個子節(jié)點的左子節(jié)點指向它原來的第1個子節(jié)點,?右子節(jié)點指向它原來的下1個兄弟。
??????? 所以這個方法又叫孩子兄弟鏈表表示法
??????? 如下圖:
??????? 可以看到, 轉(zhuǎn)換后層數(shù)增加了, 而且某個子樹的度越大, 轉(zhuǎn)化后所需的層數(shù)越多, 畢竟是利用層數(shù)來表示兄弟的個數(shù)啊。
??????? 而且有個特點, 就是轉(zhuǎn)化后的二叉樹, 對于根節(jié)點來講, 是沒有右字數(shù)的, 因為根節(jié)點沒有兄弟嘛。
??????? 優(yōu)點:
???????????? 能使用一些二叉樹的算法。
??????? 缺點:
???????????? 查找真正的父節(jié)點和子節(jié)點的算法有點復雜,? 例如上圖轉(zhuǎn)化后的二叉樹, 如果求C3的父節(jié)點, 則必須反方向先求出C2和C1..
???????
???????? 3.3 森林的存儲:
? ? ? ? ??? 所謂森林上面也提到過了, 就是互不相交(沒有公共節(jié)點)的n棵樹的集合。
? ? ? ? ? ? 例如下圖中的三棵樹就可以組成1個森林。
? ? ????????
?????????? 一般來講,
?????????? 首先: 我們會先把森林的所有樹轉(zhuǎn)化為二叉樹(孩子兄弟鏈表表示法)
?????????? 然后: 把轉(zhuǎn)化后的二叉樹合成1個新的二叉樹, 怎么合成? 就是把各個二叉樹視為兄弟, 然后新增1個根節(jié)點作為它們的父子點。
?????????? 其實不難理解的啦, 轉(zhuǎn)化后如圖:
??????? 然后就按照二叉樹的方法存儲就ok了。
總結(jié)
以上是生活随笔為你收集整理的数据结构 非线性结构 树 介绍及存储方法的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Oracle 系统改变号SCN详解
- 下一篇: 数据结构 二叉树的遍历