1. 程式人生 > >資料結構看書筆記(六)--樹的定義、抽象資料型別、儲存結構

資料結構看書筆記(六)--樹的定義、抽象資料型別、儲存結構

樹:
樹(Tree)是n(n>=0)個結點的有限集。n=0時稱為空樹,在任意一顆非空樹中:(1)有且只有一個特定的稱之為根(Root)的結點;(2)當n>1時,其餘結點可分為m(m>0)個互不相交的有限集T1、T2、……、Tm,其中每一個集合本身又是一棵樹,並且稱為根的子樹(SubTree).
對於樹的定義還要強調兩點:
1.n>0時根結點是唯一的,不可能存在多個根結點,應與現實中的大樹區分開
2.m>0時,子樹的個數沒有限制,但它們一定是互不相交的。

結點分類:
結點擁有的子樹稱為結點的度(Degree).度為0的結點稱為葉結點(Leaf)或終端結點;度不為0的結點稱為非終端結點或分支結點。除根結點外,分支節點也稱為內部結點。樹的度是樹內各結點的度的最大值。


結點間關係:
結點的子樹的根稱為該節點的孩子(Child),相應地,該結點稱為孩子的雙親(Parent).
同一個雙親的孩子之間的互稱兄弟(Sibling).
結點的祖先是從根到該節點所經分支上的所有節點。
以某結點為根的子樹中的任一結點都稱為該結點的子孫。

樹的其他相關概念:
結點的層次(Level)從根開始定義起,根為第一層,根的孩子為第二層。
雙親在同一層的結點互為堂兄弟。
樹中結點的最大層次稱為樹的深度(Depth)或高度。

如果將樹中結點的各子樹看成從左至右是有次序的,不能互換的,則稱該數為有序樹,否則稱為無序樹。
森林(Forest)是m(m>=0)棵互不相交的樹的集合。

對比線性表與樹的結構:
線性結構:
第一個資料元素:無前驅
最後一個數據元素:無後繼
中間元素:一個前驅一個後繼
樹結構:
根結點:無雙親,唯一
葉結點:無孩子,可以多個
中間結點:一個雙親多個孩子(或一個孩子)

樹的抽象資料型別:
ADT 樹(Tree)
Data
樹是由一個根結點和若干棵子樹構成。樹中結點具有相同資料型別及層次關係。
Operation
InitTree(*T):構造空樹T.
DestroyTree(*T):銷燬樹T.
CreateTree(*T,definition):按difinition中給出樹的定義來構造樹。
ClearTree(*T):若樹T存在,則將樹T清為空樹。
TreeEmpty(T):若T為空樹,返回true,否則返回false.
TreeDepth(T):返回T的深度。
Root(T):返回T的根結點。
Value(T,cur_e):cur_e是樹T中的一個結點,返回此結點的值。
Assign(T,cur_e,value):給樹T的結點cur_e賦值為value.
Parent(T,cur_e):若cur_e是樹的非根結點,則返回它的雙親,否則返回空。
LeftChild(T,cur_e):若cur_e是樹的非葉結點,則返回它的最左孩子,否則返回空。
RightSibling(T,cur_e):若cur_e有右兄弟,則返回它的右兄弟,否則返回空。
InsertChild(*T,*p,i,c):其中p指向樹的某個結點,i為所指結點p的度加上1,非空樹c與T不相交,操作結果為插入c為樹T中p指結點的第i棵子樹。
DeleteChild(*T,*p,i):其中p指向樹T的某個結點,i為所指結點的p的度,操作結果為刪除T中p所指結點的第i棵子樹。
endADT

樹的儲存結構:
雙親表示法:我們假設以一組連續空間儲存樹的結點,同時在每個結點中,附設一個指示器指示其雙親結點在陣列中的位置。---即每個結點除了知道自己是誰之外,還知道它的雙親在哪。
其結點結構如下所示:
|----------|-----------|
|data      |parent     |
|----------|-----------|

其中data為資料域,儲存結點的資料資訊,parent是指標域,儲存該結點的雙親在陣列中的下標。

以下為雙親表示法的結點結構定義程式碼:
#define MAX_TREE_SIZE 100
typedef int TElemType;
typedef struct PTNode
{
TElemType data;
int parent;
}PTNode;
typedef struct
{
PTNode nodes[MAX_TREE_SIZE];
int r,n;//根的位置和結點數
}PTree;

以下是一顆樹的表示:
|-----------|-----------|-----------|
| 下標      | data      | parent    |
|-----------|-----------|-----------|
|   0       | A         | -1        |
|-----------|-----------|-----------|
|   1       | B         | 0         |
|-----------|-----------|-----------|
|   2       | C         | 0         |
|-----------|-----------|-----------|
|   3       | D         | 1         |
|-----------|-----------|-----------|
|   4       | E         | 2         |
|-----------|-----------|-----------|
|   5       | F         | 2         |
|-----------|-----------|-----------|
|   6       | G         | 3         |
|-----------|-----------|-----------|
|   7       | H         | 3         |
|-----------|-----------|-----------|
|   8       | I         | 3         |
|-----------|-----------|-----------|
|   9       | J         | 4         |
|-----------|-----------|-----------|

此外還可以有以下型別的表示法(只是一部分):
|-----------|-----------|-----------|-----------|
| 下標      | data      | parent    | firstChild|
|-----------|-----------|-----------|-----------|

|-----------|-----------|-----------|-----------|
| 下標      | data      | parent    | rightsib  |
|-----------|-----------|-----------|-----------|

儲存結構的設計師一個非常靈活的過程,一個儲存結構設計得是否合理,取決於基於該儲存結構的運算是否合適、是否方便,時間複雜度好不好等。

孩子表示法:
多重連結串列表示法:每個結點由多個指標域,其中每個指標指向一棵子樹的根結點,我們把這種方法叫做多重連結串列表示法。
以下有兩種方案解決孩子個數不同的問題。
方案一:
|----------|-----------|-----------|-----------|-----------|-----------|
|data      | child1    | child2    | child3    | …………      | childn    |
|----------|-----------|-----------|-----------|-----------|-----------|

其中data為資料域,child1到childn為指標域,用來指向該結點的孩子結點。
優缺點分析:這種方法對於樹中各結點的度相差很大時,顯然是浪費空間的,因為有很多的結點,其指標域都是空的,但是如果各個結點的度相差不大時,就意味著空間被充分的利用了,這時儲存結構的缺點反而變成了優點。

鑑於上述第一種方案可能帶來空間上的浪費的問題,我們提出了方案二:
方案二:
第二種方案每個結點指標域的個數等於該結點的度,我們專門取一個位置來儲存結點指標域的個數,其結構如下
|----------|-----------|-----------|-----------|-----------|-----------|-----------|
|data      | degree    | child1    | child2    | child3    | …………      | childn    |
|----------|-----------|-----------|-----------|-----------|-----------|-----------|
其中data為資料域,degree為度域,也就是儲存該結點的孩子結點的個數,child1到childn為指標域,指向該結點的各個孩子的結點。
優缺點分析:這種方案客服了浪費空間的缺點,對空間的利用率是很高了,但是由於各個結點的連結串列是不相同的結構,加上要維護結點的度的數值,在運算上就會帶來時間上的損耗。

鑑於上述的多重連結串列表示法的兩種方案都不是很適合,所以我們引入孩子表示法這個概念:
具體的辦法就是,把每個結點的孩子結點排列起來,以單鏈表作儲存結構,則n個結點有n個孩子連結串列,如果是葉子結點則此單鏈表為空。然後n個頭指標又組成一個線性表,採用順序儲存結構,存放進一個一維陣列中,具體的表示如下所示

|------|------|-----------|     |-------|------|
| 下標 | data | firstChild|     |child  |next  |
|--------|--------|-------------|     |-------|------|    |----|----|
|   0  | A    |           | ==> |1      |      |==> |2   | ^  |
|--------|--------|-------------|    |-------|------|    |----|----|
|   1  | B    |           | ==> |3      |^     |    
|--------|--------|-------------|   |-------|------|    |----|----|
|   2  | C    |           | ==> |4      |      |==> |5   |  ^ |
|------|------|-----------|     |-------|------|    |----|----|    |----|----|
|   3  | D    |           | ==> |6      |      |==> |7   |    |==> |8   |^   |
|------|------|-----------|     |-------|------|    |----|----|    |----|----|
|   4  | E    |           | ==> |9      |^     |
|------|------|-----------|     |-------|------|
|   5  | F    | ^         |
|------|------|-----------|
|   6  | G    | ^         |
|------|------|-----------|
|   7  | H    | ^         |
|------|------|-----------|
|   8  | I    | ^         |
|------|------|-----------|
|   9  | J    | ^         |
|------|------|-----------|

為此需要設計兩種結點結構,一個是孩子連結串列的孩子結點
|-------|------|
|child  |next  |
|-------|------|
其中child是資料域,用來儲存某個結點在表頭陣列中的下標,next是指標域,用來儲存指向某結點的下一個孩子結點的指標。
另一個是表頭陣列的表頭結點
|------|-----------|
| data | firstChild|
|------|-----------|
其中data為資料域,儲存某結點的資料資訊,firstchild是頭指標域,儲存該結點的孩子連結串列的頭指標。

以下為孩子表示法的結構定義程式碼:
#define MAX_TREE_SIZE 100
typedef struct CTNode
{
int child;
struct CTNode *next;
}*ChildPtr;
typedef struct
{
TElemType data;
ChildPtr firstchild;
}CTBox;
typedef struct
{
CTBox nodes[MAX_TREE_SIZE];
int r,n;//根的位置和結點數
}CTree;

以上的結構對於我們要查詢某個結點的某個孩子,或者找某個結點的兄弟,只需要查詢這個結點的孩子單鏈表即可,對於遍歷整棵樹也是很方便的,對頭結點的陣列迴圈即可。

如果需要查詢某個結點的雙親結點是誰的話,則可以考慮一下這種結構
|------|------|------|-----------|     |-------|------|
| 下標 | data |parent| firstChild|     |child  |next  |
|------|------|------|-----------|     |-------|------|    |----|----|
|   0  | A    |-1    |           | ==> |1      |      |==> |2   | ^  |
|------|------|------|-----------|     |-------|------|    |----|----|
|   1  | B    |0     |           | ==> |3      |^     |    
|------|------|------|-----------|     |-------|------|    |----|----|
|   2  | C    |0     |           | ==> |4      |      |==> |5   |  ^ |
|------|------|------|-----------|     |-------|------|    |----|----|    |----|----|
|   3  | D    |1     |           | ==> |6      |      |==> |7   |    |==> |8   |^   |
|------|------|------|-----------|     |-------|------|    |----|----|    |----|----|
|   4  | E    |2     |           | ==> |9      |^     |
|------|------|------|-----------|     |-------|------|
|   5  | F    |2     | ^         |
|------|------|------|-----------|
|   6  | G    |3     | ^         |
|------|------|------|-----------|
|   7  | H    |3     | ^         |
|------|------|------|-----------|
|   8  | I    |3     | ^         |
|------|------|------|-----------|
|   9  | J    |4     | ^         |
|------|------|------|-----------|

以上的表示法稱為雙親孩子表示法


孩子兄弟表示法:
任意一棵樹,它的結點的第一個孩子如果存在就是唯一的,它的右兄弟如果存在也是唯一的。因此,我們設定兩個指標,分別指向該結點的第一個孩子和此結點的右兄弟。
|-----------|-----------|-----------|
| data      | firstchild| rightsib  |
|-----------|-----------|-----------|
其中data為資料域,firstchild為指標域,儲存該結點的第一個孩子結點的儲存地址,rightsib是指標域,儲存該結點的右兄弟結點的儲存地址。
結構定義程式碼如下。
typedef struct CSNode
{
TElemType data;
struct CSNode *firstchild,*rightsib;
}CSNode,*CSTree;

分析:這種表示法,給查詢某個結點的某個孩子帶來了方便,只需要通過firstchild找到此結點的長子,然後再通過長子結點的rightsib找到它的二弟,接著一直下去,知道找到具體的孩子。當然,如果想找某個結點的雙親,這個表示法也是有缺陷的。當然也可以增加一個雙親指標域來解決快速查詢雙親的問題,這樣其實表示出來的圖示就是一個二叉樹。二叉樹的內容,放到下一複習小節。
樹(Tree)是n(n>=0)個結點的有限集。n=0時稱為空樹,在任意一顆非空樹中:(1)有且只有一個特定的稱之為根(Root)的結點;(2)當n>1時,其餘結點可分為m(m)

相關推薦

資料結構筆記--定義抽象資料型別儲存結構

樹:樹(Tree)是n(n>=0)個結點的有限集。n=0時稱為空樹,在任意一顆非空樹中:(1)有且只有一個特定的稱之為根(Root)的結點;(2)當n>1時,其餘結點可分為m(m>0)個互不相交的有限集T1、T2、……、Tm,其中每一個集合本身又是一棵樹,

javascript資料結構與演算法筆記:雙向連結串列

javascript資料結構與演算法筆記(六):雙向連結串列 一:簡介 二:ES6版DoublyLinkedList類 一:簡介 雙向連結串列和普通連結串列的區別在於,在連結串列中,一個節點只有鏈向下一個節點的連結,而

d3.js學習筆記

樹形圖直觀的反應了資料之間的層次關係是現實應用中使用的比較普遍的一類圖形化資料表示方法,使用d3.js繪製樹狀圖的過程中,採取橫縱座標交換方式,可實現思維導圖形式的樹狀圖,示例程式碼如下: var width = 800, height = 800;

Spring 學習筆記AOP 之思想概念和作用JDK代理與Cglib子類代理

概念 AOP為Aspect Oriented Programming的縮寫,意味:面向切面程式設計。 與IOC一樣,是一種思想。 作用 對業務邏輯進行分離,降低耦合度,提高程式的重用性,提高開發效率。 JDK動態代理(介面代理) 弱點:JDK動態代理

Redis學習筆記Nginx+Tomcat+Redis實現負載均衡資源分離session共享

CentOS安裝Nginx CentOS安裝Tomcat CentOS安裝Redis 多個Tomcat負載均衡例項:可在伺服器上覆製出多個Tomcat分別修改Tomcat的 http訪問埠(預設為8080埠)   Shutdown埠(預設為8005埠)   JVM啟

C++複習筆記之函式指標和函式模板類模板

一、函式指標 函式指標在C語言中的作用類似於c++中的多型,都是可以實現框架的搭建,程式碼的相容性高。 函式三要素:名稱、引數、返回值 C語言可以通過typedef為函式型別重新命名,語法 typedef  返回值型別(型別名稱)(引數列表);如下程式碼所示: #in

Latex學習筆記——自定義Latex模板

前言:         對於一個給定的模板,自己做了修改,用於日常工作寫報告使用(主要去除了封面和摘要部分),包含兩個檔案:(1) cls 檔案裡面定義好了常用的格式和環境;(2) tex 檔案裡面是我們文件內容的原始碼。當然,大家也可以根據自己的需要進行更改。 一、

ES6學習筆記--set,map資料結構和for...of遍歷

Set 新的資料解構,成員值是唯一的 //不能新增相同的值 var s = new Set(); [2, 3, 5, 4, 5, 2, 2].map(x => s.add(x)); for (let i of s) { console.lo

資料Hadoop學習筆記

HDFS HA 背景: 在hadoop2.0之前,HDFS叢集中的NameNode存在單點故障(SPOF)對於只有一個NameNode的叢集,若NameNode機器出現故障,則整個叢集將無法使用,直到NameNode重新啟動 NameNode主要在一下兩方面影響

北京理工大學-資料結構期末考試試題

資料結構試卷(六) 一、選擇題(30分) 1. 設一組權值集合W={2,3,4,5,6},則由該權值集合構造的哈夫曼樹中帶權路徑長度之和為(  )。    (A)20     &n

學習筆記預測貸款使用者是否會逾期資料分析的簡單處理

資料的簡單處理 學習筆記(六)資料分析的簡單處理 特徵工程初步的處理 1. 資料預覽 2. 資料型別的轉化    日期格式資料的處理    無關特徵

資料結構與演算法筆記複雜度分析

2. 複雜度分析 2.1 什麼是複雜度分析 資料結構和演算法的本質:快和省,如何讓程式碼執行得更快、更省儲存空間。 演算法複雜度分為時間複雜度和空間複雜度,從執行時間和佔用空間兩個維度來評估資料結構和演算法的效能。 複雜度描述的是演算法執行時間(或佔用空間)與資料規模的增長關

資料結構與演算法筆記陣列

3.陣列 陣列(Array)是一種線性表資料結構。它是一組連續的記憶體空間,來儲存一組具有相同型別的資料。 3.1 特性 線性表 資料排成像一條線的結構,如陣列、連結串列、佇列、棧等。 與之相對立的是非線性,如二叉樹、堆、圖等,其資料之間並不是簡單的前後關係。

內建資料型別---Python基礎學習筆記

一、if-else條件語句 條件語句,條件滿足,執行一部分程式碼,不滿足,執行另一部分程式碼 四種基本格式: 1、單if …① if條件: 條件滿足時執行的程式碼…② …③ 執行順序: S1:執行① S2

資料結構與演算法筆記反轉部分連結串列

反轉部分連結串列 上次我們搞定了反轉單向連結串列和雙向連結串列的問題,但實際過程中我們可能只要反轉部分連結串列,在這種情況下我們需要對上次寫出的類增加一個叫做reverse_part_linklist的函式,傳入引數為兩個整數from和to,將from到to之間的節點進行反轉

javascript資料結構與演算法筆記:棧

javascript資料結構與演算法筆記(一):棧 一:簡介 二:ES6版Stack類(陣列) 三:ES版Stack類私有屬性的封裝 1.偽私有屬性封裝 2.真私有屬性封裝

Javascript筆記之程式結構與流程控制語句

一、程式結構簡介 1、幾個基本概念 程式結構分為順序結構、分支結構、迴圈結構,通常伴隨著這幾種程式結構存在的還有流程控制語句; 在ECMA-262規定來一組流程控制語句,語句定義了ECMAScript中的主要語法,語法通常由一個或多個關鍵字來完成給定的任務,例如

golang學習筆記——golang基礎和相關資料結構

小白前端一枚,最近在研究golang,記錄自己學習過程中的一些筆記,以及自己的理解。 go中包的依賴管理 go中的切片 byte 和 string go中的Map go中的struct結構體 go中的方法 go中的interface介面 inter

javascript資料結構與演算法筆記:連結串列

javascript資料結構與演算法筆記(五):連結串列 一:簡介 二:ES6版LinkedList類 一:簡介 連結串列儲存有序的元素集合,但不同於陣列,連結串列中的元素在記憶體中並不是連續放置的。每個 元素由一個儲

javascript資料結構與演算法筆記:迴圈佇列

javascript資料結構與演算法筆記(四):迴圈佇列 一:簡介 二:ES6版Queue類 一:簡介 迴圈佇列是指佇列頭元素的移除會追加到佇列的尾部。我們此次拿一個例子來實現迴圈佇列,例子名就是模擬民間遊戲擊鼓傳花即