資料結構與演算法

102
資資資資資資資資 資資資資資資資

Upload: fiona-england

Post on 01-Jan-2016

22 views

Category:

Documents


4 download

DESCRIPTION

資料結構與演算法. 課程教學投影片. 本章各段大綱 11-1 樹狀結構和特性 11-2 二元樹 11-3 二元樹的資料結構 11-4 二元樹的走訪. 11-5 二元運算樹 11-6 堆積 11-7 二元搜尋樹. 第十一章 – 樹. 11-1 樹狀結構和特性. 在樹狀結構中,一般最頂端者稱為根( Root), 由根開始延伸出節點( node), 所延伸出的其它節點稱為分支節點( Branch Node), 最底端不再延伸的節點稱為終端節點( Terminal node) , 連結二節點的線稱為節線( Edge)。. 樹狀結構的基本定義. 樹狀結構. - PowerPoint PPT Presentation

TRANSCRIPT

Page 1: 資料結構與演算法

資料結構與演算法

課程教學投影片

Page 2: 資料結構與演算法

第十一章–樹• 本章各段大綱• 11-1 樹狀結構和特性• 11-2 二元樹• 11-3 二元樹的資料結構

• 11-4 二元樹的走訪

• 11-5 二元運算樹• 11-6 堆積• 11-7 二元搜尋樹

Page 3: 資料結構與演算法

11-1 樹狀結構和特性•在樹狀結構中,一般最頂端者稱為根(Root),由根開始延伸出節點(node),所延伸出的其它節點稱為分支節點 (Branch Node),最底端不再延伸的節點稱為終端節點 (Terminal node) ,連結二節點的線稱為節線(Edge)。

Page 4: 資料結構與演算法

A

B C D

E F G

H

父節點

子節點

祖父節點

孫子節點

兄弟節點

樹狀結構

A

B C D

E F G

H

第1層

第2層

第3層

第4層

A 節點(Root) 節線(Edge)

內部節點

外部節點

樹狀結構的基本定義

Page 5: 資料結構與演算法

• 一個樹狀結構的基本定義如下附圖請參考上頁:– 節點( Node):樹狀結構中的每個點,如附圖中的A、 B、 C、 D、 E、 F、 G、 H。

– 根節點( Root node):最頂點的節點,可連結到其他節點,但別的節點不可連結到它,如附圖的 A。

– 節線( Edge):連結二節點的線,分有方向性和無方向性兩種。如附圖的節線為無方向性的節線。

– 內部節點( Internal node):其底下還有節點者稱為內部節點,也稱為非終止節點( Unterminal node),如附圖中的 A、 B、 C、 E。

– 外部節點( External node):其底下沒有節點者稱為外部節點,也稱為終止節點( Terminal node),如附圖中的 D、 F、 G、 H。

– 分支度( Branch Degree):由某節點往下可連結的節點數,亦即某節點往下的節線數。如附圖中, A 的分支度是 3,B 的分支度是 2。

– 層( Level):層也可定義為階度( Order)。根節點為第一層,根節點所連結的節點稱為第 2層。如附圖中,第 2層的節點有 B、 C、 D,第 3層的節點有 E、 F、 G。

– 高度( Height):樹的最大階度,如附圖的最大階度為 4,所以高度 =4。

Page 6: 資料結構與演算法

11-1 樹狀結構和特性•上下兩層節點間有節線連結者的關係稱為父子節點,下層節點稱為上層節點的子節點 (Children node),上層節點稱為下層節點的父節點 (Parent node),同層的節點且有共同父節點者稱為兄弟 (Twins或 Sibling)節點。若不只一層的關係,則同理可稱為祖父節點或孫子節點。

Page 7: 資料結構與演算法

• 由樹狀結構所定義的基本結構可延伸下列計量名稱– 總節點數:節點的總數。如圖 11-3 的總節點數 8。– 總節線數:節線的總數。如圖 11-3 的總節線數 7。– 路徑長( Path Length):連結任二個節點的節線數總和稱為路

徑長,也可看成每條節線的路徑,長度等於 1。如圖 11-3 中, A 到E 的路徑長 2, A 到 H 的路徑長 3, A 到 C 的路徑長 1, A 到 G 的路徑長 2。

– 總路徑長( Total Path Length):樹根到所有節點路徑長的總和稱為總路徑長。

– 最大分支度:所有分支度中最大數值,如 A 的分支度為 3, B 的分支度為 2,其餘分支度皆為 1,所以最大分支度為 3。

– 總分支度:所有節點分支度的總和。• 特性 :由以上的延伸定義,可得到下列的特性:

– 總節點數 =總節線數 +1如果總節點數為 8,總節線數為 7。除了根節點外,每一個節點都有一條節線數。

– 總分支度 =總節線數如果總分支度為 7,總節線數也為 7。

Page 8: 資料結構與演算法

11-2 二元樹•二元樹定義和結構

–樹狀結構有非常多種,其中最常使用的稱為二元樹( Binary tree)。二元樹的定義如下:

–二元樹 (Binary Tree):每個節點最多只能有 2個子節點的樹,即每個節點的最大分支度為 2或 1或 0。

Page 9: 資料結構與演算法

• 因為二元樹最多只有 2個子節點,以方向性來看,可以有以下的定義,以下圖為例:– 左節點( Left node):某節點的左邊子節點,如 B 是 A

的左節點, C 是 D 的右節點, G 是 C 的右節點,由此類推。– 右節點( Right node):某節點的右邊子節點,如 C 是 A

的右節點, G 是 C 的右節點。– 左子樹( Left Tree):由左節點所代表的樹,如 B 節點

以下的樹稱為 A 的左子樹。– 右子樹( Right Tree):由右節點所代表的樹,如 C 節點

以下的樹稱為 A 的右子樹。

A

BC

I

D E F

H

C是A的右節點

A的右子樹H是D的左節點

A的左子樹

G是C的右節點

B是A的左節點

左G

J

Page 10: 資料結構與演算法

11-2 二元樹• 二元樹除了有一般樹狀結構的公式之外,尚有下列公

式:– 【公式一】

二元樹有 N 個分支度為 2的節點時,則有 N+1個外部節點(分支度為 0)。

– 【公式二】若一個二元樹只有分支度 2或 0時,其內部節點數是 N 時,則外部節點路徑長和 =外部節點路徑長和 +2N

– 【公式三】二元樹中階層 i 的節點數,最多只有 2(i-1)個, i≧1

– 【公式四】高度為 i 的二元樹,節點總數最多只有 2i- 1 個, i≧1

Page 11: 資料結構與演算法

11-2 二元樹•練習1•若一個二元樹共有 n 個節點,則其最小高度和最大高度為何?當 n=280時最小高度和最大高度為何?

•解答:最小高度 =└log2 n┘+1,最大高度 =n n=300時,最小高度 =└log2 (280)┘+1=8+1=9,最大高度 =300

Page 12: 資料結構與演算法

11-2 二元樹•練習 2•深度為 5 的二元樹,最多有幾個節點,最少有幾個節點?

•解答:深度 h=5時,最多節點數 =2h-1=25-1=32-1=31,最小節點數=h=5 。

Page 13: 資料結構與演算法

11-2 二元樹•練習 3•如果一個二元樹有 6 個分支度為 2 的節點時,則其終端節點 (樹葉 )有幾個?

•解答:由公式一得知 n0=n2+1,所以 終端節點 =6+1=7 。

Page 14: 資料結構與演算法

• 完滿二元樹的定義• 完滿二元樹 (Full binary tree)是一個二元樹,所有的外部

節點都必須在同一層。• 左圖是完滿二元樹,右圖不是完滿二元樹,因為 G 外部節點在第

3層,而其他的外部節點 H、 I、 J、 K、 L、 M 都在第 4層。

A

B C

I

D E F

H

是完滿二元樹

G

J

不是完滿二元樹

L M N OK

A

B C

I

D E F

H

G

J L MK

Page 15: 資料結構與演算法

11-2 二元樹•【公式一】一個完滿二元樹若總共有 N 個節點時,則高度 =└log2 (N+1)┘。

•範例 5•上一頁中左圖的完滿二元樹,總節點數 15。

則高度 =log2 (N+1) =log2 (15+1) =log2 2

4 =4 。

Page 16: 資料結構與演算法

11-2 二元樹•【公式二】高度為 k 的完滿二元樹,含有 2k- 1 個節點

•範例6•令一個二元樹的根 (root)之深度為 l,則深度為 n 的二元樹最多共有多少的節點(nodes) ?

•答案:共有 2n- 1 個節點 ,圖解請參考下頁

Page 17: 資料結構與演算法

A

B C

I

D E F

H

G

J L M N OK

高度 1=> 此層共有1個節點

高度 2=> 此層共有21=2個節點

高度 3=> 此層共有22=4個節點

高度 4=> 此層共有23=8個節點

節點數總和=20+21+22+23=15=24-1=2k-1

Page 18: 資料結構與演算法

• 完整二元樹定義• 完整二元樹 (Complete binary tree)是外部節點相差的階

數在 1階以內,含 0階 (0 階即是完滿二元樹 ),而且可以循序編號。

• 完滿二元樹必是完整二元樹,但完整二元樹不一定是完滿二元樹

A

B C

I

D E F

H

G

J K

Page 19: 資料結構與演算法

11-2 二元樹• 【公式一】

高度為k 的完整二元樹,含有的節點數目介於 2(k-1)~ 2k-1 之間,節點的編號次序如同完滿二元樹一般 (中間無缺 )。

• 【公式二】有 n 個節點的完整二元樹,其高度為 └ log2n ┘ + 1

• 範例7若存在一棵二元樹有 25 個節點 (node) ,那麼它的高度(Height) 不 可 能 為 4 , 若 它 是 一 棵 完 整 二 元 樹(Complete Binary Tree),則它的高度為何?解答: └log2 25┘ + 1 = 4+1=5

Page 20: 資料結構與演算法

11-3 二元樹的資料結構• 二元樹的編號系統• 因為二元樹只有左節點和右節點之分,而且各個節點又有階層之

分,所以如果以根節點開始編號( 0號),然後由左而右、由上而下編號,二元樹的編號次序如下圖。

1 2

3 4

65 7

0

Page 21: 資料結構與演算法

• 二元樹的編號系統– 【結論一】第 k 層的最左邊編號為 2(k-1),最右邊的編號是 2k-1,所以 k 層的編號介於 2(k-1)至 2k-1之間

– 【結論二】第 k 層的節點數 =最大編號 -此層最小編號 +1= ( 2k-1) -( 2(k-1)) +1=( 2k) -( 2(k-1)) =2 x( 2(k-1)) -( 2(k-

1)) =2(k-1)

– 【結論三】1個 k 層的二元樹,節點數最多有 2k-1個

– 【結論四】某節點的編號 m,則其左節點的編號為 2m,右節點的編號為 2m+1 。

Page 22: 資料結構與演算法

11-3 二元樹的資料結構•練習在二元樹中,節點編號為 3 的節點,其父節點、左子節點和右子節點的編號各為多少?解答:節點編號 p 時,其父節點編號 =└ p/2┘=└3/2┘=└1.5┘=1。 其左節點編號 =2p=2x3=6,其右節點編號 =2p+1=2x3+1=7 。

Page 23: 資料結構與演算法

11-3 二元樹的資料結構•用一維陣列來表示

1

2 3

4 5 76

B

A

C

D E F G

1 2

B C D GFEA B C D GFEA

3 4 5 6 7

D的父節點 =X( 4/2 )=X(2)=B

E的父節點 =X( 5/2 )=X(2)=B

C的左子節點 =X(2*3)=X(6)=F

C的右子節點 =X(2*3+1)=X(7)=G

D的父節點 =X( 4/2 )=X(2)=BD的父節點 =X( 4/2 )=X(2)=B=X( 4/2 )=X(2)=B

E的父節點 =X( 5/2 )=X(2)=BE的父節點 =X( 5/2 )=X(2)=B=X( 5/2 )=X(2)=B

C的左子節點 =X(2*3)=X(6)=FC的左子節點 =X(2*3)=X(6)=F

C的右子節點 =X(2*3+1)=X(7)=GC的右子節點 =X(2*3+1)=X(7)=G

X

Page 24: 資料結構與演算法

11-3 二元樹的資料結構•用二維陣列來表示

(2,1)

(3,1)

(4,1)

(2,2)

A

B

D

C

E F G

H I J K L M N O

(3,4)

(4,8)(4,2) (4,3) (4,4) (4,5) (4,6) (4,7)

(1,1)

(3,2) (3,3)

K

G

J

F

I

E

C

H

D

B

A

ONMLK

G

J

F

I

E

C

H

D

B

A

ONML

876543210 876543210

X陣列

不用

0

1

2

3

4

不用

Page 25: 資料結構與演算法

11-3 二元樹的資料結構• 【結論六】 某一節點的座標是( r,c)時,則其左子節點的座標是( r+1,2c-1),右子節點的座標是( r+1,2c)

• 範例 9E 節點的座標是( 3,2),則左子節點的座標是( 3+1,2x2-1) =( 4,3), X( 4,3) =J右子節點的座標是( 3+1,2x2) =( 4,4), X( 4,4) =K

Page 26: 資料結構與演算法

11-3 二元樹的資料結構• 【結論七】

某一節點的座標是( r,c)時,其父節點的座標是( r-1,┌c/2┐ ┌)。( p┐是取大於等於p 的最小整數)。

• 範例10J 節點的座標是(4,3),則其父節點的座標是(4-1, ┌3/2┐)=( 3, ┌1.5┐)=( 3,2), X( 3,2) =E。

• 範例11K 節點的座標是(4,4),則其父節點的座標是(4-1, ┌4/2┐)=( 3, ┌2┐)=( 3,2), X( 3,2) =E。

Page 27: 資料結構與演算法

11-3 二元樹的資料結構• 比較• 用一維陣列或二維陣列來表示二元樹時,都是會用到轉換計算,求得父子節點的關係式。運算方式差不多,但以儲存空間來看,一維陣列表示法不會浪費空間,二維陣列表示法除了浪費第 0列、第 0欄的空間( 2k-1+k+1)。另外未用到的空間則有:

• ( 2k-1-20) +( 2k-1-21) +( 2k-1-22) +…+( 2k-

1-2k-1) =2k-1 x k -( 20+21+22+…+2k-1) =2k-1 x k -( 2k - 1 )

Page 28: 資料結構與演算法

• 二元樹以結構陣列來表示– 現在一般的高階語言 (如 C++、 Visual Basic等 )都提供結構的型態,而且二元樹最多只有左、右節點兩種形式,所以二元樹也適合用結構來表示。

– 以結構表示二元樹時,只要宣告 1 個有三個項目的結構,其中一欄存放節點的資料,另外兩欄分別存放可連結到左子節點和右子節點的鏈結索引指標,如下圖。

A

B C

D E F

1 2

3 4 5

0 0 2A10 2A1

1 -1B31 -1B3 2 5C42 5C4

5 -1F-15 -1F-13 -10-13 -10-1 4 -1E-14 -1E-1

-1代表無子節點

rightdataleft索引指標

-1F-15

-1E-14

-1D-13

5C42

-1B31

2A10

rightdataleft索引指標

-1F-15

-1E-14

-1D-13

5C42

-1B31

2A10

Page 29: 資料結構與演算法

• 以 C語言為例,結構陣列的宣告方式如下:struct bitree{

int left;char data;int right;

}typedef struct bitree bitreenode;bitreenode a_bitree[100];

• 這樣的定義方式,由父節點要連結子節點時,只要利用子節點索引指標或右節點索引指標,即可取得左右節點的值。例如:x=a_bitree[0].left; /* x是根的左子樹索引指標 */xdata=a_bitree[x].data; /* xdata是根左子節點的值 */y=a_bitree[0].right; /* y是根的右子樹索引指標 */ydata=a_bitree[y].data /* ydata是根右子節點的值 */

相關建立樹的範例程式請參考本書範例程式

Page 30: 資料結構與演算法

• 只要控制好索引指標,即可控制二元樹的存取。另外上述結構中,無法由子節點找到父節點,所以我們再修改結構定義如下,即可同理取得父節點的索引指標。Struct bitree1{

int left;char data;int right;int parent;

}typedef struct bitree1 bitreenode1;bitreenode1 a_bitree1[100];

• 其父節點的資料取得方式如下:p=a_bitree|[1].parent /* 取得父節點 A 的索引指標 */pdata=a_bitree1[p].data /* 取得父節點 A 的資料 */

-1

-1

-1

5

-1

2

right parentdataleft索引指標

2F-15

2E-14

1D-13

0C42

0B31

-1A10

-1

-1

-1

5

-1

2

right parentdataleft索引指標

2F-15

2E-14

1D-13

0C42

0B31

-1A10A

B C

D E F

1 2

3 4 5

0

Page 31: 資料結構與演算法

• 二元樹以鏈結串列來表示– C 語言提供鏈結串列的指令,所以也可以用鏈結串列來表示二

元樹,此方法主要是以鏈結串列運用動態記憶體配置方式運作,和前一小節的結構陣列很類似,但結構陣列是運用靜態記憶體配置方式。

– 以鏈結串列表示二元樹時,要設計一個左子節點指標、一個右子節點指標、一個資料欄位和一個父節點指標,如下圖。

A

B C

D E F

parentrightAleft parentrightAleft

parentrightBleft parentrightBleft parentrightCleft parentrightCleft

parentrightDleft parentrightDleft parentrightEleft parentrightEleft parentrightFleft parentrightFleft

Null Null Null NullNullNull

Null

Page 32: 資料結構與演算法

11-3 二元樹的資料結構• 表示二元樹的鏈結串列宣告方式如下:struct bitree2{

struct bitree2 *left;char data;struct bitree2 *right;struct bitree2 *parent;

}typedef struct bitree2 bitreenode2;bitreenode2 *p_bitree2;

相關建立樹的範例程式請參考本書範例程式

Page 33: 資料結構與演算法

11-4 二元樹的走訪• 當我們已經將資料建立成二元樹結構(不管資料結構是一

維陣列、二維陣列、結構陣列或鏈結串列等),當我們要對此二元樹操作時(找尋某資料,擷取各節點資料,統計各節點等),必須「走訪」( travrtsal)整個二元樹。

• 常用的走訪方法如下, D 表示某節點, L 表示走訪左子樹,R 表示走訪右子樹 – LDR:先走訪左子樹,再走訪節點,最後走訪右子樹,以節點被走

訪的次序是中間順序,所以稱為「中序法走訪」( inorder traversal)。

– LRD:先走訪左子樹,再走訪右子樹,最後走訪節點,節點最後被走訪,所以此法稱為「後序法走訪」( postorder traversal)。

– DLR:先走訪節點,再走訪左子樹,最後走訪右子樹,節點最先被走訪,所以稱為「前序法走訪」( preorder traversal)。

Page 34: 資料結構與演算法

• 前序法走訪( preorder reaversal)如前所述是 DLR方法,走訪次序是【節點】→【左子樹】→【右子樹】,在【左子樹】中會遞迴處理,直到結束時才往下一個走訪,以一個運算式 A*B+C/D-E建立成二元樹後(建立方法下一節介紹),其前序法走訪將資料印出時,如果遞迴的關係以堆疊的角度來看,則操作次序如下圖。

次序次序 走訪節點走訪節點 堆疊

2

3

6

1

4

7

8

0

A

*

/

B

C

D

E

5

無左子樹了

無左子樹了

無左子樹了

無左子樹了

無左子樹了,結束

輸出+,push(2)

輸出*,push(4)

輸出A,pop(4)

輸出B,pop(2)

輸出/,push(5)

輸出C,pop(5)

輸出D,pop(1)

所以前序法走訪的資料次序為- + * A B / C D E

輸出-,push(1)

1

21

421

21

51

3

6

+

*

E

/

A B C D

7 8 9

1

2

4 52

3 6

1

4 7

8

0

5

精簡為

+

*

E

/

A B C D

1

輸出E,結束

<0>

<1>

<2>

<3> <4>

<5>

<6> <7>

<8>

Page 35: 資料結構與演算法

前序法走訪演算法010203040506070809

void preorder(binode *d){

if (d!=Null){

prontf(“%C”, d->data);preorder(d->left);preorder(d->right);

}}

Page 36: 資料結構與演算法

範例一個單循環淘汰賽比賽二元樹如下圖,列出前序法走訪的結果,其中 G1~G7代表比賽, P1~P8代表比賽選手。

• 前序法走訪是先走訪節點,再左子樹,再右子樹,所以輸出順序為:• G1、 G2、 G4、 P1、 P2、 G5、 P3、 P4、 G3、 G6、 P5、 P6、 G

7、 P7、 P8。• 代表意義:• G1→G2→G4→P1→P2→G5→P3→P4→G3→G6→P5→P6→G7→P7→P8• G1是冠軍,可設計程式依據此輸出資料作進一步處理。

Page 37: 資料結構與演算法
Page 38: 資料結構與演算法

中序法走訪( inorder traversal)如前所述是 LDR方法,走訪次序是【左子樹】→【節點】→【右子樹】,在【左子樹】中會用遞迴處理,直到結束時才往下一個走訪。同樣以一個運算式 A*B+C/D-E建立成二元樹後(建立方法下一節介紹),以中序法走訪方式將資料印出,且如果遞迴關係以堆疊的角度來看,則操作次序如圖 11-17 。

3

6

+

*

E

/

A B C D

7 8 9

1

2

4 5

2

3 10

1

6 13

15

8

9

125

14

4 7 11

次序 走訪節點 堆疊

7

8 +

C 無左子樹了

放入5

取出2

取出5

5 1

B 無左子樹了 1

9

10

11

輸出A B +C / D - E*

/

D

E

無左子樹了

無左子樹了,結束

12

13

14

15

16

取出1

2

3

6

1

4 A

5

無左子樹了

放入2

放入4

取出4

放入11

2 1

4 2 1

2 1

* 2 1

堆疊動作

Page 39: 資料結構與演算法

中序法走訪演算法010203040506070809

void inorder(binode *d){

if (d!= Null){

inorder(d->left);printf(“%C”, d->data);inorder(d->right);

}}

Page 40: 資料結構與演算法

範例以一個單循環淘汰賽的比賽二元樹如下圖,列出中序走訪的結果,其中 G1~G7代表比賽, P1~P8代表比賽選手。

Page 41: 資料結構與演算法
Page 42: 資料結構與演算法

後序法走訪( postorder traversal)如前所述是 LRD方法,走訪次序是【左子樹】→【右子樹】→【節點】,在【左子樹】中會用到遞迴處理,直到結束時才往下一個走訪。同樣以一個運算式 A*B+C/D-E 建立成二元樹後(建立方法下一節介紹),以後序法走訪方式將資料印出,且遞迴關係以堆疊的角度來看,則操作次序如圖 11-18

3

6

+

*

E

/

A B C D

7 8 9

1

2

4 5

2

3 9

1

5

13

17

8 16

12

7

14

4 6 10

15

11

2

3

6

1

4 A

B

5

無左,右子樹了

無左子樹了

放入2

放入4

取出4

放入11

2 1

4 2 1

2 1

1

2 1

7 取出2* 1

11

所以輸出A B * C D / + E -

走訪節點 堆疊次序 堆疊動作

17

5 2 19 放入5

C 無左右節點 取出510 2 1

放入55 2 1

D12 取出52 1

+14 取出1

E16 取出1

/13 取出21

8 放入22 1

15 放入11

精簡為

<9>

<4>

+

*

E

/

A B C D

<7> <8>

<5><1> <2>

<3> <6>

Page 43: 資料結構與演算法

後序法走訪演算法010203040506070809

void postorder(binode *d){

if (d!=Null){

postorder(d->ledt);postorder(d->right);postorder(“%C”, d->data);

}}

Page 44: 資料結構與演算法

範例以一個單循環淘汰賽的比賽二元樹如下圖,列出後序走訪的結果,其中 G1~G7代表比賽, P1~P8代表比賽選手。

Page 45: 資料結構與演算法

階層走訪階層走訪( level-order traversal)是指依階層的順序走訪,先訪問階層小的所有節點,同一階層由左而右走訪,再往下一階層走訪,以此累推。

如果建立二元樹的資料結構是一維陣列或二維陣列時,只要控制陣列的索引指標,一維陣列的索引指標由小而大,二維陣列的索引指標控制由左而下再由上而下。但如果二元樹的資料結構是用結構陣列或鏈結串列的 left和 right指標來指到左子樹和右子樹時,由於階層走訪的型式不是用遞迴關係,而是用佇列的結構,因為由根走訪完後,可將左子樹的根節點先放入佇列,再放右子樹的根節點,以此累推, 如下圖說明。

-+子樹E子樹 -+子樹E子樹(1)

(2)

+

*

E

/

A B C D(3)

輸出-處理+子樹

E子樹*子樹/子樹 +E子樹*子樹/子樹 +

(4)

輸出+ 處理E子樹

/子樹BA */子樹BA *

輸出E 處理*子樹

ABCD /ABCD /

輸出*處理/子樹

輸出/

ABCD ABCD(5)

階層走訪結果為 - + E * / A B C D

輸出A B C D

佇列

Page 46: 資料結構與演算法
Page 47: 資料結構與演算法

11-4 二元樹的走訪• 利用中、前序法轉二元樹:

1. 利用前序法的順序(根、左子樹、右子樹)可知,第 1個一定是根。

2. 利用中序法的順序(左子樹、根、右子樹),配合步驟( 1)所找到的根作對應,可決定出根,再分成左子樹和右子樹。

3. 再分別依步驟( 1)、( 2)處理左子樹和右子樹,找出其他的根和終端節點。

Page 48: 資料結構與演算法

範例 16假設一運算式前序法走訪的資料次序為 -+*AB/CDE,中序法走訪的資料次序為A*B+C/D-E,請畫出此二元樹。

Page 49: 資料結構與演算法
Page 50: 資料結構與演算法
Page 51: 資料結構與演算法
Page 52: 資料結構與演算法

11-4 二元樹的走訪• 利用中、後序法轉二元樹:

1. 利用後序法的順序(左子樹、右子樹、根),得知最後 1個一定是根。

2. 利用中序法的順序(左子樹、根、右子樹),配合步驟( 1)所找到的根作對應,可決定出根,再分成左子樹和右子樹。

3. 再分別依步驟( 1)、( 2)處理左子樹和右子樹,找出其他的根和終端節點。

Page 53: 資料結構與演算法

範例 17假設一運算式後序法走訪的資料次序為 AB*CD/+E-,中序法走訪的資料次序為A*B+C/D-E,請畫出此二元樹。

Page 54: 資料結構與演算法
Page 55: 資料結構與演算法
Page 56: 資料結構與演算法

11-5 二元運算樹• 運算式的處理和計算是每種程式語言必須做的程

序,由於運算式的處理需考慮運算子的優先順序,而且大部分的運算子都有兩個運算元(除了正負號之外),而二元樹的左右子樹也有順序之分,所以可以將運算式表示成二元樹,稱為二元運算樹( Binary Expression Tree)。再以二元樹的走訪方法,即能計算出運算式的值。

• 【定義】 • 二元運算式是一個二元樹,且內部節點是運算子

(例如 +-*/等),外部節點則為純資料的運算元,優先率高的運算子是優先率較低的子樹。

Page 57: 資料結構與演算法

11-5 二元運算樹•一般運算子的優先次序是 (1)括號、 (2)正負號、 (3) 次方、 (4)*/、 (5)+- 、(6)=

•建立二元運算樹 :要將運算式轉換為二元樹時有兩種方法–直接用觀察的方法,畫出二元運算樹。–利用堆疊法運算,以程式運作建立二元運算式。

Page 58: 資料結構與演算法

11-5 二元運算樹• 觀察法 ,利用運算子的優先次序和二元運算

樹的特性,將運算式表示成二元運算樹,再建立二元樹供後續程式運作,一般簡單的考題可用此方法,快速建立二元運算樹,其步驟如下:1. 根據運算子的優先次序和結合性,將運算式加入括號。

2. 由內部括號開始,將括號中的運算子當樹根,左邊的運算元當左子樹,右邊的運算元當右子樹,依由內而外順序處理其餘括號,直到最外層括號為止。

Page 59: 資料結構與演算法

11-5 二元運算樹範例 18,將 A*B+C/D-E運算式化為二元運算樹。解答:依運算子優先次序加入括號( */ 優先率高於 +-)A * B + C / D - E → ( A * B ) + C / D - E→ ( A * B ) + ( C / D ) - E→ ( ( A * B ) + ( C / D ) ) - E→ ( ( ( A * B ) + ( C / D ) ) - E )所以二元運算式 = ( ( ( A * B ) + ( C / D ) ) - E )

* /

A B C D

+

* /

A B C D

+

*

E

/

A B C D

(1) (2) (3) (4)

Page 60: 資料結構與演算法
Page 61: 資料結構與演算法

11-5 二元運算樹• 堆疊法 ,運算式轉換二元運算樹正規的方法是利用堆疊法,以

可程式化的方法直接輸入運算式來建立二元運算式,再進行其他處理。

• 要利用此方法時,會運用到堆疊章節 (第 7章 )所介紹的內容,將中序式轉成後序式。再以後序式建立二元運算樹,其步驟如下:a) 中序式→後序式。b) 後序式→二元運算樹。

1. 由左而右掃描。2. 如果是運算元:建立一節點,放入堆疊。3. 如果是運算子: POP所需的資料項,建立一子樹,再放入堆疊。

4. 如果掃描到最後一個資料時步驟結束。

Page 62: 資料結構與演算法

範例 19將 A * B + C / D - E 運算式化為二元運算樹。

Page 63: 資料結構與演算法
Page 64: 資料結構與演算法
Page 65: 資料結構與演算法
Page 66: 資料結構與演算法

11-6 堆積• 堆積結構• 一般對於所建立的二元樹,如果要找出最大(最小)

值時,可以利用前序法、中序法、後序法走訪各個節點,然後比較各節點的值,找出最大(或最小)值,但是當我們設計二元樹的目的是隨時可查詢節點資料的最大(最小)值,或前幾大(前幾小)的值時,每作一次查詢都得走訪整棵樹,這將很沒有效率。

• 其實為了應付上述的搜尋問題,我們在建立二元樹時,可將資料設計成稱為堆積( Heap)的特殊二元樹,則搜尋最大(最小)值時,只要走訪最低階層(或最高階層)即可,因為堆積是將資料依大小順序安排節點,且要符合完整二元樹的定義,其正式定義分為最大堆積( max-heap)和最小堆積( min-heap)兩種。

Page 67: 資料結構與演算法

最大堆積 :最大堆積為一完整二元樹,任一非終端節點的資料不小於其子節點的資料。

由前述定義可知,最大堆積的根節點一定是最大值,較大的資料位於階層較低的層

25

15 20

4

10 12 16

5

18

是最大堆積 不是最大堆積

25

15 20

4

10 12

5

階層相差2 不是完整二元樹

不是最大堆積

20

15 30

4

10 12 16

5

18

子節點比父節點大不是最大堆積

Page 68: 資料結構與演算法

最小堆積為一完整二元樹,任一非終端節點的資料不大於其子節點的資料。

最小堆積的根節點一定是最小值,較小的資料位於階層較低的層

2

5 20

30

15 18 12

35

20

2

5 10

30

15 18

25

階層相差2 不是完整二元樹

是最小堆積 不是最小堆積

2

5 10

30

15 18 12

25

9

子節點資料比父節點小不是最小堆積

不是最小堆積

Page 69: 資料結構與演算法

11-6 堆積• 資料結構• 因為堆積是一個完整二元樹,之前中介紹過用陣列來表示二元樹的方法,

所以一般完整二元樹可以用一維陣列來當作它的資料結構,則程式運作的處理較簡單方便。

• 因為最大堆積和最小堆積只是大小順序相反,其他都一樣,所以接著所介紹的範例和圖例都以最大堆積為例。堆積以陣列的表示法如下圖。

25

10 15

5 8

1 2

3 4

0

1 2

10 15 5 825 10 15 5 825

3 4 5 6…….0陣列指標

Page 70: 資料結構與演算法

堆積的操作因為最大堆積必須符合節點的資料比子節點大或等於,所以建立堆積、插入節點、刪除節點後,都還要維持堆積的特性,所以這些對於堆積的操作程序,則有一定的規則。

插入節點到最大堆積中假設如上一頁圖中的最大堆積,如果要加入一個新節點,其資料為 20時,其運作方式如下圖 1。

假設如圖 1的最大堆積,再加上一新節點,其資料為 40時,其運作方式如圖 2。

25

10 15

5 8

1 2

3 4

0

20

5

25

10 20

5 8

1 2

3 4

0

15

5

(1) (2)

20比15大交換

20比25小不交換

(3)

20放入陣列(2)的位置

1 2

10 15 5 825 10 15 5 825

3 4 50 1 2

10 15 5 15825 10 15 5 15825

3 4 50 1 2

10 20 5 15825 10 20 5 15825

3 4 50

25

10 20

5 8

1 2

3 4

0

155

(1)

40比20大交換

40

1 2

1510 20 5 825 1510 20 5 825

3 4 50 6

25

10 40

5 8

1 2

3 415

5

(2) 40又比25大交換

20

1 2

1510 20 5 20825 1510 20 5 20825

3 4 50 6

0

6 6

40

10 25

5 8

1 2

3 415

5

(3)

(4)再放入40

20

1 2

1510 25 5 20825 1510 25 5 20825

3 4 50 6

0

6

1 2

1510 25 5 20840 1510 25 5 20840

3 4 50 6

Page 71: 資料結構與演算法

11-6 堆積• 由上一頁圖 1和圖 2的說明可得知:

– 新加入的節點與其父節點比較大小,如果比父節點大,則將父節點的資料放在新節點的編號位置(X),其父節點的編號 =└(X-1)/2┘,即 (X-1)/2的商數。例如圖 1的 X=5,其父節點編號 =└(5-1)/2┘=└4/2┘=2 。而圖 2的 X=6,其父節點編號 =└(6-1)/2┘=└5/2┘=2 。

– 如果步驟 (1) 中的父節點資料有拷貝到新節點時,則新加入的節點還須一直往上找尋其父節點是否比它還小,重複步驟 (1) 的程序,直到父節點比它大為止,才將新節點的資料放在正確位置。

Page 72: 資料結構與演算法

• 刪除最大堆積中的某節點• 當我們要刪除最大堆積中的某節點時,原來的堆積要做調整才能維持最大堆積的特性,一般的做法是根據被刪除的節點位置,往下找其左右子節點,看哪個大,往上移到此被刪除的位置,再以此累推,依序檢查是否符合堆積的特性,直到維持堆積的特性或到達最底層為止。其操作步驟如下:– 1. 取得被刪除節點的位置(例如 index)和資料(例如 X)。– 2.取得最後節點的資料,例如 xend,先假想目前 index位址

的資料是 xend。– 3.編號 index節點的左右子節點的編號為

2*index+1, 2*index+2。現在比較 xend、左子節點的資料、右子節點的資料三者誰大。• (a)xend最大:表示 xend放在 xindex編號時,可維持堆積的特性,程序進入步驟 4 。

• (b)如果左子節點最大:將左子節點移到 index編號的位置,且 index用 2*index+1取代,重複步驟 3 的程序。

• (c)如果右子節點最大:將右子節點移到 index編號的位置,且 index用 2*index+2取代,重複步驟 3 的程序。

– 4.最後 index編號節點的資料放 xend。

Page 73: 資料結構與演算法

最大堆積經常運用刪除根節點的方法取出最大值,再維持最大堆積樹,根據上述演算法步驟的說明,其圖解說明如下圖。

40

10 25

5 8 2015

最後一個放到根節點

40 10 25 5 8 15 200 1 2 3 4 5 6

0

1 2

3 4 5 6

20

10 25

5 8 15

40 10 25 5 8 15 200 1 2 3 4 5 6

0

1 2

3 4 5

(1) (2)

25較大,25放到根節點

xend=2020和10,25比較

(3)

25

10 20

5 8 15 重覆(2)的程序,直到節點的資料比左右子節點的資料大為止25 10 25 5 8 15 20

0 1 2 3 4 5 6

0

1 2

3 4 5

xend=20

(4)

25

10 20

5 8 15

25 10 20 5 8 150 1 2 3 4 5 6

0

1 2

3 4 5

刪除根節點

Page 74: 資料結構與演算法
Page 75: 資料結構與演算法

• 堆積樹的應用 -優先佇列• 由 11-2 節我們知道一個有 n 個節點的堆積樹,其最高階度為 log2n(假如

n=2k-1時,有 k 層),而堆積樹新增節點或刪除節點都是在階層之間的移動節點,所以最差情況需移動最大階層時,其時間複雜度是 O(log n)。

• 另外最大堆積樹節點間有階層低的節點資料不小於左右子節點的特性,當我們要取得較大(或最大)的資料,且刪除它;另外也可以加入新的節點時,則可應用最大堆積樹。例如作業系統所使用優先佇列( priority queue)。

• 一般的佇列是先進先出的特性,在佇列中的元素,先到先處理,不會根據各個元素的優先率(或稱權重)來處理,如果要用一般的佇列來達成優先率高的先處理時,其作法是由佇列中找出優先率最高者,假如有 n 個元素,未經排列且用線性搜尋法時,其時間複雜度為 O(n),當 n 很大時,則堆積的 O(log n)比線性搜尋的 O(n)效率高許多,如下圖。

A

B C

D E F G

G F E D C B A40 50 30 40 60 80 100

(1) 找出最大者,所需時間O (n)

(2) 刪除節點,所需時間O (n)

(3) 新增資料,所需時間O (n)

合計 O (n)

元素優先率

(1) 找出最大者,所需時間O(1)

(2) 刪除節點,所需時間最差情況O (leg n)

(3) 新增資料,所需時間最差情況O (leg n)

合計 O (leg n)

堆積(優先順序)

100

80 60

40 30 50 40

Page 76: 資料結構與演算法

11-6 堆積• 優先佇列在電腦系統中,應用的情況很普遍,例如列印佇列、中央處理器的工作排程、記憶體可用空間管理、硬碟連續儲存空間管理等。

• 列印佇列是指要列印的資料皆會送到作業系統所提供的印表機佇列中,由列印管理程式管理印表機應該優先列印哪份資料,如果大家的優先率一樣時,以先到先服務為原則,有優先率差別時,則先服務優先率高者。

• 中央處理器的工作排程,除了依據優先率來處理之外,在有分時( time sharing)功能的作業系統中,希望每個處理程序( process)皆能在一定的時間週期內被服務到,因此一般作業系統有一種「最短時間工作優先」( shortest-job-first)的排程策略,即可應用最小堆積快速地取出需最短時間的處理程序,且新加入的處理程序可以用最短的時間來維護其優先佇列。

Page 77: 資料結構與演算法

• 堆積排序法• 當有 n 個資料要排序時,我們從第七章所介紹的各種排序法得知最佳的

時間複雜度是 O(n log n),例如快速排序法等。堆積的特殊結構稍加以變化,也可以得到一個 O(n log n)的排序演算法,此利用堆積結構的演算法稱為堆積排序法( heap sort)。

• 堆積排序法能夠達到 O(n log n)的等級,主要是因為建立一個堆積時,最差情況是 n 個 O(log n),而且刪除節點的最差情況也是 O(log n),考慮所有 n 個節點都要處理一遍時,則最差情況為 O(n log n)。堆積排序法是用到 11-6-2 的兩個演算法。

• 因為最大堆積的根節點一定是堆積中所有節點的最大值,如果我們用另一陣列來存放抽出的最大值,可依你的需要由小到大放置或由大到小放置,但是一般堆積排序法是強調不多佔用其他空間,所以我們只好把最大值放到原堆積陣列的最後一個位置,則依此累推可得到由小到大的排序結果。

• 【結論】 – 要得到由小到大的排序,需建立最大堆積;– 要得到由大到小的排序,需建立最小堆積。

• 假設一陣列已存放了要排序的資料,則堆積排序演算法可分為兩部份:– 建立堆積。– 以刪除根節點(和最後位置調換)的方法排序。

Page 78: 資料結構與演算法

建立最大堆積建立堆積且不希望用到多餘的儲存空間時,可利用 11-6-2 節加入新節點的演算法,加上迴圈控制目前的節點數,即第 1次只有 1節點,第 2次只有 2節點;第 3次只有3個節點,以此累推。尚未作用的節點,其陣列資料未做任何異動,建立最大堆積的流程如下圖說明。

21 31 71 61 51 11 410 1 2 3 4 5 6

原始資料陣列X陣列指標

建立堆積(1)

(2)

(3)

210 210 1 2 3 4 5 6

未變

21

31

0

1

31

2131比21大

31 210 1 2 3 4 5 6

31

21

0

1

71比31大交換

712

71

21

0

131

2

71 21 310 1 2 3 4 5 6

(4)

71

21

0

1 61比21大交換

312

71 61 31 210 1 2 3 4 5 6

613

71

61

0

131

2

213

OK

(5)

71

61

0

1 312

213 514

71 61 31 21 510 1 2 3 4 5 6

Page 79: 資料結構與演算法
Page 80: 資料結構與演算法

由最大堆積作由小到大排序現在由圖 11-27 所建立的最大堆積,只要每次抽出根節點,再維護剩餘的節點為堆積,重複這樣的程序,即可得到由大到小的排序。但若是要在陣列資料中作排序,則要將根節點的和作用中堆積的最後一個位置交換,例如最剛開始有 7個元素,所以根節點(索引指標 0 )和最後一個(索引指標 6 )交換,此時剩下的作用中堆疊只有 6個元素(最後一個最大值不算),此 6個元素依 11-6-2 節介紹的刪除根節點,把最後一個元素移到根節點再維護堆積的作法一樣。

71 61 41 21 51 11 31

0 1 2 3 4 5 6

原最大推積 71

61 41

21 51 3111

0

1 2

3 4 5 6

索引指標

最大推積陣列

X 71 61 41 21 51 11 31

0 1 2 3 4 5 6

原最大推積 71

61 41

21 51 3111

0

1 2

3 4 5 6

索引指標

最大推積陣列

X

(1)

31

61 41

21 51 7111

0

1 2

3 4 5 6

61最大往上移

61

31 41

21 51 7111

61 51 41 21 31 11 710 1 2 3 4 5 6

根(71)和最後一個(31)交換,重新整理成最大堆積樹

51最大

61

51 41

21 31 7111

作用中 不作用

Page 81: 資料結構與演算法

(2)

11

51 41

21 31 7161

61最大交換

51

11 41

21 31 7161

51 31 41 21 11 61 710 1 2 3 4 5 6

根(61)和作用中最後一個(11)交換

31最大交換

51

31 41

21 11 7161

(3)

11

31 41

21 51 7161

41最大交換

41

31 11

21 51 7161

41 31 11 21 51 61 710 1 2 3 4 5 6

根(51)和作用中最後一個(11)交換

(4)

21

31 11

41 51 7161

31最大交換

31

21 11

41 51 7161

31 21 11 41 51 61 710 1 2 3 4 5 6

根(41)和作用中最後一個(21)交換

Page 82: 資料結構與演算法

(5)

11

21 31

41 51 7161

21最大交換

21

11 31

41 51 7161

21 11 31 41 51 61 710 1 2 3 4 5 6

根(31)和作用中最後一個(11)交換

(6)

11

21 31

41 51 7161

只剩一個元素=>結束 11 21 31 41 51 61 710 1 2 3 4 5 6

根(21)和作用中最後一個(11)交換

Page 83: 資料結構與演算法

11-7 二元搜尋樹• 二元搜尋樹• 上一節介紹的堆積是具有父子節點之間的關係,可看成是垂直方向的關係,本節要介紹的二元搜尋樹 (Binary Search Tree)則是節點和左子樹、右子樹之間的大小關係、可以看成是水平方向的關係。二元搜尋樹簡單定義為:「二元樹中任意節點 x,其左子樹中所有節點元素皆小於x,其右子樹中所有節點元素皆大於 x」。二元搜尋樹和堆積的比較如下一頁附圖。

Page 84: 資料結構與演算法

XY是子數中最大值

Z是子數中最小值

Y<X

Y Z

Z>X

水平間條

1 4 6 8

3 7

5

10

13

11 15

XY是子數中最大值

Z是子數中最大值

Y<X

Y Z

Z<X

3 8 10 5

15 25

30

40

20

16 6

垂直間條

二元搜尋樹 堆積

Page 85: 資料結構與演算法

• 二元搜尋樹類似於堆積的結構,也是節點之間有一定的大小關係,當你做新增節點到二元搜尋樹,或從二元搜尋樹刪除節點的操作時,還是得維持二元搜尋樹得大小關係。

• 【定義】:二元搜尋樹• 二元搜尋樹是一顆二元樹,可能為空二元樹,若不是空二元樹,則滿足

下列條件:– 1.所有節點的資料皆相異。– 2.左子節點的資料比父節點的資料小。– 3.右子節點的資料比父節點的資料大。– 4.由左、右節點為樹根的左、右子樹也具備二元搜尋樹的條件。

10

15

25

30

10

28

25

30

38

35

40

30

是二元搜尋樹

20

25 35

40

30

20

5515 35

40

30

35比30大

不是二元搜尋樹

20

15

Page 86: 資料結構與演算法

• 二元搜尋樹的特性• 二元搜尋樹是一顆二元樹,所以二元搜尋樹具備所有二元樹的特性,其中比較重要的是階層數的特性。如果一個二元搜尋樹有 n 個節點,則其階層數(高度) h滿足下列不等式:

┌ log2(n+1) ┐ ≦ h ≦ n

• 當 h=n時,代表每個非終端節點只有 1個分支,二元搜尋樹稱此為最大高度,以n 為例,如下圖。

• 當 h=┌ log2(n+1) ┐時,例如 n=5 ==> h=3, n=6 ==> h=3, n=7 ==> h=3, n=8 ==> h=4,以此推累。代表每個非終端節點都儘可能有 2個分支,此稱為最小高度二元搜尋樹,要高度最小,則類似於完整二元樹或完滿二元樹,以 n=5為例,最小高度為 3,如下圖

20

30

40

50

43

45

40

50

1044

80

70

60

50

90

75

70

80

50

74

40

70

50

非完整二元樹,但一定高度最小

30

20

完整二元樹

60

70

50

30

10

70

50

30

9010 60

70

50

30

90

Page 87: 資料結構與演算法

• 二元搜尋樹的搜尋• 二元搜尋樹的最大特點是某節點的左子樹所有節點的資

料皆比此節點的資料小,而右子樹的情況則相反。所以二元搜尋樹最適合用於搜尋,且可經由安排搜尋資料的次序,可搜尋出由小到大或由大到小的排序功能。

• 二元搜尋樹在搜尋某資料 k 時,其步驟如下:• 先將根節點的資料 m 比較:

– (1)k = m =>搜尋成功。– (2)k > m =>

• (a)如果是非終端節點:搜尋右子樹,再重回步驟 1 ,原根節點換成右子樹的根節點。

• (b)如果是終端節點:結束,搜尋未成功。– (3)k < m =>

• (a)如果是非終端節點:搜尋左子樹,再重回步驟 1 ,原根節點換成左子樹的根節點。

• (b)如果是終端節點:結束,搜尋未成功。• 範例圖示在下一頁

Page 88: 資料結構與演算法

25 35

40

30

20比30小

20

5015

搜尋30 搜尋20

25 35

40

30

20

5015

Bingo

25 35

40

30

20

5015

Bingo

搜尋45

25 35

40

30

20

501525 35

40

30

20

5015

35>30

35<40

搜尋35

45>30

45>40

Bingo 找不到

Page 89: 資料結構與演算法

11-7 二元搜尋樹01020304050607080910111213141516

/* 演算法名稱:二元搜尋樹的搜尋節點*/

/* 輸入:二元搜尋樹中要搜尋的節點 *//* 輸出:二元搜尋樹要搜尋節點的位置

*/ bintree_search(tree){

ptr = tree;while (ptr != null && key != ptr->data){

if(key<ptr->data) /* 如果 key此節點要小 ,看左邊 */ptr=ptr->left;

else /* 否則 ,看右邊 */ptr=ptr->right;

}return(ptr);

}

Page 90: 資料結構與演算法

• 二元搜尋樹與二元樹、堆積、二分搜尋法比較• 二元樹和二元搜尋樹比較• 由圖 11-33 得知,二元搜尋樹搜尋一個資料時,一直往下搜尋,如果高度為 h,

則最多只要搜尋 h 次,且┌ log2(n+1) ┐ ≦ h ≦ n,所以一般稱二元搜尋樹的平均時間為 O(log n),而一般二元樹的前序法、中序法、後序法走訪來搜尋資料時,其最差情況是每個節點都要走訪過,時間是 n,平均是 n/2,其時間複雜度為 O(n)。

• 二元搜尋樹和堆積比較• 二元搜尋樹尋找某資料的平均時間是 O(log n),而堆積只具備某節點的資料比

左、右子樹中所有的節點的資料大的性質,也是要用前序法、中序法、後序法走訪來尋找某資料,所以堆積的平均搜尋時間也是 O(n)。

• 但是在搜尋最大值時,最大堆積只是 O(1),因為根節點就是最大值的節點。而二元搜尋樹的最大值則要一直往右子樹找到終端節點,搜尋最小值則要一直往左子樹找到終端節點,其平均時間即為高度 h,平均時間複雜度是 O(log n)。二元搜尋樹尋找最大值和最小值的方式如下圖。

7 11

13

10

5

153

1 4 6 8

往右子樹尋找直到終端節點

第2大值

此終端節點是最大值

往左子樹尋找直到終端節點

第2小值

此終端節點是最小值

Page 91: 資料結構與演算法

• 二元搜尋樹的搜尋和二分搜尋法比較• 討論二元搜尋樹的目的是此樹非常適合用於搜尋(故以此命名),其搜

尋方式如同二分法搜尋一樣,都是先比較某點,即可決定接著要比較的範圍是左子樹(二分搜尋法是左邊範圍)或右子樹(二分搜尋法是右邊範圍),二分搜尋法平均時間為 O(log n),而二元搜尋樹在最大高度情況來討論時,其平均搜尋時間為 O(n),此為最差情況;以最小高度情況來討論時,其平均搜尋時間為 O(log n)。這可看出縮小二元搜尋樹的高度對於此樹的程式運作是很重要的 (註:平衡樹 [AVL tree]可縮小二元搜尋樹的高度,且維持二元搜尋樹的特性,本書因篇幅關係,在此不作介紹 ) 。

Page 92: 資料結構與演算法

• 二元搜尋樹應用於排序• 由上一節尋找二元搜尋樹中最大值、最小值的方法得知,最大值是樹狀

圖形最右邊的節點,第 2大值是其最大節點的父節點。同理最小值是樹狀圖形最左邊的節點,第 2小直是最小值節點的父節點,如先前二頁的圖所示。

• 所以當我們要搜尋由小到大的值時,可用前序法走訪二元搜尋樹所有節點,可得到由小到大的排序資料,如下圖。

• 同理,要搜尋由大到小的值時,可使用類似前序法的走訪,但是左子樹、右子樹的搜尋次序要相反,如下圖。

7 11

13

10

5

153

1 4 6 8

前序法走訪:先一路往左,再中間,再右邊,得到

1, 3, 4, 5, 6, 7, 8, 10, 11, 13, 15

7 11

13

10

5

153

1 4 6 8

類似前序法走訪:先一路往右,再中間,再左邊,得到

15, 13, 11, 10, 8, 7, 6, 5, 4, 3, 1

Page 93: 資料結構與演算法

• 建立二元搜尋樹與新增資料– 二元搜尋樹的建立可依尋找節點資料的方法來建立,假設二元

搜尋樹中的資料皆相異時,則每次皆搜尋不到,但以最後的終端節點連結此新節點,而建立二元搜尋樹,其步驟如下:

– 1. 假如是空樹,建立根節點。– 2. 將新增節點的資料 k 與根節點的資料 m 相比較:

• (1)k > m – (a)如果是非終端節點:搜尋右子樹,原根節點換成右子樹的根節點,重回步驟 2 。

– (b)如果是終端節點:新節點建立成此節點的右子節點。• (2)k < m

– (a)如果是非終端節點:搜尋左子樹,原根節點換成左子樹的根節點,再重回步驟 2 。

– (b)如果是終端節點:新節點建立成此節點的左子節點。

• 範例在下一頁

Page 94: 資料結構與演算法

資料的順序 20,40,15,35,25,30,50

(1) 20,空樹,建立根節點 20

(2) 40,因40>20,建立右節點

40

20

(3) 15,因15<20,建立左節點

40

20

15

(4) 35,因35>20,往右尋找

因35<40,建立左節點 40

20

15

35

(5) 25,因25>20,往右尋找

因25<40,往左尋找

因25<35,建立左節點40

20

15

35

25

(6) 30,因30>20,往右尋找

因30<40,往左尋找

因30<35,往左尋找

因30>25,建立右節點

40

20

15

35

2530

(7) 50,因50>20,往右尋找

因50>40,建立右節點 40

20

15

35

25

30

50

Page 95: 資料結構與演算法

資料的順序 20,40,15,35,30,25,50

(1) 20,空樹,建立根節點 20

(2) 40,因40>20,建立右節點

40

20

(3) 15,因15<20,建立左節點

40

20

15

(4) 35,因35>20,往右尋找

因35<40,建立左節點 40

20

15

35

(5) 30,因30>20,往右尋找

因30<40,往左尋找

因30<35,建立左節點40

20

15

35

30

(6) 25,因25>20,往右尋找

因25<40,往左尋找

因25<30,往左左節點

40

20

15

35

30

25

(7) 50,因50>20,往右尋找

因50>40,建立右節點 40

20

15

35

30

25

50

與圖11-39不同的地方

與圖11-39相同

Page 96: 資料結構與演算法

當要建立二元搜尋樹的資料已經排好順序了,則所建立的二元搜尋樹是左斜二元樹(由大到小的資料項)或是右斜二元樹(由小到大的資料項)。這種已排序好的資料項將會造成 1+2+…+(n-1)的比較次數,總和等於 n(n-1)/2,時間複雜度是 O(n2),而圖 11-37 和圖 11-38 等非排序過的資料項,其平均時間複雜度是 O(n log n)。已排序好的資料項建立二元搜尋樹的圖解說明如下圖。

資料項由小到大排序(15,20,25,30,35,40,50)

所建立的二元搜尋樹

20

15

25

30

35

40

50

資料項由大到小排序(50,40,35,30,25,20,15)

所建立的二元搜尋樹40

50

35

30

25

20

15

Page 97: 資料結構與演算法
Page 98: 資料結構與演算法

11-7 二元搜尋樹01020304050607080910111213141516171819202122232425262728

/* 演算法名稱:二元搜尋樹的新增節點*/

/* 輸入:新增節點到二元搜尋樹 *//* 輸出:新增節點後的二元搜尋樹 */ search_and_add(tree, key){

q = null;ptr = tree;while (ptr != null){

if (key == ptr->data)return(p);

q = ptr; /* 把 q設定為 ptr節點 *//* ptr節點往下看 */if(key<ptr->data) /* 如果 key此節點要小 ,看左邊 */

ptr=ptr->left;else /* 否則 ,看右邊 */

ptr=ptr->right;}v = maketree(key); /* 產生一個新的節點 */if (q == null)

tree = v;else if (key < q->data)

q->left = v;else

q->right = v;return(v);

}

Page 99: 資料結構與演算法

11-7 二元搜尋樹• 刪除二元搜尋樹的節點• 一顆已建立的二元搜尋樹同樣會因程式的運作,增加節點或刪除節點,當要刪除的節點是終端節點時,只要將其父節點原先對此節點的連結去除(指向 null)即可。

• 如果要刪除的節點不是終端節點,則被刪除的節點要以最適合的節點來放入此被刪除節點的位置,最適合的節點有兩個可能:– 1.其左子樹的最右邊節點(因為是左子樹的最大值)。– 2.其右子樹的最左邊節點(因為是右子樹中的最小值)。

Page 100: 資料結構與演算法

圖解說明如下圖

m

Ln R1

關係(1) L子樹中的所有資料 <m(2) R子樹中的所有資料 >m(3) Ln是L子樹的最右邊節點,Ln是

L子樹的最大值(4) R1是R子樹的最左邊節點, R1是

R子樹的最小值

L R

...< Ln < m < R1 < ...

L子樹 R子樹

P情況1: Ln取代m

Ln

R1

L’ R ... < Ln < R1 < ...

L’子樹 R子樹

P

符合二元搜尋樹定義

情況2: R1取代m

R1

Ln

L R’

L子樹 R’子樹

P

符合二元搜尋樹定義

...< Ln < R1 < ...

•當你去除 m 時,不管是 Ln 或是 R取代其位置,以 R 為例,此時你要做的事情有:

•1.將原來 R1 的父節點對 R1 的連結設成空連結。•2.將原來 m 的父節點 p 連結到 m 的連結,改為連結到 R1。•3.將原來 m 的左節點,改為 R1 的左連結,將原來 m 的右連結改為 R1 的右連結。

•經過上述三個步驟的處理,即可維護一個二元搜尋樹,且找到 LR 或 R1 點的次數最多是高度 h的次數。所以維護一個二元搜尋樹的平均時間是O(log n) ,最差情況是 O(n) 。

Page 101: 資料結構與演算法
Page 102: 資料結構與演算法

11-7 二元搜尋樹•有關二元搜尋樹的刪除節點演算法範例程式請參考本書 11-7-7 刪除二元搜尋樹的節點