1 Chapter 13 Priority Queues. 2 Chap.13 Contents 13.1 Introduction 13.2 The PurePriorityQueue...

Post on 19-Jan-2016

224 views 4 download

Transcript of 1 Chapter 13 Priority Queues. 2 Chap.13 Contents 13.1 Introduction 13.2 The PurePriorityQueue...

1

Chapter 13

Priority Queues

2

Chap.13 Contents

13.1 Introduction13.2 The PurePriorityQueue Interface

13.3 Implementations of the PurePriorityQueue Interface 13.3.2 The Heap Implementation of the PurePriorityQueue Interface

13.4 Application: Huffman Codes

3

13.1 Introduction

A priority queue (PQ) is an interface (property) in which access or deletion is of:

the highest-priority element,

according to some method of

assigning priorities to elements.

4

舉個例子, 假設 hospital emergency room (急診室) 有四個病患, 並有下列三種資料:

姓名, 受傷程度, 傷勢

5

姓名 受傷程度 傷勢

張三 80 足踝扭傷 李四 45 腿斷了 王五 80 足踝扭傷 林二 20 頭部中彈 四位病患該以怎樣的順序來排隊看診?

6

規則為: 受傷程度最小值 表示最嚴重 有 最高優先權 (highest priority). 所以,急診室前排了 priority queue 如下:

20(林二) 45(李四) 80(張三)80(王五) 可用下面 interface來實作優先權的比較: 1) The comparable interface or

2) The comparator interface.

急診室

7

13.2 The PurePriorityQueue Interface

public interface PriorityQueue {

/*** size 回傳 PurePriorityQueue 中元素的個數*/int size ( )

8

/* isEmpty 看 PurePriorityQueue * 是否沒有元素 .

** return true – if PurePriorityQueue* 沒有元素 ;* otherwise, return false ;*/boolean isEmpty ( )

9

/*** add 加元素到 PurePriorityQueue .** The worstTime(n) is O(n).** @param element – 要插入 PurePriorityQueue 的元素*/void add (E element)

10

/* getMin * 回傳 PurePriorityQueue 中的最高優先權元素 *

* The worstTime(n) is O (1).** @return PurePriorityQueue 的最高優先權元素** @throws NoSuchElementException – * if PurePriorityQueue 是空的*/E getMin ( )

11

/* removeMin* 從 PurePriorityQueue 移除具有最高優先權的元素

** The worstTime(n) is O (log n).** @return 被移除的元素**@throws NoSuchElementException – * if PurePriorityQueue 是空的*/E removeMin ( )

12

13.3 Implementations of The PurePriorityQueue Interface

有三種 data structures 可實作之,分別是 :

1) linked list

2) tree set

3) heap

1. Use Linked List

13

14

public class LinkedPQ <E> impelments

PurePriorityQueue <E> {

LinkedList <E> list ;

Comparator <E> comparator ;

15

public LinkedPQ()

{list = new LinkedList<E>( ) ;

comparator = null ; }

public LinkedPQ (Comparator<E> comp)

{this() ; comparator = comp ;}

public int size() { return list.size() ; }

public E getMin() { return list.getFirst() ; }

public E removeMin(){ return list.removeFirst() ;}

16

下頁 add(): 假設有個 element 優先權為 22, 並有個 linked list 實作的 priority queue 如下:

10, 12, 18, 25, 39 當 element < itr.next( ), loop 會終止,

也就是說,,當 22 < 25 會離開 loop 但因為 itr 目前指到 39之前 25之後 所以須 將 itr 倒退一位到 25之前 18之後. add 22 即可加到 18 與 25之間

17

public void add (E element){

if /* 空或 element優先權比最後的大 */

(list.isEmpty( ) || compare (element, list.get (list.size( ) – 1)) >= 0)

/* 直接 add此 element*/ list.add (element) ;

else {/*找適當位置 add 使優先權由小到大 */ ListIterator<E> itr = list.listIterator( );

while (itr.hasNext() && compare (element, itr.next( )) >= 0) ;

/*倒退一位 , 再 add*/ itr.previous( ) ; itr.add (element) ;

}} //end of add

WorstTime (n) is linear in n.

18

protected int compare (Object elem1, Object elem2) { /*IF comparator = null,

回傳 elem1.compareTo (elem2)的結果。 OTHERWISE, 回傳 comparator.compare (elem1, elem2) 的結果。*/

return (comparator==null ? ((Comparable)elem1).compareTo(elem2)

: comparator.compare (elem1, elem2)); } // end of compare

19

2. Use Tree Set

20

public class TreeSetPQ<E> implements PriorityQueue<E> {

TreeSet<E> set;

Comparator<E> comparator;

// the 2 constructors, size, isEmpty and compare

// methods 與 LinkedPQ class 的類似

public TreeSetPQ (Comparator<E> c) {

comparator = c ; set = new TreeSet<E> (c);

} // one-parameter constructor

21

public void add (E element) {set.add (element);}

public E getMin ( ) {return set.first( );}

public E removeMin ( ) {E temp = set.first( ); set.remove (set.first( )); return temp;}

} // end of class TreeSetPQ

For these three methods, worst time (n) is logarithmic in n.

3. Use Heap

22

23

現在來看完全不一樣的!

public class Heap implements PriorityQueue {

尚未納入 Java collections framework

什麼是 Heap?

24

Heap [Collins English Dictionary]

A collection of articles or mass of material gathered together in one place. 這定義用於 compiler, OS.Ex: Heap storage (堆 ) vs. stack storage ( 疊 ) in main

memory.

25

但是, Data Structure 中的 Heap , 有不同的定義 :

Heap 是一棵 tree , 且任何一個節點比 所有 descendants ( 後代 ) 都小

26

Recall that: A complete binary tree is full Except at the lowest level,

where all items are as far to the left as possible.

27

heap t 是 complete binary tree , 且 t是空的 或 :

1. root element是 t中最小元素 2. t 的左右子樹 (sub-tree) 都是 heap.

28

26 index 0

40 31 index 2 48 50 85 index 5 36 107 48 55 57 88 index 11

29

heap 不是 binary search tree!

因為對 root 而言binary search tree 是 左小右大

heap 則是上 (root)小 下 ( 左右 ) 大 這叫 min heap (minimal element at root)

另有 max heap (maximal element at root)

則是下 ( 左右 ) 小 上 (root) 大

30

跟 complete binary tree一樣, Heap可以 Array方式儲存:

root element 為 index 0, root element 的 left child 為 index 1, 以此類推:

index 0 1 2 3 4 5 6 7 8 9 10 11

26 40 31 48 50 85 36 107 48 55 57 88

31

如同我們在 Chapter 9看到的,

當 complete binary tree 被儲存成 array時,

從 parent index 走到 child index 以及 從 child index 走到 parent index的

執行時間是 constant-time

32

下列的學生分數程式,創出 Heap 結構 ,並執行 Heap 動作 : add 和 removeMin

請輸入學生姓名及 GPA 或 ***( 結束 )”;

Mary 4.0 (red indicates user input)

John 3.5

***

系統輸出如下John 3.5

Mary 4.0

33

public static void main(String[] args){ final String PROMPT= “ 請輸入學生姓名及 GPA 或 ***( 結束 )”; final String RESULTS =“\n 學生姓名及 GPA 如下” ; String line ;

Heap<Student> heap = new Heap<Student>( ) ; BufferedReader keyboardReader = new BufferedReader (new InputStreamReader(System.in) ) ; try{ while(true) {System.out.print (PROMPT) ;

line = keyboardReader.readLine() ; if (line.equals(“***”)) break ;

heap.add (new Student(line) );} //while

system.out.println (RESULTS); while(!heap.isEmpty()) System.out.println (heap.removeMin() ) ;

}//try catch(Exception e){ System.out.println(e) ;}} // end of main

34

import java.util.* ;

public class Student implements Comparable { protected String name ; protected double gpa ;

/** Student 從特定的 String s 初始化 Student object.** @param s – String 初始化 Student object.*@throws NullPointerException, NoSuchElementException,* NumberFormatException*/

public Student (String s){StringTokenizer tokens = new StringTokenizer(s) ;name = tokens.nextToken() ;gpa = Double.parseDouble( tokens.nextToken() ) ;

} // constructor

public String toString() {return name+“ “+ gpa;}

} // end of class Student

35

Data Structure in the heap class:

protected E[ ] heap; protected int size; protected Comparator<E> comparator;

13.3.2 The Heap Implementation of the PurePriorityQueue Interface

36

heap [j]

heap [2 * j + 1] heap [2 * j + 2]

37

heap [(j – 1) / 2]

heap [j] heap [j + 1]

38

Implementation of the heap class

public Heap( ) { final int DEFAULT_INITIAL_CAPACITY = 11; heap = (E[ ]) new Object [DEFAULT_INITIAL_CAPACITY]; } // default constructor

39

// add an element to the heap // Postcondition: element 加入 heap // worstTime (n) is O(n), // averageTime (n) is constant. public void add (E element) { /* 1.假設 heap 現有三元素(size為 3) 容量(length)為 10 size先加 1 (為 4) 未達 10 故不 resize(容量加倍)*/ if (++size == heap.length) /* then resize */ { E[ ] newHeap = (E[]) new Object [2 * heap.length]; System.arraycopy (heap, 0, newHeap, 0, size); heap = newHeap ; } // end if /* 2.heap 現有三元素的 array index為 0,1,2 下一元素放最後 即 index為 3 即 size (4) 減 1*/

heap [size - 1] = element;

/* 3. 最後元素向上滲透*/ percolateUp ( ); } // end of add

40

加入 30 到下列的 Heap: 26

32 31 48 50 85 36 107

41

26

32 31 48 50 85 36 107 30 percolateUp( ): 30 藉 swapping往上滲透 , 直到調整成 Heap結構

42

26

32 31 30 50 85 36 107 48

43

26 30 31 32 50 85 36 107 48

44

Protected void percolateUp(){ // 設定 child 為 最後一個 node (size -1) int child = size -1; int parent; Object temp ;

while(child > 0){ parent = (child-1)/2; // 如果 parent <= child 不用做了 if(compare(heap[parent],heap[child] <= 0) break;

// 否則 swap parent 與 child temp=heap[parent];heap[parent]=heap[child];heap[child]=temp; /*child 向上走一步 */ child = parent; }//end while} //end of percolateUp

45

// Precondition: Heap不為空 // Otherwise, 拋出 NoSuchElementException // Postcondition: 刪除 Heap最高優先權元素,並回傳之 // // The worstTime(n) is O(log n). public E removeMin ( ) { if (size==0) throw new NoSuchElementException ("Priority queue empty.");

/*1.假設 heap有三元素其 index 0 1 2, size 為 3 index 0 為 minimal element*/E minElem=heap[0]; /*2.移最後元素(index 2, 即 size 減 1)到 index 0 heap [0] = heap [size–1]; /*3. size 少 1 變成 2*/ size--;

/* 4 index 0元素向下滲透*/ percolateDown (0); /* 5*/ return minElem; } // end of removeMin

46

26 30 31 32 50 85 36 107 48 當 removemin( ) 被呼叫, 48 會與 26 交換,並將 size 從 9變小為 8。

47

48 30 31 32 50 85 36 107 percolateDown (0): 48 (at index 0) 持續與 children swapping向下滲透,直到調整成 heap結構。

48

30 48 31 32 50 85 36 107

49

30 32 31 48 50 85 36 107

50

Protected void percolateDown(int start){ // parent 為 tree 的 root (index 0 ) // child 為 root 的左子樹 int parent = start, child = 2*parent + 1; Object temp ; while(child < size){

// 如 child 有右兄弟 且 右兄弟較小 則設定 child 為右兄弟 if(child < size – 1 && compare(heap[child],heap[child+1])> 0) child ++;

// 如 parent <= child 不用再比了

if(compare(heap[parent],heap[child] <= 0) break;// 否則 swap parent 與 child

temp = heap[child]; heap[child] = heap[parent];heap[parent] = temp;

// child 向下走一步 parent = child; child = 2 * parent +1; }//while} // end of percolateDown

51

Exercise: 畫出最後的 Heap結構 Heap<Integer> myHeap = new Heap<Integer>( ); myHeap.add (new Integer (60)); myHeap.add (new Integer (50)); myHeap.add (new Integer (40)); myHeap.add (new Integer (30)); myHeap.add (new Integer (20)); myHeap.add (new Integer (10)); myHeap.removeMin( ); myHeap.removeMin( ); myHeap.removeMin( );

52

40

60 50

實際儲存於 array: 40 60 50 index: 0 1 2

53

13.4 Application: Huffman Codes

問題: 如何 encode message 使之為最節省空間的 code (用 bit表示), 傳輸 code 至遠端後, 遠端可 decode code還原為 message

54

例子: m (message) 為 100,000 個下面字母組成:

‘a’, b’, ‘c’, ‘d’, ‘e’.

encode(編碼) m 成 bit組成的 code叫 e

55

在 Java unicode編碼中, 每個字母編成 16 bits. 如採用此,

|e| 表示 e的 size 為:

1,600,000 bits

56

假設每一字母都使用固定數目的 bits 最少需要多少 bits,才能將 5個字母編碼,而且是唯一編碼?

57

若每個字母所表示的 bits數目相同 將 5個字母編碼成唯一編碼, 則每個字母需要

3 bits

58

將 n個字母編碼成唯一編碼 需要最少 bits數為:

the number of bits in the binary representation of n:

ceil (log2 n)

ceil(x): 大於或等於 x的最小整數

59

這是可能的編碼:

‘a’ 000 ‘b’ 001 ‘c’ 010 ‘d’ 011 ‘e’ 100

60

|e| = 100000 * 3 =

300,000 bits

61

可以更好一點嗎? 可以! 希望 n個字母的編碼中, 不需每個字母都要 ceil (log2 n)個 bits 就像這樣子:

‘a’ 0 ‘b’ 1 ‘c’ 00 ‘d’ 01 ‘e’ 10

62

|e| << 300,000 bits

可是…

63

有矛盾的狀況產生: 001010 解碼 (decode) 成:

adda or cee or …

64

產生上述矛盾, 是因為有些字的編碼 是其他字編碼的 Prefix 例如 :

a 0 c 00

(a是 c 的 prefix)

65

採用 prefix-free (無 prefix) 的編碼方式才會

沒有矛盾狀況 (unambiguous)

66

要得到 prefix-free encoding, 必須創造 binary tree:

左邊分支(branch) 表示 0 bit 右邊分支(branch) 表示 1 bit

而每個字母位在 leaf

67

0 1

0 1 0 1 c d b 0 1

a e

68

因為每個字母都是 leaf, 各字母從 root到 leaf不會走同樣路徑 這實現了 prefix-free (unambiguous) 編碼 如: ‘a’ 010 ‘b’ 11 ‘c’ 00 ‘d’ 10 ‘e’ 011

69

這是另一棵 unambiguous 編碼的 binary tree:

0 1 0 1 b c 0 1 a 0 1 e d

70

要決定 |e|, 我們必須知道:

在 message中, 各字母的 frequency.

71

Huffman Tree 是根據: message(m)中字母的 frequency 創 minimal prefix-free encoding 的 binary tree

72

將出現頻率最少 (least-frequently) 的字母, 放在離 root最遠的 leaf 使它的編碼會有最多 bits 例如, “a” 編碼成 “1100” 有 4 bits.

73

假設 frequencies 如下:

‘a’ 5,000 ‘b’ 20,000 ‘c’ 8,000 ‘d’ 40,000 ‘e’ 27,000

left-branch for least(最小) right-branch for next-to-least(次小)

74

0 1

a c

75

0 1 a c 結果如何? 總共的 frequencies 是 13,000. 比 b的 frequency (20,000) 少, 所以我們創造上層 2分支 (branches):

這棵 subtree 會是左分支, 而 b 會是右分支 (見下頁)

76

0 1 b 0 1 a c

77

這 subtree的 frequencies 是 33000 (次小)

e 的 frequencies 是 27000 (最小) 所以這 subtree要成為上層右子樹,

e要成為上層左子樹 (見下頁)

78

0 1

e 0 1 b 0 1

a c

79

最後, 這 subtree的 frequencies 是 60000 (次小),

d的 frequencies 是 40000 (最小) 所以這 subtree要成為上層右子樹,

d成為上層左子樹

80

0 1

d 0 1

e 0 1

0 1 b

a c

81

‘a’ 1100 ‘b’ 111 ‘c’ 1101 ‘d’ 0 ‘e’ 10 What is the bits for the message “acceded”? What is the message for the bits “11001110101101”? See answers on next page.

82

Answers: “acceded” 110011011101100100 11001110101101 -> “abdec”

83

|e| =

字母的 bits數

乘以

字母的 frequnecy

84

|e| = 4 * 5000 +

3 * 20000 + 4 * 8000 + 1 * 40000 + 2 * 27000

= 206,000 bits 遠小於前面提到的 1600,000 bits

及 300,000 bits

85

我們用 priority queue 儲存字母的 frequencies和 subtrees add: 插入字母的 frequency 到 priority queue removeMin: 從 priority queue刪除 lowest frequency (最小)

86

在 priority queue中, 每個 element由 character-frequency成對組成, 加上 left, right, parent pointers. 例如,假設 character-frequency 為:

(a: 34000) (b: 20000) (c: 31000) (d: 10000) (e: 5000)

87

priority queue中 element的順序為:

e 5000 (lowest frequency) d 10000 b 20000 c 31000 a 34000

getmin( ) 回傳最高優先權 (highest-priority) 也就是 lowest-frequency的 element 即 e 5000

88

removeMin被呼叫兩次: 第一個最小 element 成為 left branch,

第二個次小 element 成為 right branch, 加總其 frequencies (15000) 再加入 priority queue

( : 15000) (b: 20000) (c: 31000) (a: 34000) 所以目前的 Huffman tree 為: 15000 0 1 e d

89

再一次, removeMin 又呼叫兩次, 而 frequencies的總和也被加入 PRIORITY QUEUE:

(c: 31000) (a: 34000) ( : 35000) 所以目前的 Huffman tree 為: 35000 0 1 15000 b 0 1 e d

90

又一次, removeMin又呼叫兩次, 在新的 HUFFMAN TREE 中,elements 變成了 leaves ,

而 frequencies的總和也加入 PRIORITY QUEUE:

( : 35000) ( : 65000)

目前的 Huffman tree 為:

35000 0 1 15000 b 65000 0 1 0 1 e d c a

91

最後,( : 35000)和 ( :65000) 也從 priority queue移除, 並將總和加入 priority queue,, 目前 priority queue 為:

( : 100000).

最後的Huffman tree 為:

92

100000 0 1

35000 0 1

15000 b 65000

0 1 0 1

e d c a

93

Exercise: create a minimal, prefix-free encoding for the following character-frequency pairs: ‘a’ 20,000 ‘b’ 4,000 ‘c’ 1,000 ‘d’ 17,000 ‘e’ 25,000 ‘f’ 2,000 ‘g’ 3,000 ‘h’ 28,000 Note: you will need to maintain the priority queue, ordered by frequencies.

94

13.4.3 The Huffman Encoding Project

重複以下動作: 從 priority queue中, 移除兩個最小 frequency的元素, 然後創造 huffman tree的 subtree,

最後將得到 minimal (prefix-free) encoding.

95

Huffman Class 會:

encode message 成為 code

首先看看 Entry Class:

96

class Entry implements Comparable { int freq; // ex 31000 String code;// ex 10 ENCODING char id; // ex c

Entry left, //ex null right, //ex null BINARY TREE parent;// node (entry) with freq. 65000 public int compareTo (Object entry) {return freq - ((Entry)entry).freq; } } // class Entry

97

protected Entry [ ] leafEntries; ARRAY INDEX = ASCII FOR CHARACTER ‘A’ 65 That is, (int)’A’ = 65 ‘0’ 48 (int)’0’ = 48

98

例如, 因為 (int)’c’ = 99, leafEntries array其中的一個 entry如下

leafEntries [99] 31000 10 c

65000 65000

null null

99

Huffman Class的 Method interfaces :

100

// Postcondition: 這個 Huffman object 已初始 public Huffman( ); // Postcondition: The file name in the input line s has // been processed. public void processInput (String s); // Postcondition: 從輸入資料創出 priority queue 叫 pq // The worstTime(n) is O(n). public void createPQ( ) throws IOException;

101

// Postcondition: 創出 Huffman tree // The worstTime(n) is constant. public void createHuffmanTree( ); // Postcondition: 計算過 Huffman codes The worstTime(n) is constant. public void calculateHuffmanCodes( ); // Postcondition: Huffman codes和編碼後訊息存檔 // The worstTime (n) is O (n). public void saveToFile() throws IOException;

102

Huffman Class中的 Data Structure: 除了 1) leafEntries, 還需要: 2) a graphical user interface (gui) and

3) a priority queue (pq). 還有, 4) a file reader, 5) a file writer, 和 6) a boolean variable 來判斷

哪一個檔案名稱被讀取.

103

最後, 儲存 7) the input file name 我們開啟 input file兩次: 一次 計算 frequencies 另一次 編碼 message

104

// Data structure

protected Entry [ ] /*1*/ leafEntries; protected GUI /*2*/ gui; protected PriorityQueue /*3*/ pq; protected BufferedReader /*4*/ fileReader; protected PrintWriter /*5*/ fileWriter; protected boolean /*6*/ readingInFileName; protected String /*7*/ inFileName;

105

這是 4 個 Huffman class methods的 high-level pseudocodes : 跟預期一樣 processinput 是 high-level controller: 它創造了: 1) the priority queue (pq) and

2) the Huffman tree, 然後 3) calculates the Huffman codes and

4) saves the codes and encoded message

to a file.

106

1) The createPq method 1. 創造 leafEntries, 每個 ASCII character都有 entry

2. 讀入每一行資料時,更新每個 character entry中的 freq field 3. 利用 frequencies 創造出 pq

107

2) The createHuffmanTree method loop until pq.size( ) = 1. 1. 移除兩個 entries

變成 Huffman tree中的 左 (code = “0”) 右 (code = “1”) 分支 2. 建立新 entry,並加入到 pq,

而新 entry 的 frequency是: 被移除兩 entries 的 frequencies總和

end loop

108

3) The calculateHuffmanCodes method for each entry in leafEntries whose frequency is > 0

loop until the entry has no parent 1.the entry’s code field

is prepended to (加在前面) an initially empty string variable code

2.the entry is replaced with the entry’s parent.

end loop

end for

109

例如, 假設部分 Huffman tree如下( 字母 c 的 code): 1 0

c

110

由下到上計算‘c’ 的 code:

0 1加在 0前面 10

因為 (int)‘c’ 為 99, “10” 儲存在 leafEntries [99] 裡的 code FIELD中 就是: c 字母的 code 是 10 兩個 bits

111

最後, 4) saveToFile method 含兩部份: 1.loop through leafEntries, 將每個 character和對應的 code, 存到 output file

2. 重新讀入 input file 產生 message編碼後的 bit 組成的 code 並存到 output file

112

Exercise: 下為輸出檔, 請將之解碼(decode)

11010 END OF LINE MARKER 011 BLANK . 1100 PERIOD a 11011 e 00 h 010 l 111 LOWER-CASE L s 10 ** 1001000011100011111110011100011011011100100011111110110011010

ANS: she sells sea shells.