《Algorithms 4th Edition》读书笔记——2.4 优先队列(priority queue)-Ⅵ
· 学后心得体会与部分习题实现
心得体会:
曾经只是了解了优先队列的基本性质,并会调用C++ STL库中的priority_queue以及 java.util.PriorityQueue<E>中的优先队列封装类,但是没有看过源码,也并不曾知道实现方法用到了堆结构。
优先队列通过堆进行插入元素和删除最小元素的两种高效操作来维护元素集合,每个操作的时间都为对数级(logN)。堆结构及其操作符合优先队列的全部特点,另附有高效率,用来描述与实现优先队列再合适不过。
在学习过程中,在对于堆结构众多操作的巧妙操作中,令我印象深刻的是两种操作:swim() 和 sink()操作,即对于新元素或者删除最大(或最小)根节点后的新根的上浮和下沉操作(有些教材上称为上滤和下滤操作。其命名个人YY的含义是:由于swim() 和sink() 操作是用在insert() 和 delMax() 两个方法中的子方法,用于过滤二叉堆,使其重构成完全二叉树结构。)。
该方法的效率证明可以参看《Introduction to Algorithm》(《算法导论》)中的 6.2 维护堆的性质:
通过对优先队列的学习,自己用Java实现了一下。并可以用以下这个个人编写的二叉堆的实现,完成《Algorithms 4th Edition》中的2.4.1和2.4.6。
1 import java.math.*; 2 3 public class MaxPQ<Key extends Comparable<Key>> { 4 private Key[] pq; 5 private int N = 0; 6 7 @SuppressWarnings("unchecked") 8 public MaxPQ(int maxN) { 9 pq = (Key[]) new Comparable[maxN + 1]; 10 } 11 12 public boolean isEmpty() { 13 return N == 0; 14 } 15 16 public int size() { 17 return N; 18 } 19 20 public void insert(Key v) { 21 pq[++N] = v; swim(N); 22 } 23 24 private void exch(int i, int j) { 25 Key t = pq[i]; pq[i] = pq[j]; pq[j] = t; 26 } 27 28 public Key delMax() { 29 Key max = pq[1]; 30 exch(1, N--); 31 pq[N + 1] = null; 32 sink(1); 33 return max; 34 } 35 36 private boolean less(int i, int j) { 37 return pq[i].compareTo(pq[j]) < 0; 38 } 39 40 private void swim(int k) { 41 while(k > 1 && less(k / 2, k)) { 42 exch(k / 2, k); 43 k =k / 2; 44 } 45 } 46 47 private void sink(int k) { 48 while(2 * k <= N) { 49 int j = 2 * k; 50 if(j < N && less(j, j + 1)) j++; 51 if(!less(k, j)) break; 52 exch(k , j); 53 k = j; 54 } 55 } 56 57 public void prtPQ() { 58 for(int i = 1; i <= N; i++) { 59 System.out.print(pq[i] + " "); 60 } 61 System.out.println(); 62 } 63 }
以下是对应部分题目实现(只给出Main部分):
1 import java.math.*; 2 import java.util.*; 3 4 public class Main { 5 public static void main(String[] args) { 6 MaxPQ<Character> PQ = new MaxPQ<Character>(1000); 7 String op = "PRIO*R**I*T*Y***QUE***U*E"; 8 int cnt = 0; 9 for(int i = 0; i < op.length(); i++) { 10 if(op.charAt(i) == '*') { 11 System.out.println(++cnt + ". Max is " + PQ.delMax()); 12 } else { 13 PQ.insert(op.charAt(i)); 14 } 15 } 16 17 } 18 }
运行结果:
1 import java.math.*; 2 import java.util.*; 3 4 public class Main { 5 public static void main(String[] args) { 6 MaxPQ<Character> PQ = new MaxPQ<Character>(1000); 7 String op = "PRIO*R**I*T*Y***QUE***U*E"; 8 int cnt = 0; 9 for(int i = 0; i < op.length(); i++) { 10 if(op.charAt(i) == '*') { 11 PQ.delMax(); 12 } else { 13 PQ.insert(op.charAt(i)); 14 } 15 System.out.print(++cnt + ". "); 16 PQ.prtPQ(); 17 } 18 } 19 }
运行结果:
至此,对于优先队列的学习告一段落。笔者一直相信唯 有坚持者才能把算法学的精通,所以算法的学习之路好会坚持下去。一直用luc的文章 http://zh.lucida.me/blog/on- learning-algorithms/ 来激励自己,相信自己终有一天,能达到自己理想的高度。如此才不后悔。