chapter 17 - cs.pu.edu.twbcc/982course/f7811_ch17.pdf · 17-2 基礎資料結構 •...

158
Chapter 17 資料結構與演算法 1

Upload: others

Post on 27-Sep-2019

0 views

Category:

Documents


0 download

TRANSCRIPT

  • Chapter 17

    資料結構與演算法

    1

  • 學習目標

    • 認識資料結構與演算法的定義• 熟悉佇列與堆疊的原理與應用• 熟悉二元樹的原理與應用• 瞭解常用排序與搜尋方法的原理與應用• 瞭解演算法的分析方法• 瞭解基本演算法設計策略

    第17章 資料結構與演算法

    2

  • 大綱

    • 17-1 虛擬碼• 17-2 基礎資料結構• 17-3 基礎演算法• 17-4 演算法分析與策略

  • CH17 資料結構與演算法

    • 資料結構是指資料在電腦內的表示方式和存取方法。

    • 演算法就是電腦處理資料的詳細流程。• 資料結構與演算法要相互配合才能有效率的處理資料。

    • 每個資料結構都有其對應的存取使用方法,演算法設計必須搭配資料結構設計才能達到最佳表現。

  • 17-1 虛擬碼

    • 「流程圖」 (Flow Chart):用來描繪虛擬碼動作。• 流程圖是由一些圖形所組成,主要的圖形如下圖所示。

    動作 圖形

    運算 / 處理 /轉換

    決策

    邏輯流向

    是判斷條件

  • 17-1 虛擬碼

    以下介紹幾個主要的「虛擬碼」(pseudo code)指令。

    • READ– READ讀入資料儲存於某變數。– 其語法格式如下:

    • PRINT– PRINT指令用來輸出資料。– 語法格式如下:PRINT 變數名稱或訊息

    READ 變數名稱

  • 17-1 虛擬碼

    • IF-THEN– IF-THEN指令在選擇或決策使用。– 語法格式為:

    條件式

    指令敘述

    否 IF 條件式 THEN

    指令敘述 ENDIF

  • 17-1 虛擬碼

    8

    範例:

    虛擬碼 流程圖

    IF A>0 THEN B=C+D

    C=D+F

    ENDIF

    E=F+G

    A > 0

    E=F+G

    B = C + D C=D+F

  • 17-1 虛擬碼

    • IF-THEN-ELSE– 此指令在選擇或決策時使用。– 語法格式為:

    條件式

    成立

    指令敘述 1

    指令敘述 2

    IF 條件式 THEN

    指令敘述 1 ELSE

    指令敘述 2 ENDIF

  • 17-1 虛擬碼

    10

    範例:

    虛擬碼 流程圖

    IF A>0 THEN

    A=A+B

    ELSE

    B=C+D

    ENDIF

    C=C+D

    A = A + B B= C + D

    A > 0

    C = C+ D

  • 17-1 虛擬碼

    • FOR-LOOP– 此指令在需重複執行指令時使用。– 語法格式為:FOR 控制變數=初值 TO 終值 SETP 累加值

    指令敘述 END FOR

  • 17-1 虛擬碼

    • FOR-LOOP

    控制變數 ≤ 終值

    指令敘述

    控制變數=控制變數+累加值

    控制變數=初值

    累加值為正值或 0 時:

    控制變數 ≥ 終值

    指令敘述

    控制變數=控制變數+累加值

    控制變數=初值

    累加值為負值或 0 時:

  • 17-1 虛擬碼

    • 例如要重複印出五個A,利用FOR-LOOP寫出的虛擬碼如下:

    虛擬碼 流程圖

    FOR I=1 TO 5 STEP 1 PRINT “A”

    END FOR

    I=1

    PRINT A

    I= I+1

    I ≤5

  • 17-1 虛擬碼

    • WHILE-LOOP– 此指令在需重複執行指令時使用。– 語法格式為:

    • 可以把前面FOR- LOOP的例子改為WHILE-LOOP:

     

    WHILE 條件運算式

    不成立

    指令敘述成立…

    I=1

    WHILE I

  • 17-1 虛擬碼

    • EXIT– EXIT指令表示停止執行虛擬碼。– 語法格式為:

    • EXIT FOR– EXIT FOR指令將停止執行目前的FOR-LOOP,並繼續執行目前FOR-LOOP後的第一個指令。

    – 語法格式為:

    EXIT

    EXIT FOR

  • 17-1 虛擬碼

    • EXIT WHILE– EXIT WHILE指令表示停止執行目前的WHILE-LOOP,並繼續執行目前WHILE-LOOP後的第一個指令。

    – 語法格式為:EXIT WHILE

  • 17-1 虛擬碼

    本章演算法之格式:

    • 格式中的演算法描述可以虛擬碼敘述,或以一步一步的步驟式方式敘述之。

    • 本章將演算法描述格式如下:

    演算法:名稱輸入:輸入資料輸出:輸出資料BEGIN演算法描述END

  • 17-2 基礎資料結構

    • 陣列 (Array):– 是最常見的資料結構之一。– 當一個資料需儲存到主記憶體時,可以指定一個變數來儲存,但若要同時處理100個同樣類型資料時,指定100個不同變數名稱,在使用上就相當不便了,此時就能利用陣列來處理。

    – 陣列是記憶體的變數名稱,它是一群具有相同性質的資料型態之集合,而各個元素以不同的註標 (Index) 值來區分。

  • 17-2 基礎資料結構

    • 一維陣列:– 欲儲存10個人的平均成績79,80,55,75,99,65,70,68,90,74,可以使用名稱為SCORE的陣列,而各元素的變數名稱分別為SCORE[1],SCORE[2],SCORE[3],...,SCORE[10],其陣列的表示如下:

    1 2 3 4 5 6 7 8 9 10

    79 80 55 75 95 65 70 68 90 74

    SCORE[1] SCORE[2] SCORE[3] SCORE[4] SCORE[5] SCORE[6] SCORE[7] SCORE[8] SCORE[9] SCORE[10]

  • 17-2 基礎資料結構

    • 上述以一個註標來存取元素的陣列稱為一維 (One Dimension) 陣列,以n個註標來存取元素的陣列稱為n維陣列。

    第一個學生 79 80 55 75 95 65 70 68 90 74

    第二個學生 80 85 40 80 85 90 95 93 70 60

    第三個學生 90 60 90 85 90 75 79 85 88 90

    第四個學生 60 90 80 75 95 75 80 83 86 90

    考試

    考試

    考試

    考試

    考試

    考試

    考試

    考試

    考試

    考試

  • 17-2-1 佇列與堆疊

    • 當我們買票看電影時,大家依據到達的先後次序排隊買票,先來的先買票進場;但是當我們清洗餐盤時,常是把洗完的盤子直接就放在之前洗好的盤子的最上面,等到要取用盤子時會先拿最上面的盤子,反而是後放上去的盤子先使用。

    • 前者排隊買票是「先進先出」 (First In First Out,FIFO) 的處理觀念,而後者使用盤子是「後進先出」 (Last In First Out,LIFO) 的處理觀念。接下來介紹的佇列和堆疊分別應用了FIFO和LIFO的處理觀念。

    21

  • 17-2-1 佇列與堆疊

    • 佇列 (Queue) 是一個有順序的資料列,其資料列有兩端,分別稱為「頭端」 (Front) 和「尾端 」(Rear) (如圖 17.4)。資料可「加入」(Add) 佇列中或從佇列中「刪除」 (Delete),但是加入的資料只能加在資料列的尾端,而刪除資料卻是除去資料列頭端的資料。

    22

    頭端 尾端

    A B C D E F

    圖 17.4

  • 17-2-1 佇列與堆疊

    • 假設 Q 是一佇列,以 ADD (Q, a) 表示將 a 加入佇列 Q 中,以 DELETE (Q) 表示從佇列 Q 刪除資料。若佇列 Q 內有 3, 9, 7, 8,四個資料,其資料列的頭端是3,尾端是8 (見圖17.5 (a))。

    23

    頭端 尾端

    3 9 7 8

    頭端 尾端

    3 9 7 8 5

    頭端 尾端

    3 9 7 8 5 2

    (a) (b) (c)

    ADD(Q, 5) Q ADD(Q, 2)

    DELETE(Q)

    頭端 尾端

    9 7 8 5 2

    (d)

    DELETE(Q)

    頭端 尾端

    7 8 5 2

    (e)

  • 17-2-1 佇列與堆疊

    • 如果將 5 加入 Q 中,則 5 將加在尾端 8 之後,而使 5 成為新的尾端 (圖 17.5 (b))。

    24

    頭端 尾端

    3 9 7 8

    頭端 尾端

    3 9 7 8 5

    頭端 尾端

    3 9 7 8 5 2

    (a) (b) (c)

    ADD(Q, 5) Q ADD(Q, 2)

    DELETE(Q)

    頭端 尾端

    9 7 8 5 2

    (d)

    DELETE(Q)

    頭端 尾端

    7 8 5 2

    (e)

  • 17-2-1 佇列與堆疊

    • 再加入 2,則佇列 Q 的資料列成為 3, 9, 7, 8, 5, 2 (圖 17.5 (c))。

    25

    頭端 尾端

    3 9 7 8

    頭端 尾端

    3 9 7 8 5

    頭端 尾端

    3 9 7 8 5 2

    (a) (b) (c)

    ADD(Q, 5) Q ADD(Q, 2)

    DELETE(Q)

    頭端 尾端

    9 7 8 5 2

    (d)

    DELETE(Q)

    頭端 尾端

    7 8 5 2

    (e)

  • 17-2-1 佇列與堆疊

    • 若此時從佇列 Q 刪除資料,將會刪除其頭端資料 3,而使 9 成為新的頭端 (圖17.5 (d))。

    26

    頭端 尾端

    3 9 7 8

    頭端 尾端

    3 9 7 8 5

    頭端 尾端

    3 9 7 8 5 2

    (a) (b) (c)

    ADD(Q, 5) Q ADD(Q, 2)

    DELETE(Q)

    頭端 尾端

    9 7 8 5 2

    (d)

    DELETE(Q)

    頭端 尾端

    7 8 5 2

    (e)

  • 17-2-1 佇列與堆疊

    • 再刪除一次,則資料列成為 7, 8, 5, 2 (圖17.5 (e))。

    27

    頭端 尾端

    3 9 7 8

    頭端 尾端

    3 9 7 8 5

    頭端 尾端

    3 9 7 8 5 2

    (a) (b) (c)

    ADD(Q, 5) Q ADD(Q, 2)

    DELETE(Q)

    頭端 尾端

    9 7 8 5 2

    (d)

    DELETE(Q)

    頭端 尾端

    7 8 5 2

    (e)

  • 17-2-1 佇列與堆疊

    • 下面分別是ADD 和 DEL兩個演算法,其中 ADD 將 ITEM 加入最多有 N 個資料的佇列 Q 中,而DEL 是從最多有 N 個資料的佇列 Q 中刪除一個資料,並以 ITEM 紀錄此刪除的資料。

    • 此處以一維陣列來儲存佇列 Q,並以 FRONT 及REAR 分別表示頭端及尾端的位置。

    28

  • 17-2-1 佇列與堆疊

    29

    演算法:ADD (ITEM, Q[], N, REAR)

    輸入:ITEM, Q[], N, REAR

    輸出:Q[], REAR 1 BEGIN

    2 IF REAR = N THEN

    3 PRINT “QUEUE IS FULL”

    4 EXIT

    5 END IF

    6 REAR=REAR+1

    7 Q[REAR] = ITEM

    8 END

  • 17-2-1 佇列與堆疊

    30

    演算法:DEL (ITEM, Q[], FRONT, REAR)

    輸入:Q[], FRONT, REAR

    輸出:ITEM, Q[], FRONT 1 BEGIN

    2 IF FRONT = REAR THEN

    3 PRINT “QUEUE IS EMPTY”

    4 EXIT

    5 END IF

    6 FRONT = FRONT + 1

    7 ITEM = Q[FRONT]

    8 END

  • 17-2-1 佇列與堆疊

    • 佇列常使用於作業系統中,如工作佇列 (Job Queue)、列印佇列 (Printer Queue) 等。

    31

  • 17-2-1 佇列與堆疊

    • 堆疊 (Stack) 也是一個有順序的資料列,其資料列兩端的一端稱為頂端 (Top) (如圖17.6)。資料一樣可以加入 (Push) 堆疊中或從堆疊中刪除 (Pop),可是加入的資料只能放於資料列的頂端,而刪除資料也是目前資料列頂端的資料。

    32

    B

    頂端 C

    A 堆疊

    圖17.6

  • 17-2-1 佇列與堆疊

    • 假設 S 是一堆疊,以PUSH (S, a) 表示將 a 加入堆疊S中,以 POP (S) 表示從堆疊 S 刪除資料。若堆疊 S 內有 1, 5, 3 三個資料,其資料列的頂端是3 (見圖17.7 (a))。

    33

     

    PUSH(S ,9) PUSH(S, 2)

    頂端3

    S

    51

    (a)

    9 頂端

    3

    S

    51

    (b)

    頂端2

    S

    9351

    (c)

    9 頂端

    3

    S

    51

    (d)

    頂端3

    S

    51

    (e)

    POP(S)

    POP(S)

  • 17-2-1 佇列與堆疊

    • 如果將9 加入 S 中,則 9 將加在頂端 3 之上,使 9 成為新的頂端 (圖17.7 (b))。

    34

    PUSH(S ,9) PUSH(S, 2)

    頂端3

    S

    51

    (a)

    9 頂端

    3

    S

    51

    (b)

    頂端2

    S

    9351

    (c)

    9 頂端

    3

    S

    51

    (d)

    頂端3

    S

    51

    (e)

    POP(S)

    POP(S)

  • 17-2-1 佇列與堆疊

    • 再加入 2,則堆疊 S 的資料列成為 1, 5, 3, 9, 2 (圖17.7 (c)),其中 2 為頂端。

    35

    PUSH(S ,9) PUSH(S, 2)

    頂端3

    S

    51

    (a)

    9 頂端

    3

    S

    51

    (b)

    頂端2

    S

    9351

    (c)

    9 頂端

    3

    S

    51

    (d)

    頂端3

    S

    51

    (e)

    POP(S)

    POP(S)

  • 17-2-1 佇列與堆疊

    • 若此時從堆疊 S 刪除資料,將會刪除其頂端資料 2,而使 9 成為新的頂端 (圖17.7 (d))。

    36

    PUSH(S ,9) PUSH(S, 2)

    頂端3

    S

    51

    (a)

    9 頂端

    3

    S

    51

    (b)

    頂端2

    S

    9351

    (c)

    9 頂端

    3

    S

    51

    (d)

    頂端3

    S

    51

    (e)

    POP(S)

    POP(S)

  • 17-2-1 佇列與堆疊

    • 再刪除一次,則資料列成為 1, 5, 3 (圖 17.7 (e)),其中 3 為頂端。

    37

    PUSH(S ,9) PUSH(S, 2)

    頂端3

    S

    51

    (a)

    9 頂端

    3

    S

    51

    (b)

    頂端2

    S

    9351

    (c)

    9 頂端

    3

    S

    51

    (d)

    頂端3

    S

    51

    (e)

    POP(S)

    POP(S)

  • 17-2-1 佇列與堆疊

    • 下面分別有PUSH 和 POP 的演算法,其中 PUSH將 ITEM 加入最多有 N 個資料的堆疊 S 中,而POP是從最多有 N 個資料的堆疊 S 中刪除一個資料,並以 ITEM 紀錄此刪除的資料。

    • 此處以一維陣列來儲存堆疊 S。

    38

  • 17-2-1 佇列與堆疊

    39

    演算法: PUSH ( S[], ITEM, N, TOP) 輸入:S[], ITEM, N, TOP 輸出:S[], TOP 1 BEGIN

    2 IF TOP >= N THEN

    3 PRINT "STACK IS FULL"

    4 EXIT

    5 END IF

    6 TOP = TOP + 1

    7 S[TOP] = ITEM

    8 END

  • 17-2-1 佇列與堆疊

    40

    演算法: POP (ITEM, S[], TOP) 輸入:S[], TOP 輸出:ITEM, S[], TOP 1 BEGIN

    2 IF TOP

  • 17-2-1 佇列與堆疊

    • 堆疊的應用很多,如程式執行、計算數學式等。以下以計算數學式為例說明堆疊的應用。

    • 假設我們要計算數學式 A + B*C 的值,依四則運算的規則,要先計算 B*C 的值後再加上 A。若用電腦計算 A + B*C,不能單單地只從左至右計算,需先將此算式轉換成「後序算式」(Postfix Expression),亦即將運算符號放到其運算對象的後方。例如A+B*C 的後序算式為ABC*+。

    41

  • 17-2-1 佇列與堆疊

    • 若給了一個後序算式,我們就能利用堆疊算出其值。

    • 從左至右輸入後序算式,當輸入的不是運算符號時,就將其加入堆疊中;若輸入的是運算符號,就連續從堆疊中刪除兩個資料,並以此二資料作為這運算符號的運算對象計算之,然後將計算結果再加入堆疊中。如此這般直到算式輸入完畢,最後位於堆疊頂端的資料就是此算式的值。

    42

  • 17-2-1 佇列與堆疊

    • 若現在要計算後序算式 352*+ 的值,首先設定好一空堆疊 (圖17.8 (a))。當輸入 `3'、`5'、`2' 時,因為都不是運算符號,所以都被加入堆疊中 (圖17.8 (b), (c), (d))。

    43

    頂端 S

    頂端

    S

    3 3

    頂端

    S

    53

    (a) (b) (c)

    頂端2

    S

    5

    3

    (d)

    *

    5

    頂端

    S

    10

    3 頂端

    S

    13

    (e) (f)

    +

    2

  • 17-2-1 佇列與堆疊

    • 當輸入 `*' 時,從堆疊刪除 `2' 和 `5' 兩個資料,然後作 `5*2' 的計算,得結果 10,再將 10 加入堆疊中 (圖17.8 (e))。

    44

    頂端 S

    頂端

    S

    33

    頂端

    S

    53

    (a) (b) (c)

    頂端2

    S

    5

    3

    (d)

    *

    5

    頂端

    S

    10

    3頂端

    S

    13

    (e) (f)

    +

    2

  • 17-2-1 佇列與堆疊

    • 最後輸入 `+' 時,從堆疊刪除 `10' 和 `3' 兩個資料,然後作 `3+10' 的計算,得結果 13,再將13 加入堆疊中 (圖17.8 (f))。所以 352*+ 的值是位於堆疊頂端的 13。

    45

    頂端 S

    頂端

    S

    33

    頂端

    S

    53

    (a) (b) (c)

    頂端2

    S

    5

    3

    (d)

    *

    5

    頂端

    S

    10

    3頂端

    S

    13

    (e) (f)

    +

    2

  • 17-2-2 樹與二元樹

    • 下圖是一張具有樹狀結構的家譜。• 李一是大家的共同祖先,其後有李二、李三和李四,李二之

    後有李五,李三之後有李六、李七和李八,李四之後有李九,而李七之後有李十。

    • 整張圖就像是以李一為樹根向外生長的一棵樹。

    46

  • 17-2-2 樹與二元樹

    • 「樹 」(Tree) 是一群有限個但至少一個「節點」(Node)所成的集合,其中某個節點被指定為樹根 (Root)。

    • 除了樹根以外的節點可分為 T1, ..., Tn 等 n 個不相交的集合 (n ≥ 0),而且每個集合也是一棵樹。T1, ..., Tn稱為樹根的「子樹」(Subtree)。

    • 若節點 N 沒有子樹,則稱節點 N 為「樹葉」(Leaf)節點N 的子樹的樹根稱為節點 N 的「孩子」(Child),而 N稱為其孩子的「父母」(Parent)。

    47

  • 17-2-2 樹與二元樹

    以右圖為例:

    • 「節點」有李一、...、李十,樹根是李一。

    • T1, T 2, T 3 是李一的三棵子樹,其樹根分別為李二、李三和李四。

    • 李五、李六、李十、李八和李九都是「樹葉」。

    • 李一的孩子有李二、李三和李四,李二、李三和李四的父母都是李一。

    48

  • 17-2-2 樹與二元樹

    二元樹 (Binary Tree):

    • 是每個節點至多兩個孩子的樹(如下圖)。我們稱節點左邊的孩子為左孩子,右邊的為右孩子。

    49

  • 17-2-2 樹與二元樹

    • 用右圖的 LCHILD、RCHILD 和 DATA 三個欄位表示節點。

    • 其中LCHILD和RCHILD分別是節點的左孩子和右孩子,DATA 是原來的節點資料。

    50

  • 17-2-2 樹與二元樹

    則可將圖:

    改成

    表示

    51

  • 17-2-2 樹與二元樹

    • 圖 17.13 是用所謂的「鏈結串列」(Linked List) 來表示二元樹。

    • 鏈結串列也可以用陣列表示。• 宣告 Data、Lchild 和 Rchild

    三個陣列,先將節點資料存入陣列 Data中,然後一一將節點的左孩子及右孩子在位於陣列Data的註標值分別填入到陣列Lchild和Rchild中。

    • 圖17.13的陣列表示方式如圖17.14所示。

    52

  • 17-2-2 樹與二元樹

    • 「追蹤」(Traversal)樹上的節點是對樹狀結構相當重要的運算。

    • 追蹤樹上的節點,就是拜訪每個節點恰好一次,而其追蹤的結果就是拜訪節點的順序。

    • 追蹤方式可分:– 中序追蹤– 後序追蹤– 前序追蹤

    • 以下將介紹二元樹的各種追蹤方式。

    53

  • 17-2-2 樹與二元樹

    中序追蹤 (Inorder Traversal):

    • 若樹只有一個節點 a,定義此樹之中序追蹤為a。• 對二元樹的中序追蹤是先以中序追蹤拜訪樹根的左子樹,再拜訪樹根,然後以中序追蹤拜訪樹根的右子樹。

    • 各子樹又依此定義遞迴(Recursive)追蹤下去。

    54

  • 17-2-2 樹與二元樹

    以右圖的二元樹為例

    • 樹根`+'的左子樹的中序追蹤為A;因為節點`*'的左、右子樹的中序追蹤分別為B和C,所以樹根`+'的右子樹的中序追蹤為B*C。

    • 最後得知中序追蹤為:A + B * C

    55

  • 17-2-2 樹與二元樹

    以右圖的二元樹為例:

    • 以拜訪順序:左→中→右追蹤此樹。

    • 可知其中序追蹤是DBEHAFCG。

    56

  • 17-2-2 樹與二元樹

    前序追蹤(Preorder Traversal):

    • 若樹只有一個節點a,定義其前序追蹤為 a。• 對二元樹的前序追蹤是先拜訪樹根,再以前序追蹤拜訪樹根的左子樹,然後以前序追蹤拜訪樹根的右子樹。

    • 各子樹又依此定義遞迴的追蹤下去。

    57

  • 17-2-2 樹與二元樹

    以右圖為例:

    • 以拜訪順序:中→左→右追蹤此樹。

    • 可得前序追蹤為:ABDEHCFG

    58

  • 17-2-2 樹與二元樹

    以右圖為例:

    • 以拜訪順序:中→左→右追蹤此樹。

    • 可得前序追蹤為:+A*BC

    59

  • 17-2-2 樹與二元樹

    後序追蹤 (Postorder Traversal):

    • 若樹只有一個節點a,定義其後序追蹤為a。• 對二元樹的後序追蹤是先以後序追蹤拜訪樹根的左子樹,再以後序追蹤拜訪樹根的右子樹,然後拜訪樹根。

    • 各子樹又依此定義遞迴的追蹤下去。

    60

  • 17-2-2 樹與二元樹

    以右圖為例:

    • 以拜訪順序:左→右→中追蹤此樹。

    • 可得前序追蹤為:DHEBFGCA

    61

  • 17-2-2 樹與二元樹

    以右圖為例:

    • 以拜訪順序:左→右→中追蹤此樹。

    • 可得前序追蹤為:ABC*+

    62

  • 17-2-2 樹與二元樹

    • 二元樹還能用來表示廣泛使用的 Huffman 碼之「解碼樹」(Decode Tree) 。

    • 以下圖為例:

    63

     

    0

    0

    0

    1

    1

    1a b

    c

    d

    樹根

  • 17-2-2 樹與二元樹

    • 下圖是 a、b、c、d 四個字元的解碼樹。

    • 以0代表左分支,以1代表右分支,從樹根沿著分支走到各節點的路徑可分別記為:

    a:000

    b:001

    c:01

    d:1

    • 以此路徑當作編碼,當要傳送資料時,依此編碼方式轉換資料,而接收端在收到後利用此圖解碼之,可還原成原始資料。

    64

     

    0

    0

    0

    1

    1

    1a b

    c

    d

    樹根

  • 17-2-2 樹與二元樹

    如何找出解碼樹:

    • 假設字母 a、b、c、d 出現的機率分別為 0.1, 0.2, 0.3, 0.4。

    • 首先產生 a、b、c、d 四個樹葉節點,設定其加權(Weight) 分別為 0.1, 0.2, 0.3, 0.4,並設集合 L = {a, b, c, d} (如下圖)。

    65

     a b c d

    0.1 0.2 0.3 0.4

    L = {a, b, c, d}

    (a)

  • 17-2-2 樹與二元樹

    • 其後的每一步驟如右圖(a)~ (d)。

    • 產生一新的節點並設定其加權為L中前兩個加權最低的節點之加權和。

    • 令新節點為此二節點的父母。• 然後將此二節點從L中刪除,將

    新的節點加入 L。

    • 如此重複直到L只剩一加權為1.0的節點。

    • 最後指定L中的節點為樹根這樣就完成了 a、b、c、d 的解碼樹 (如圖(d))。

    66

     

    ab 0.3 c d

    a b 0.3 0.4

    L = {ab, c, d}

    a b c d

    0.1 0.2 0.3 0.4

    L = {a, b, c, d}

    (a)

    (b)

    abc 0.6 d

    ab c 0.4

    L = {abc, d}

    a b (c)

    L = {abcd}

    abc 0.6 d

    ab c0a b

    0

    0abcd

    1

    1

    1

    1.0

  • 17-3 基礎演算法

    本節將介紹以下兩個主題:

    • 17-3-1 排序• 17-3-2 搜尋

    67

  • 17-3-1 排序

    • 「排序問題」 (Sorting Problem) 是將資料依據其“大小"由小至大或由大至小排好,例如將考卷依分數從高至低排好。

    • 以下將介紹四個排序法。1. 插入排序法

    2. 氣泡排序法

    3. 謝耳排序法

    4. 兩兩合併排序法

    68

  • 17-3-1-1 插入排序法

    • 「插入排序法」(Insertion Sort) 利用下述技巧將資料排序好。

    • 假設2、9、16、18、26是一群由左至右依小至大順序排序好的整數資料。

    • 若15要在維持排序順序之條件下加入此群資料內,可以將 15 直接插入 9 和 16 之間。

    • 如此 16、18 和 26 三個資料就要向右移一個位置,使得 9之後空出一個位置讓 15 填入,最後得到 2、9、15、16、18、26 的排序結果 (如右圖)。

    69

  • 17-3-1-1 插入排序法

    • 如右圖將 38、25、13、49、30五個整數資料依序放入陣列D中。

    • 一開始設定I = 2,N = 5。• 將D[I]依排序順序插入

    D[1]至D[I–1]的資料群中,完畢後將I加1。

    • 重複上述動作,直到I >N。

    70

  • 17-3-1-1 插入排序法

    • 下圖是一個插入排序演算法。• 程式的2~4行讀取資料放於陣列 D 內,5~15 行的迴圈I是排序程式

    ,16~18行是輸出排序好的資料。

    • 迴圈I從2至N遞增,內有一從I–1至1遞減的迴圈J。• 迴圈I的目的是把D[I]逐一插入前I–1個資料內,利用迴圈J自D[I–

    1]開始向左反方向找出D[I]應插入的位置。

    演算法:InsertionSort (D[], N)

    輸入:D[], N

    輸出:D[] 1 BEGIN

    2 FOR I=1 TO N STEP 1

    3 READ D[I]

    4 END FOR

    5 FOR I=2 TO N STEP 1

    6 K=D[I]

    7 FOR J=I-1 TO 1 STEP -1

    8 IF K >= D[J] THEN

    9 EXIT FOR

    10 ELSE

    11 D[J+1]=D[J]

    12 END IF

    13 END FOR

    14 D[J+1]=K

    15 END FOR

    16 FOR I=1 TO N STEP 1

    17 PRINT D[I]

    18 END FOR

    19 END

  • 17-3-1-2 氣泡排序法

    • 「氣泡排序法」(Bubble Sort) 是利用相鄰資料兩兩相比而完成資料的排序。

    • 如果給了N個資料,其排序過程分為N – 1回合,第I回合將第I大的資料像氣泡般的“浮現"至其排序的位置。

    • 如下圖將 38、49、25、49、13 五個整數資料依序放入陣列 D 中。

  • 17-3-1-2 氣泡排序法

    • 用氣泡排序法將這五個資料由小到大排序好。

    • 首先要把最大者從左向右推到到D[5]內。從D[1]開始,讓兩個相鄰的資料相比,若左方的資料比右方的大,就將資料交換,也就是讓較大者放到右方,而較小者放到左方。

    • 先比較D[1]與D[2],因為D[1]D[3],所以交換D[2]和D[3]的資料。

    • 如此繼續向右比較下去。最後在D[5]內的49就是這五個資料的最大者。

    D: 1 2 3 4 5

    38 49 25 30 13

    不交換

    38 49 25 30 13

    交換不

    38 25 49 30 13

    交換不

    38 25 30 49 13

    交換不

    38 25 30 13 49

  • 17-3-1-2 氣泡排序法

    • 找出最大數後,然後就要找出次大數並放於D[4]內。

    • 同樣的,從D[1]開始,讓兩個相鄰的資料相比,把較大者放到右方,而較小者放到左方。

    • 最後在D[4]內的38就是這五個資料的次大者。

    D: 1 2 3 4 5

    38 25 30 13 49

    交換

    25 38 30 13 49

    交換

    25 30 38 13 49

    交換

    25 30 13 38 49

  • 17-3-1-2 氣泡排序法

    • 右圖是浮現第三大數的過程。

    • 相同的,利用前述的方法可找出第三大數並放於D[3] 。

    • 最後將30放於D[3]內。

    D: 1 2 3 4 5

    25 30 13 38 49

    不交換

    25 30 13 38 49

    交換不

    25 13 30 38 49

    交換不

  • 17-3-1-2 氣泡排序法

    • 最後就是找出第四大數並放於D[2]內。

    • 之後陣列D的內容為13、25、30、38、49,是由小到大的順序。

    D: 1 2 3 4 5

    25 13 30 38 49

    交換不

    13 25 30 38 49

  • 17-3-1-2 氣泡排序法

    • 下圖是一個氣泡排序演算法。• 5~18行的迴圈J用7~14行的迴圈I比較D[I]與D[I +1] 相

    鄰資料,以使得第N + 1 – J大數放於D[J ] 中。• 9~11行是交換D[I]和D[I + 1]的內容。• FLAG 變數用來表示迴圈I內是否有交換資料,若無資料交

    換,代表資料都已經排序好了,可以停止排序。 演算法:BubbleSort(D[],N)

    輸入:D[], N

    輸出:D[] 1 BEGIIN

    2 FOR I=1 TO N STEP 1

    3 READ D[I]

    4 END FOR

    5 FOR J=N TO 2 STEP 1

    6 FLAG=0

    7 FOR I=1 TO J-1 STEP 1

    8 IF D[I] > D[I+1] THEN

    9 TEMP=D[I]

    10 D[I]=D[I+1]

    11 D[I+1]=TEMP

    12 FLAG=1

    13 END IF

    14 END FOR

    15 IF FLAG=0 THEN

    16 EXIT FOR

    17 END IF

    18 END FOR

    19 FOR I=1 TO N STEP 1

    20 PRINT D[I]

    21 END FOR

    22 END

  • 17-3-1-3 兩兩合併排序法

    • 「兩兩合併排序法」(2-Way Merge Sort)是先將資料分成幾個排序好的串列,然後把串列兩兩合併成更大的已排序串列,直到合併成包含所有資料的已排序串列為止。

  • 17-3-1-3 兩兩合併排序法

    • 如右圖要排序10個整數資料。

    • 先分成10個只有一個資料的串列,然後兩兩合併成擁有兩個資料的串列。

    • 接著又合併成有4個資料的串列,依此做下去,直到合併成包含10個資料的串列。

  • 17-3-1-3 兩兩合併排序法

    • 合併方法是將兩個已排序好的資料串列 A 與 B 合併成為一排序好的串列 C。

    • 其方法是分別從A和B中依小到大的順序取出第一數,比較此二數,將較小數放入C中,並從較小數的來源串列中取出下一個數,然後再與先前的較大數比較。

    • 如此比較及讀取資料下去,直到有一方的資料都已放入 C 中,最後將另一方未放入C內的資料依序放入 C。

  • 17-3-1-3 兩兩合併排序法

    • 如右圖,陣列 A 和 B 的內容都是依小到大排序好的三個整數。

    • 現在我們要把這兩個資料串列合併到陣列C內,還要保證陣列C的內容一樣是由小到大排序好的。

  • 17-3-1-3 兩兩合併排序法

    • 一開始比較A[1]和B[1]。因為B[1]較小,所以將B[1] 放入C[1](如圖17.28),陣列B及C的索引指標向前推1。

    • 接著比較A[1]與B[2],此時A[1]較小,所以放入C[2] (圖17.29),然後將陣列A及C的索引指標向前推進。

  • 17-3-1-3 兩兩合併排序法

    • 再比較A[2]和B[2]。因為B[2]較小,所以將B[2]放入C[3](如圖17.30),陣列B及C的索引指標向前推進。

    • 然後比較A[2]與B[3],此時B[3]較小,所以放入C[4](如圖 17.31)。

  • 17-3-1-3 兩兩合併排序法

    • 現在陣列 B 的資料都已放入 C,所以把 A[2] 和A[3] 依序放入 C (如圖17.32)。

    • 最後完成合併陣列 A 與 B於陣列 C。

  • 17-3-1-3 兩兩合併排序法

    • 下圖的演算法 MERGE 是將陣列A(有 N 個已排好的整數)與陣列B(有 M 個已排好的整數)以合併成排序序列並存於陣列 C 中。

    演算法:MERGE (A[], N, B[], M, C[])

    輸入:A[], N, B[], M, C[]

    輸出:C[] 1 BEGIN

    2 I=1

    3 J=1

    4 K=1

    5 WHILE I M THEN

    22 FOR L=I TO N STEP 1

    23 C[K]=A[L]

    24 K=K+1

    25 END FOR

    26 END IF

    27 END

  • 17-3-2 搜尋

    • 在一堆資料裏尋找某特定的資料謂之「搜尋」(Searching)。

    • 以下將陸續介紹:– 循序搜尋– 二分搜尋– 二元搜尋樹等常用搜尋法。

  • 17-3-2 搜尋

    • 「循序搜尋」 (Sequential Search) 又稱「線性搜尋」(Linear Search),依據資料儲存順序從頭至尾尋找下去。

    • 如下圖,陣列 D 中有六個整數資料。若要尋找的資料為25,從D[1] 開始比對,然後 D[2],一直到D[3] 才找到。但如果要找的是 40,就要經歷 D[1], D[2],..., D[6] 六次比對才找到。

  • 17-3-2 搜尋

    • 右圖的演算法 SEQSEACH 是在有 NUM 個整數的陣列 D 中以循序搜尋找尋 KEY。

    • 執行完後,若 INDEX = 0,表示 KEY 不在陣列 D 內,否則,KEY 在 D[INDEX) 內。

    演算法:SEQSEACH (D[], NUM, KEY, INDEX)

    輸入:D[], NUM, KEY, INDEX

    輸出: INDEX 1 BEGIN

    2 INDEX=0

    3 FOR I=1 TO NUM

    4 IF D[I]=KEY THEN

    5 INDEX=I

    6 EXIT FOR

    7 END IF

    8 END FOR

    9 END

  • 17-3-2 搜尋

    • 使用「二分搜尋」 (Binary Search) 的前題是資料要先排序好。假設陣列 D 中有 N 個已排序好的數,要尋找的資料為 KEY。二分搜尋法是每比對一次後就把搜尋範圍縮小一半,在 (log2N ) +1 次比對內就可判斷出 KEY 是否在資料中。

    • 一開始搜尋的範圍是 D[1] 至 D[N ],將 KEY 與D[1] 至D[N ] 的中間數 D[(1 + N )/2] 相比,若 KEY 較大,就將搜尋範圍縮小為後半,即 D[(1 + N )/2+1] 至D[N ];KEY 較小,就將搜尋範圍縮小為前半,即 D[1] 至 D[(1 + N )/2– 1]。然後 KEY 再與新搜尋範圍的中間數比對,如此遞迴地搜尋下去。

  • 17-3-2 搜尋

    • 如下圖,陣列 D 內有依小至大順序排好的七個整數。假設 KEY = 41,第一次 KEY 與中間數 D[4] 比較。因 KEY > D[4],所以繼續搜尋 D[5] 至 D[7]。第二次KEY 與D[6] 比較,結果 KEY < D[6],再搜尋 D[5] 至 D[5]。第三次 KEY 與 D[5] 比較,因為 KEY = D[5],所以 KEY

    的確在陣列 D 中。

  • 17-3-2 搜尋

    • 下圖的演算法 BINSEACH 是在有 NUM 個整數的陣列 D 中以二分搜尋法找尋 KEY。執行完後,若 INDEX = 0,表示 KEY 不在陣列 D 內,否則,KEY 在 D [INDEX] 內。

    演算法:BINSEACH (D[], NUM, KEY, INDEX)

    輸入:D[], NUM, KEY, INDEX

    輸出: INDEX 1 BEGIN

    2 INDEX=0

    3 LOWER=1

    4 UPPER=NUM

    5 WHILE LOWER D[MIDDLE] THEN

    8 LOWER=MIDDLE+1

    9 END IF

    10 IF KEY < D[MIDDLE] THEN

    11 UPPER=MIDDLE-1

    12 END IF

    13 IF KEY = D[MIDDLE] THEN

    14 INDEX=MIDDLE

    15 EXIT WHILE

    16 END IF

    17 END WHILE

    18 END

  • 17-3-2-3 二元搜尋樹

    • 「二元搜尋樹」(Binary Search Tree)是二元樹的結構,但是樹中的每一節點資料都不小於它的左孩子,也不大於它的右孩子。將資料先存於此特殊的二元搜尋樹,之後可利用此樹做資料的搜尋。

  • 17-3-2-3 二元搜尋樹

    • 若有 18、26、11、30、24、35、13 等七個整數資料,我們依據輸入順序建立一二元樹。第一個輸入的數 18 當作樹根 (如圖17.36)。

    • 接下來輸入的數從樹根的節點資料開始與之相比。若比節點資料大,就再和其右孩子相比 (如果沒有右孩子,就將新輸入的數當作其右孩子)。

    • 若比節點資料小,就再和其左孩子相比 (如果沒有左孩子,就將新輸入的數當作其左孩子)。

    18

    圖 17.36

  • 18

    11 26

    13 24 30

    35

    圖 17.42

    17-3-2-3 二元搜尋樹

    • 圖 17.37、圖 17.38、圖 17.39、圖 17.40、圖 17.41 和圖 17.42 分別是輸入 26、11、30、24、35 和 13 後二元樹的變化。

    • 最後在圖 17.42 上得到的二元樹就是這七個資料的二元搜尋樹。

    18 18

    26 11 26

    圖 17.37 圖 17.38

    18 18 18

    11 26 11 26 11 26

    30 24 30 24 30

    35圖 17.39 圖 17.40

    圖 17.41

  • 17-3-2-3 二元搜尋樹

    • 假設搜尋的資料為KEY,從二元搜尋樹之樹根的節點資料開始與KEY相比。

    • 若相等,就找到了;若 KEY 比節點資料大,而且此節點又沒有右孩子,表示KEY不在資料內,否則就再和其右孩子相比;若比節點資料小,而且此節點又沒有左孩子,表示KEY不在資料內,否則就再和其左孩子相比。

  • 17-3-2-3 二元搜尋樹

    • 例如在圖17.42的二元搜尋樹上找尋KEY,假設KEY為13。• 開始時KEY與樹根18相比,因為KEY < 18,所以再與其左

    孩子節點11相比。

    • 又因KEY > 11,再與節點11的右孩子節點13相比,結果就找到了KEY的確在這些資料中。

    18

    11 26

    13 24 30

    35

    圖 17 .4 2

  • 17-3-2-3 二元搜尋樹

    • 但當KEY=12時,開始與樹根18相比,因為KEY < 18,所以再與其左孩子節點11相比。

    • 又因KEY > 11,再與節點11的右孩子節點13相比。• 因為KEY < 13,而且節點13又沒有左孩子,表示KEY不在

    這些資料中。

    18

    11 26

    13 24 30

    35

    圖 17 .4 2

  • 17-4 演算法分析與策略

    • 「演算法」 (Algorithm) 就是解決問題的方法。例如兩個整數相加的問題,其演算法是先做個位數字的相加,再做十位數字的相加,接著是百位數,依此類推。

    • 完整的演算法包括明確的輸出入資料和詳細且有限的執行步驟。

    98

  • 17-4-1 算法分析

    • 圖 17.43描繪出在電腦上解決問題的基本架構。演算法以程式描述後在電腦上執行,資料輸入至程式後,經過程式對資料的運算,最後產生解答。

    99

    資料 演算法

    (程式) 解答

    圖17.43

  • 17-4-1 算法分析

    • 假設演算法 A 能解問題 P,而問題 P 的輸入資料量為 N。– 假設資料量為 N 的資料樣本 (Data Instance) 有 D1, D2, ..., Di, ..., Dm 共 m 種,令 T (Di) 表示當輸入資料為 Di 時演算法 A 要執行的運算或步驟的總次數。

    – 演算法 A 的「最好狀況時間複雜度」 (Best-Case Time Complexity) 是 T (Di) (1 ≤ i ≤ m) 中最小的值,即 min 1≤ i ≤ m { T (Di)},而演算法 A 的「最差狀況時間複雜度 」(Worst-Case Time Complexity) 是 T (Di) (1 ≤ i ≤ m) 中最大者,即 max 1≤ i ≤ m{ T (Di)}。

    100

  • 17-4-1 算法分析

    – 而演算法 A 的「一般狀況時間複雜度」 (Average-Case Time Complexity) 是 T (Di) (1 ≤ i ≤ m) 的平均值或期望值 (在某機率假設下)。

    – 通常我們說演算法 A 的時間複雜度為 C 就是指其最差狀況時間複雜度為 C。

    – 例如問題 P 之輸入資料量為 N 的樣本有 D1, D2, D3三種,而 T (D1) = 3 ,T (D2) = N,T (D3) = N

    3,則演算法 A 的最好狀況時間複雜度為 3,最差狀況為N 3,而一般狀況為 (3 + N + N 3) /3。稱演算法 A的時間複雜度為 N 3。

    101

  • 17-4-1 算法分析

    • 時間複雜度常用「階次」 (Order) 來表達。首先介紹表示「漸近上限」 (Asymptotic Upper Bound) 的 O 階次。

    • 如果存在某固定正實數 c 及某個非負整數 m 使得對所有的 n ≥ m,不等式 g (n) ≤ c × f(n) 都成立,則 g (n) = O ( f (n)),稱 f(n) 為 g (n) 的漸近上限。

    • 例如 N 2 +10N = O (N 2),因為當 n ≥ 10 時,N 2 + 10N ≤ 2N 2。稱 N 2 為 N 2 + 10N 的漸進上限。

    102

  • 17-4-1 算法分析

    • 接著介紹 Ω 階次。若存在某固定正變數 c 及某非負整數 m 使得對所有 n ≥ m,不等式 g (n) ≥ c × f (n) 都成立,則 g (n) = Ω ( f (n)),稱 f (n) 為 g (n) 的「漸近下限」(Asymptotic Lower Bound)。

    • 例如 n3 = Ω (n2),因為當 n ≥ 1時,n3 ≥ 1 ×n2,所以 n2 為 n3 的漸近下限。

    103

  • 17-4-1 算法分析

    • 若g (n) = O ( f (n)) 且 g (n) = Ω ( f (n)),則稱 f (n) 為 g (n) 的「真正階次」 (Exact Order),記為 g (n) = θ ( f (n))。

    104

  • 17-4-1 算法分析• 表17.1 顯示多種 f (n) 函數的數值,當 n 足夠大時,2n > n3 > n2 > nlogn > n > logn。

    105

    nf(n)

    2n n3 n2 nlogn n logn

    12 1 1 0 1 0

    24 8 4 0.602059991 2 0.301029996

    416 64 16 2.408239965 4 0.602059991

    8256 512 64 7.224719896 8 0.903089987

    1665536 4096 256 17-26591972 16 1.204119983

    324294967296 32768 1024 48.16479931 32 1.505149978

    641.84467E+19 262144 4096 115.5955183 64 1.806179974

    1283.40282E+38 2097152 16384 269.7228761 128 2.10720997

    2561.15792E+77 16777216 65536 616.5094311 256 2.408239965

    5121.3408E+154 134217728 262144 1387.14622 512 2.709269961

  • 17-4-1 算法分析

    • 如果 f (n) = c p (n),其中 c 為常數,p (n) 為 n 的「多項式函數」 (Polynomial Function) (如n, n2 + 10, log2n, nlog2n),則稱 f (n) 為「指數函數」 (Exponential Function)。

    • 例如 2n、3n、4logn 等函數都是指數函數。• 因為指數函數的數值上升速度相當快,所以時間複雜度為指數函數階次的演算法,在資料量足夠多時,需要相當長的時間才能解得答案。

    106

  • 17-4-1 算法分析

    • 若演算法的時間複雜度是 O (p (n)),其中 p(n) 為 n 的多項式函數,則稱此演算法為「多項式時間演算法」(Polynomial-Time Algorithm)。

    • 若問題 P 能用多項式時間演算法解得答案,則問題 P 就能用電腦在合理時間內求得答案,在電腦上是「可解」 (Tractable) 的問題。

    – 例如排序及搜尋等問題就是此類問題。

    107

  • 17-4-1 算法分析

    • 表 17.2是各種排序法及搜尋法的時間複雜度。

    108

    演算法時間複雜度

    最好狀況 最差狀況

    插入排序 O(n) O(n2)

    氣泡排序 O(n) O(n2)

    兩兩合併排序 O(n) O (nlog2n)

    循序搜尋 O(1) O(n)

    二分搜尋 O(1) O (log2n)

  • 17-4-1 算法分析

    • 若可解問題 P 的演算法 A 的時間複雜度為 t,而解問題 P 的演算法最少需要 t 時間,則稱演算法 A 是解問題 P 的「最佳演算法」(Optimal Algorithm)。

    • 像排序 n 個數的問題已證明在「比較與交換模式」 (compare-and-exchange model)下最少需要 Ω (nlog2n) 時間,而兩兩合併排序法的時間複雜度也是 O (nlog2n),所以兩兩合併排序法是最佳排序演算法。

    109

  • 17-4-1 算法分析

    • 若問題 P 到目前為止仍無法以多項式時間演算法解得答案,也就是無法於電腦上在合理時間內求得答案,我們稱問題 P 為「難解問題」(Intractable Problem)。

    • 有許多的應用問題是屬於這類的問題,舉例說明如下:

    – 1.旅行推銷員問題 (Travelling Salesman Problem)• 有各城市間的公路交通圖,請找出一條從某城市出發,經過每個城市一次再回到該城市的最小長度的交通路線。

    110

  • 17-4-1 算法分析

    – 2.圖形塗色問題 (Graph-Coloring Problem)• 給一由頂點 (Vertex) 及邊 (Edge) 構成的圖形,在有邊相連的兩頂點不能有相同顏色的限制下來對頂點塗色,請問所需的顏色個數最少是多少?

    – 3.裝箱問題 (Bin-Packing Problem)• 有 n 個不可分解的貨物,其大小分別為 S1 , ... , Sn , 0 < Si ≤ 1,若每個箱子的容量為 1,請問將這 n 個貨物放入箱子內所需的最小箱子個數為何 ?

    111

  • 17-4-1 算法分析

    • 目前要找出難解問題的「真正解答」(exact solution)是相當耗時的,因為目前還沒有人能提出在當今電腦架構下任何快速的演算法以求得真正解答。

    • 然而有些難解問題是能以多項式時間演算法求得接近解答的「逼近解」 (approximate solution)。

    112

  • 17-4-2 個別擊破策略

    • 「個別擊破策略」(Divide-and-Conquer Method) 的主要精神是將原來的問題 P 分解成 2 個或多個子問題,待個別解決這些子問題後再經由「合併」(Merge) 處理,整理出原來問題的答案,而分割後的子問題是資料量較小的問題 P。

    • 因此在解子問題時又可遞迴應用上述的方法,經由分割及合併的過程得到解答。

    • 前一章介紹的二分搜尋法就是採用個別擊破策略。

    113

  • 17-4-2 個別擊破策略

    • 兩兩合併排序法也可視為是個別擊破的策略。

    • 如圖 17.44所示,一開始就將資料分割成兩部分處理,然後再遞迴細分各資料直至不能細分為止,接著就兩兩合併成排序的序列,慢慢的將分割的資料合併成排序的序列,最後得到所有資料的排序結果。

    114

    圖17.44

      10 6 27 17 32 4 5 56

    10 6 27 17 32 4 5 56

    10 6 27 17 32 4 5 56

    10 6 27 17 32 4 5 56

    6 10 17 27 4 32 56 5

    6 10 17 27 4 5 32 56

    4 5 6 10 17 27 32 56

    分割

    分割

    分割

    合併

    合併

    合併

  • 17-4-2 個別擊破策略

    • 讓我們回到排序的問題上。假設陣列 D 中依序放有 4, 3, 5, 2, 6, 7, 1 等七個整數,我們的目的是將這些數依小到大順序排序。

    • 首先觀察第一個數 4 在最後排序好的序列中應是第四位,即應放在 D [4]。

    • 如果能進一步將小於 4 的數放在 D [1] 至 D[3] 中,而大於 4 的數放在 D[5] 至 D [7] 中,則只要再分別排序好 D [1] 至 D[3] 的數及 D[5] 至 D[7] 內的數,那原來排序問題就解決了。

    115

  • 17-4-2 個別擊破策略

    • 接著介紹如何將 4 放到 D[4] 及將小於 4 的數集中在 D[1] 至D[3] 內,而大於 4 的數則集中在 D[5] 至 D[7] 內。

    116

  • 17-4-2 個別擊破策略

    • 令 i = 2,j = 7 從D[i] 開始向右找到第一個比 D[1] 大的數,以 i 紀錄其陣列的索引。從 D[ j] 開始向左找到第一個不大於4的數,以 j 紀錄其陣列索引。

    117

  • 17-4-2 個別擊破策略

    • 若 i < j,則交換D[i] 與 D[ j] 的內容,並繼續前述的左右搜尋;若 i ≥ j,則交換 D[1] 與 D[ j] 的內容,並停止搜尋。

    118

  • 17-4-2 個別擊破策略

    119

    • 雖然快速排序法的最差時間複雜度為 O (n2) (其中 n 為資料個數),因為其一般狀況時間複雜度為O (nlog2n),還是相當實用的排序方法。

  • 17-4-3 貪婪策略

    • 「貪婪策略」(Greedy Method)常用來解決「最佳化問題」(Optimization Problem)。

    • 貪婪法是最直接的解法,每次的決策都是朝向目前“最好"的方向前進,而且不回頭。

    120

  • 17-4-3 貪婪策略

    • 舉例來說,某一個銀行出納櫃檯要服務 n 個顧客,銀行的目標是希望顧客在櫃檯等待的平均時間要最少。

    • 解決之道是每次都從尚未服務的顧客中,選擇需要服務時間最短的顧客來服務,如此就可達到預期目標。

    • 像這樣每次都選擇最小服務時間的策略就是一種貪婪策略。

    121

  • 17-4-3 貪婪策略

    • 假設有 5 個顧客 A, B, C, D, E 幾乎同時到達銀行櫃檯,其所需服務時間如表 17.3。

    • 根據貪婪演算法,銀行櫃檯將依序服務B, D, E, C, A。

    • 顧客在櫃檯等待的平均時間為[1 + (1 + 2) + (1 + 2 + 3) + (1 + 2 + 3 + 4) + (1 + 2 + 3 + 4 + 5) ] / 5 = 7。

    122

    顧客 所需服務時間

    A 5

    B 1

    C 4

    D 2

    E 3

  • 17-4-3 貪婪策略

    • 再介紹一個可用貪婪策略解決的「背包問題」(Knapsack Problem) 。假設有多個可分解的物件和一只限重W的背包,每件物件有其重量及其被選取放入背包內的利益。請問要如何將物件放進背包內而使得獲得的利益最大?

    • 解決的方法是每次在限重的情形下,選取尚未放入的物件中擁有最大的利益與重量的比值之物件放入背包內。

    123

  • 17-4-3 貪婪策略

    • 設背包限重 100,有 A, B, C, D, E 共五個物件,其資料如表17.4。

    124

    物件 重量 利益 利益/重量

    A 10 20 2.0

    B 20 30 1.5

    C 30 66 2.2

    D 40 40 1.0

    E 50 60 1.2

  • 17-4-3 貪婪策略

    • 因為物件C的利益/重量最高,所以將物件C放入背包內,此時背包的重量為30。

    125

    物件 個數 累計利益 重量

    C 1 66 30

  • 17-4-3 貪婪策略

    • 接著從物件A, B, D, E中挑出其利益/重量最高的物件A放入背包內,這時背包的重量為 30+10=40。

    126

    物件 個數 累計利益 重量

    C 1 66 30

    A 1 86 40

  • 17-4-3 貪婪策略

    • 接下來,從物件B, D, E中挑出其利益/重量最高的物件 B 裝入背包內,之後背包的重量為 40 + 20 = 60。

    127

    物件 個數 累計利益 重量

    C 1 66 30

    A 1 86 40

    B 1 116 60

  • 17-4-3 貪婪策略

    • 再從剩下的物件 D 和E 中選出其利益/重量最高的物件 E。由於物件 E 整個放入背包內會超過重量 100,所以物件 E 只能放入0.8 個。

    128

    物件 個數 累計利益 重量

    C 1 66 30

    A 1 86 40

    B 1 116 60

    E 0.8 164 100

    表17.5。

  • 17-4-3 貪婪策略

    • 放入以後得總重量為60 + 50 ∗ 0.8 = 100。最後得到的利益為 66 + 20 + 30 + 60 ∗0.8 = 164。物件裝入背包的情形整理於表17.5。

    129

    物件 個數 累計利益 重量

    C 1 66 30

    A 1 86 40

    B 1 116 60

    E 0.8 164 100

    表17.5。

  • 17-4-3 貪婪策略

    • 接著介紹「最小擴展樹」(Minimum Spanning Tree) 問題。圖 17.47 是由「頂點」 (Vertex) 及「邊」(Edge) 構成的圖形,其中圓圈代表頂點,連接兩頂點的線為邊,而且每個邊都有非負 (nonnegtive) 的「加權」 (Weight) (例如頂點 V3 與頂點 V5 間的邊之加權為 5)。

    130

    4

    2

    6

    1 7

    3 5

    2

    V5

    V1

    V2

    8

    V4

    V3

  • 17-4-3 貪婪策略

    • 由於圖 17.47的邊都無方向,稱圖17.47的圖形為「無向圖」 (Undirected Graph)。

    131

    4

    2

    6

    1 7

    3 5

    2

    V5

    V1

    V2

    8

    V4

    V3

  • 17-4-3 貪婪策略

    • 定義無向圖的「路徑」(Path) 為頂點的序列,其中序列裡每個頂點與其後一個頂點之間必有邊相連。

    • 例如圖17.47中 [V1, V5, V2] 是一條從頂點 V1 到頂點 V2 的路徑。

    132

    4

    2

    6

    1 7

    3 5

    2

    V5

    V1

    V2

    8

    V4

    V3

  • 17-4-3 貪婪策略

    • 若無向圖的任兩頂點都存在一條路徑,則稱此圖為「聯通圖」 (Connected Graph)。例如圖17.47 就是聯通圖。

    • 從某頂點出發回到該頂點的路徑稱為「迴圈」(Cycle)。例如圖17.47 的 [V1, V5, V2, V1] 路徑就是迴圈。

    133

    4

    2

    6

    1 7

    3 5

    2

    V5

    V1

    V2

    8

    V4

    V3

    • 一個沒有迴圈的圖形稱為「無迴圈圖」 (Acyclic Graph)。

  • 17-4-3 貪婪策略

    • 定義「樹」 (Tree) 為聯通的無迴圈圖。• 若圖形 G 的頂點所成的集合為 V,邊所成的集合為 E,以 G = (V, E) 表示圖形 G。

    • 定義圖形 G = (V, E) 的「擴展樹」 (Spanning Tree) T = (V, E) 是包括所有 G 的頂點的樹,其中 E' 是 E 的子集合。例如圖17.48 (a) 及(b) 都是圖 17.47 的擴展樹。

    134

    6

    1 7

    3 5

    2

    V5

    V1

    V2

    8

    V4

    V3

    6

    1 7

    3 5

    2

    V5

    V1

    V2

    V4

    V3

    圖17.48

  • 17-4-3 貪婪策略

    • 接著介紹以貪婪策略設計的 Kruskal 演算法。假設要求圖形 G = (V, E) 的最小擴展樹。

    • 此演算法依據邊的加權由小到大的順序考慮該邊是否為最小擴展樹的邊。

    • 若邊 e 加入後不會產生迴圈,則邊 e 為最小擴展樹的一員;反之,邊 e 就不是最小擴展樹的一員。如此重複直到建構出最小擴展樹。詳細的步驟分述於下:

    135

  • 17-4-3 貪婪策略

    136

    演算法:Kruskal 方法

    輸入:圖形 G = (V, E)

    輸出:G 的最小擴展樹

    步驟 1:將圖形 G = (V, E) 所有的邊依其加權由小到大排好, 依序為 e1, e2, e3, …, em。

    步驟 2:建立圖形 T = (V, E’), 其中 E’ = Φ。設 i = 1。

    步驟 3:若 ei 加入圖形 T 中不產生迴圈, 則將 ei 加入圖形 T, 即 E’= E’ ∪ { ei };

    否則, i = i + 1。

    步驟 4:若圖形 T 的邊數小於|V|-1, 則重複步驟 3;否則, 圖形 T 是圖形 G 的最小

    擴展樹, 結束演算法執行。

  • 17-4-3 貪婪策略

    • 用 (a, b) 表示頂點 a與頂點 b 的邊。以找圖17.47 的最小擴展樹為例,將圖 17.47 的邊依加權排序後得 e1 = (V1,V5), e2 = (V2,V3), e3 = (V2,V5), e4 = (V1,V2), e5 = (V3,V5), e6 = (V1,V4), e7 = (V4,V5), e8 = (V3,V4)。

    137

    (a) T

    1

    (b) 加入 (V1, V5)

    1

    2

    (c) 加入 (V2, V3)

    3

    1

    2

    (d) 加入 (V2, V5)

    3

    1

    2

    (e) 拒絕 (V1, V2)

    3

    1

    2

    (f) 拒絕 (V3, V5)

    3

    1

    6

    2(g) 加入 (V1, V4)

    V1

    V5

    V4

    V2 V3

    V1

    V5

    V4

    V2 V3

    V1

    V5

    V4

    V2 V3

    V1

    V5

    V4

    V2 V3

    V1

    V5

    V4

    V2 V3

    V1

    V5

    V4

    V2 V3

    V1

    V5

    V4

    V2 V3

  • 17-4-3 貪婪策略

    • 一開始圖形 T 只有頂點但沒有邊 (圖17.48(a))。考慮 e1 = (V1,V5),因不產生迴圈,就將(V1,V5) 加入 T 內 (圖17.49(b))。

    138

    (a) T

    1

    (b) 加入 (V1, V5)

    1

    2

    (c) 加入 (V2, V3)

    3

    1

    2

    (d) 加入 (V2, V5)

    3

    1

    2

    (e) 拒絕 (V1, V2)

    3

    1

    2

    (f) 拒絕 (V3, V5)

    3

    1

    6

    2(g) 加入 (V1, V4)

    V1

    V5

    V4

    V2 V3

    V1

    V5

    V4

    V2 V3

    V1

    V5

    V4

    V2 V3

    V1

    V5

    V4

    V2 V3

    V1

    V5

    V4

    V2 V3

    V1

    V5

    V4

    V2 V3

    V1

    V5

    V4

    V2 V3

  • 17-4-3 貪婪策略

    • 同樣的, e2 = (V2,V3) 及e3 = (V2,V5) 也依序加入 T 內 (圖17.49(c) 和 (d))。

    139

    (a) T

    1

    (b) 加入 (V1, V5)

    1

    2

    (c) 加入 (V2, V3)

    3

    1

    2

    (d) 加入 (V2, V5)

    3

    1

    2

    (e) 拒絕 (V1, V2)

    3

    1

    2

    (f) 拒絕 (V3, V5)

    3

    1

    6

    2(g) 加入 (V1, V4)

    V1

    V5

    V4

    V2 V3

    V1

    V5

    V4

    V2 V3

    V1

    V5

    V4

    V2 V3

    V1

    V5

    V4

    V2 V3

    V1

    V5

    V4

    V2 V3

    V1

    V5

    V4

    V2 V3

    V1

    V5

    V4

    V2 V3

  • 17-4-3 貪婪策略

    • 當考慮 e4 = (V1,V2) 時,因加入後會產生[V1, V5, V2, V1] 的迴圈,所以拒絕 (V1,V2) 的加入 (圖17.49(e))。

    140

    (a) T

    1

    (b) 加入 (V1, V5)

    1

    2

    (c) 加入 (V2, V3)

    3

    1

    2

    (d) 加入 (V2, V5)

    3

    1

    2

    (e) 拒絕 (V1, V2)

    3

    1

    2

    (f) 拒絕 (V3, V5)

    3

    1

    6

    2(g) 加入 (V1, V4)

    V1

    V5

    V4

    V2 V3

    V1

    V5

    V4

    V2 V3

    V1

    V5

    V4

    V2 V3

    V1

    V5

    V4

    V2 V3

    V1

    V5

    V4

    V2 V3

    V1

    V5

    V4

    V2 V3

    V1

    V5

    V4

    V2 V3

  • 17-4-3 貪婪策略

    • 同樣的理由,也拒絕e5 = (V3,V5) 的加入(圖17.49(f))。

    141

    (a) T

    1

    (b) 加入 (V1, V5)

    1

    2

    (c) 加入 (V2, V3)

    3

    1

    2

    (d) 加入 (V2, V5)

    3

    1

    2

    (e) 拒絕 (V1, V2)

    3

    1

    2

    (f) 拒絕 (V3, V5)

    3

    1

    6

    2(g) 加入 (V1, V4)

    V1

    V5

    V4

    V2 V3

    V1

    V5

    V4

    V2 V3

    V1

    V5

    V4

    V2 V3

    V1

    V5

    V4

    V2 V3

    V1

    V5

    V4

    V2 V3

    V1

    V5

    V4

    V2 V3

    V1

    V5

    V4

    V2 V3

  • 17-4-3 貪婪策略

    • 而後因 e6 = (V1,V4) 加入 T 後不產生迴圈,所以將 (V1,V4) 加入T 中 (圖17.49(g))。此時 T 為 G 的擴展樹,停止演算法。

    • 最後得到的圖17.49(g) 就是圖17.47 的最小擴展樹。

    142

    (a) T

    1

    (b) 加入 (V1, V5)

    1

    2

    (c) 加入 (V2, V3)

    3

    1

    2

    (d) 加入 (V2, V5)

    3

    1

    2

    (e) 拒絕 (V1, V2)

    3

    1

    2

    (f) 拒絕 (V3, V5)

    3

    1

    6

    2(g) 加入 (V1, V4)

    V1

    V5

    V4

    V2 V3

    V1

    V5

    V4

    V2 V3

    V1

    V5

    V4

    V2 V3

    V1

    V5

    V4

    V2 V3

    V1

    V5

    V4

    V2 V3

    V1

    V5

    V4

    V2 V3

    V1

    V5

    V4

    V2 V3

  • 17-4-3 貪婪策略

    • 接著介紹「最短路徑問題」(Shortest Path Problem)。下圖是 S、A、B、T 四個地點的交通路線,各路線分別標上距離:

    143

     

    S A

    10

    40

    25B T

    15

    60

    20

    30

    50

  • 17-4-3 貪婪策略

    • 假設要求從 S 到 T 的最短路徑長度。令 D (a, b) 表示從 a 到 b 的最短路徑長度。從上圖,可知 D (S, T) = D (S, A) +D (A, B) +D (B, T) = 10 + 15 + 20 = 45。如此只要利用貪婪策略就能得到答案。但是此貪婪策略並不適用於所有的最短路徑問題。

    144

  • 17-4-3 貪婪策略

    • 例如要找下圖的 D (S, T ),則 D (S, T ) = 24 + 20 = 44,此結果不等於 D (S, A) + D(A, B) + D (B, T) = 45。

    145

    圖17.51

     

    S A

    10

    40

    25B T

    15

    60

    20

    30

    50

    24

  • 17-4-4 動態規劃

    • 先前介紹的個別擊破策略是遞迴地將問題分成較小的問題後分別求得解答,然後合併每個小部分的答案成為完整的答案,是屬於由上至下 (Top-Down) 的計算策略。

    • 例如要計算下面的 Fibonacci 函數 f (n):– f (0) = 0– f (1) = 1– f (n) = f (n – 1) + f (n – 2), n ≥ 2

    146

  • 17-4-4 動態規劃

    • 若要計算 f (5),利用個別擊破方法的計算過程如下圖所示﹕

    • 其中 f (3), f (2), f (1), f (0) 被重複計算許多次,如果能先依序將 f (0), f (1),f (2), f (3), f (4)的結果計算出來並儲存於表格或陣列,就可避免重複計算,增進計算速度。

    147

    f (5)

    f (4)

    f (2)

    f (3)

    f (2)f (1)

    f (1)f (0) f (1)f (0)

    f (3)

    f (2)f (1)

    f (1)f (0) 圖17.52

  • 17-4-4 動態規劃

    • 以陣列 FIB 儲存 Fibonacci 函數,可利用下面的方法計算 f (n):

    • 將問題分成小問題,然後直接解小問題,將結果儲存起來以備之後使用,像這種「由下至上」(Bottom-Up) 的設計策略稱為「動態規劃」(Dynamic Programming)。

    148

    FIB[0] =0

    FIB[1] =1

    FOR I = 2 TO n STEP 1

    FIB[I] = FIB[I-1] + FIB[I-2]

    END FOR

  • 17-4-4 動態規劃

    • 「二項式係數」 (Binomial Coefficient) 的定義為

    • 當 n, k 的值不小時,n!及 k!是很大的天文數字,非常難計算。修改此定義,重新以遞迴關係定義於下﹕

    149

  • 17-4-4 動態規劃

    • 陣列 BI 是一個二維陣列,有 n +1 個列 (編號從 0 至 n),k + 1 個欄 (編號從 0 到k),讓B (n, k) 的值儲存於 BI[n, k] 內。下列的方法能很快的計算出 B (n, k)﹕

    150

    FOR I = 0 TO n STEP 1

    FOR J = 0 TO min{i, k} STEP 1

    IF J = 0 OR J = I THEN

    BI[I, J] =1

    ELSE

    BI[I, J] =BI[I-1, J-1] +BI[I-1,J]

    END IF

    END FOR

    END FOR

  • 17-4-4 動態規劃

    • 接下來介紹有向圖的「最短路徑問題」(Shortest Path Problem) 。

    • 下圖是一個「有向的」 (Directed) 且加權的的圖形,其中圓圈代表頂點,而連接兩頂點的線稱為邊。

    151

    2 V1

    7

    4

    1

    5 6

    3 1

    V4

    V2

    V3

  • 17-4-4 動態規劃

    • 每個邊都有方向也有對應的非負的加權,例如有一個從頂點 V1 至頂點 V2 的邊,其加權為 1。路徑是一個頂點序列,其中序列裡每個頂點到其後一個頂點的邊一定存在。

    • 例如 [V1, V3, V2] 就是一條從頂點 V1 至頂點V2 的路徑。

    152

    2 V1

    7

    4

    1

    5 6

    3 1

    V4

    V2

    V3

  • 17-4-4 動態規劃

    • 設定路徑的長度為路徑上所有的邊的加權的和。例如路徑 [V1, V3, V2] 的長度為 6 + 4 = 10。假設邊的加權不為負數,最短路徑問題是要去找出在有向及加權的圖形上任兩頂點的最短路徑。

    153

    2 V1

    7

    4

    1

    5 6

    3 1

    V4

    V2

    V3

  • 17-4-4 動態規劃

    • 假設圖形有 n 個頂點,分別是 V1, …, Vn。令d k (i, j) 表示只有經過集合 {V1, V2, …, Vk} 中某頂點的 Vi 到 Vj 的最短路徑長度,令 d

    0

    (i, j) 為 V i 到 Vj 的邊的加權。

    154

  • 17-4-4 動態規劃

    • 經過集合{V1, …,Vk}中某些頂點的 V i 到 Vj 最短路徑可能不經過 Vk,也可能經過 Vk,如果不經過 Vk,則 d

    k (i,j) = dk–1(i,j);如果經過Vk,則 d

    k(i,j) = dk–1(i,k) + dk–1(k,j),所以dk(i, j) 可由下列式子計算得到。

    155

  • 17-4-4 動態規劃

    • 對圖17.59的圖形求最短路徑,計算過程的 d 0, d 1, ……, d 4 表格列於圖 17.54。

    156

    d 0 1 2 3 4 d 1 1 2 3 4

    1 0 1 6 2 1 0 1 6 2

    2 1 0 ∞ ∞ 2 1 0 7 3

    3 5 4 0 ∞ 3 5 4 0 7

    4 3 ∞ 7 0 4 3 4 7 0

  • 17-4-4 動態規劃

    d 2 1 2 3 4 d 3 1 2 3 41 0 1 6 2 1 0 1 6 22 1 0 7 3 2 1 0 7 33 5 4 0 7 3 5 4 0 74 3 4 7 0 4 3 4 7 0

    d 4 1 2 3 41 0 1 6 22 1 0 7 33 5 4 0 74 3 4 7 0

    157

  • 17-4-4 動態規劃

    • 假設 Vi 到 Vj 的最短路徑經過 Vk,則Vi 到 Vj的路徑也是最短的路徑,而 Vk到 Vj 的路徑也是最短的。例如,由 V2 到 V4 的最短路徑為 [V2, V1, V4],而路徑 [V2, V1] 也是 V2 到 V1 的最短路徑,而路徑 [V1, V4] 也是V1 到 V4 的最短路徑。像這樣最佳解包含其組成份子的最佳解之特性,稱為「最佳化原則」 (Principle of Optimality)。

    • 如果最佳化問題能應用此最佳化原則,則可以用動態規劃策略設計遞迴運算式來求得最佳解。

    158

    Chapter 17 學習目標大綱CH17 資料結構與演算法17-1 虛擬碼17-1 虛擬碼17-1 虛擬碼17-1 虛擬碼17-1 虛擬碼17-1 虛擬碼17-1 虛擬碼17-1 虛擬碼17-1 虛擬碼17-1 虛擬碼17-1 虛擬碼17-1 虛擬碼17-1 虛擬碼17-2 基礎資料結構17-2 基礎資料結構17-2 基礎資料結構17-2-1 佇列與堆疊17-2-1 佇列與堆疊17-2-1 佇列與堆疊17-2-1 佇列與堆疊17-2-1 佇列與堆疊17-2-1 佇列與堆疊17-2-1 佇列與堆疊17-2-1 佇列與堆疊17-2-1 佇列與堆疊17-2-1 佇列與堆疊17-2-1 佇列與堆疊17-2-1 佇列與堆疊17-2-1 佇列與堆疊17-2-1 佇列與堆疊17-2-1 佇列與堆疊17-2-1 佇列與堆疊17-2-1 佇列與堆疊17-2-1 佇列與堆疊17-2-1 佇列與堆疊17-2-1 佇列與堆疊17-2-1 佇列與堆疊17-2-1 佇列與堆疊17-2-1 佇列與堆疊17-2-1 佇列與堆疊17-2-1 佇列與堆疊17-2-2 樹與二元樹17-2-2 樹與二元樹17-2-2 樹與二元樹17-2-2 樹與二元樹17-2-2 樹與二元樹17-2-2 樹與二元樹17-2-2 樹與二元樹17-2-2 樹與二元樹17-2-2 樹與二元樹17-2-2 樹與二元樹17-2-2 樹與二元樹17-2-2 樹與二元樹17-2-2 樹與二元樹17-2-2 樹與二元樹17-2-2 樹與二元樹17-2-2 樹與二元樹17-2-2 樹與二元樹17-2-2 樹與二元樹17-2-2 樹與二元樹17-2-2 樹與二元樹17-2-2 樹與二元樹17-3 基礎演算法17-3-1 排序17-3-1-1 插入排序法17-3-1-1 插入排序法17-3-1-1 插入排序法17-3-1-2 氣泡排序法 17-3-1-2 氣泡排序法17-3-1-2 氣泡排序法17-3-1-2 氣泡排序法17-3-1-2 氣泡排序法17-3-1-2 氣泡排序法17-3-1-3 兩兩合併排序法 17-3-1-3 兩兩合併排序法17-3-1-3 兩兩合併排序法17-3-1-3 兩兩合併排序法17-3-1-3 兩兩合併排序法17-3-1-3 兩兩合併排序法17-3-1-3 兩兩合併排序法17-3-1-3 兩兩合併排序法17-3-2 搜尋17-3-2 搜尋17-3-2 搜尋17-3-2 搜尋17-3-2 搜尋17-3-2 搜尋17-3-2-3 二元搜尋樹 17-3-2-3 二元搜尋樹17-3-2-3 二元搜尋樹17-3-2-3 二元搜尋樹17-3-2-3 二元搜尋樹17-3-2-3 二元搜尋樹17-4 演算法分析與策略17-4-1 算法分析17-4-1 算法分析17-4-1 算法分析17-4-1 算法分析17-4-1 算法分析17-4-1 算法分析17-4-1 算法分析17-4-1 算法分析17-4-1 算法分析17-4-1 算法分析17-4-1 算法分析17-4-1 算法分析17-4-1 算法分析17-4-1 算法分析17-4-2 個別擊破策略17-4-2 個別擊破策略17-4-2 個別擊破策略17-4-2 個別擊破策略17-4-2 個別擊破策略17-4-2 個別擊破策略17-4-2 個別擊破策略17-4-3 貪婪策略17-4-3 貪婪策略17-4-3 貪婪策略17-4-3 貪婪策略17-4-3 貪婪策略17-4-3 貪婪策略17-4-3 貪婪策略17-4-3 貪婪策略17-4-3 貪婪策略17-4-3 貪婪策略17-4-3 貪婪策略17-4-3 貪婪策略17-4-3 貪婪策略17-4-3 貪婪策略17-4-3 貪婪策略17-4-3 貪婪策略17-4-3 貪婪策略17-4-3 貪婪策略17-4-3 貪婪策略17-4-3 貪婪策略17-4-3 貪婪策略17-4-3 貪婪策略17-4-3 貪婪策略17-4-3 貪婪策略17-4-3 貪婪策略17-4-3 貪婪策略17-4-4 動態規劃17-4-4 動態規劃17-4-4 動態規劃17-4-4 動態規劃17-4-4 動態規劃17-4-4 動態規劃17-4-4 動態規劃17-4-4 動態規劃17-4-4 動態規劃17-4-4 動態規劃17-4-4 動態規劃17-4-4 動態規劃17-4-4 動態規劃