排序---优先队列
写在前面的话:
一枚自学Java和算法的工科妹子。
- 算法学习书目:算法(第四版) Robert Sedgewick
- 算法视频教程:Coursera Algorithms Part1&2
本文是根据《算法(第四版)》的个人总结,如有错误,请批评指正。
一、优先队列介绍
优先队列是0个或多个元素的集合,每个元素都有一个优先权或值,对优先队列执行的操作有1) 插入元素;2) 删除最大(最小)元素.
在最小优先队列(min priority queue)中,删除操作用来删除优先权最小的元素;在最大优先队列(max priority queue)中,删除操作用来删除优先权最大的元素。
优先队列的使用和队列(删除最老的元素)以及栈(删除最新的元素)类似,但高效的实现它则更具有挑战性。
优先队列是一种抽象数据类型,它表示了一组值和对这些值的操作。优先队列(最大优先队列为例)的API设计如下:
设计优先队列API,我们想实现的功能:输入N个字符串(N非常大),每个字符串都对应着一个整数,目标就是从中找出最大的(或最小的)M个整数及其关联的字符串。在上述API中着重要关注的方法就是插入insert()和删除最大元素delMax().
解决方法:
1.将输入排序然后从中找出M个最大的元素,但是因为输入元素非常庞大,造成存储空间的浪费;
2.将每个新的输入和已知的M个最大元素对比,这会导致比较的时间复杂度为NM;
3.以下会介绍基于堆的优先队列实现,降低时间和空间复杂度。
表1 从N个输入中找到最大的M个元素所需的成本
示 例 | 时间复杂度 | 空间复杂度 |
排序算法的用例(方法一) | NlgN | N |
初级实现的优先队列(方法二) | NM | M |
基于堆的优先队列(方法三) | NlgM | M |
优先队列的初级实现:
1.数组实现(无序)
- 插入操作:和栈的push()方法完全一样;
- 删除操作:添加一段类似于选择排序的内循环,将最大元素和边界元素交换,然后删除,和pop()操作一样;
1 public class UnorderedMaxPQ<Key extends Comparable<Key>> 2 { 3 private Key[] pq; // pq[i] = ith element on pq 4 private int N; // number of elements on pq 5 6 public UnorderedMaxPQ(int capacity){ 7 pq = (Key[]) new Comparable[capacity]; 8 } 9 10 public boolean isEmpty(){ return N == 0; } 11 12 public void insert(Key x){ pq[N++] = x; } 13 14 public Key delMax(){ 15 int max = 0; 16 for (int i = 1; i < N; i++) 17 if (less(max, i)) max = i; 18 exch(max, N-1); 19 return pq[--N]; 20 } 21 }
2.数组实现(有序)
- 插入操作:将所有较大的元素向右边移动一个使数组保持有序,类似于插入排序;
- 删除操作:和栈的pop()操作一样;
表2 优先队列的各种事先在最坏情况下运行时间的增长数量级
数据结构 | 插入元素 | 删除最大元素 |
无序数组 | N | 1 |
有序数组 | 1 | N |
堆 | lgN | lgN |
二、二叉堆的介绍
在二叉堆数组中,每个元素都要保证大于等于另两个特定位置的元素。从a[k]向上一层就令k=k/2,向下一层就令K=2k或2k+1。一颗大小为N的完全二叉树的高度为lgN向下取整。
堆有序化操作:
(1)由下至上的堆有序化(上浮)
1 private void swim(int k) { 2 while (k > 1 && less(k/2, k)) { 3 exch(k, k/2); 4 k = k/2; 5 } 6 }
(2)由上至下的堆有序化(下沉)
1 private void sink(int k) { 2 while (2*k <= N) { 3 int j = 2*k; 4 if (j < N && less(j, j+1)) j++; 5 if (!less(k, j)) break; 6 exch(k, j); 7 k = j; 8 } 9 }
三、基于堆的优先队列
1.如何实现?
- 优先队列有一个基于堆的完全二叉树表示,存储于数组pq[1..N]中,pq[0]没有使用。
- 在insert()中,我们将N加1并把新元素添加在数组最后,然后用swim()恢复堆的秩序。
- 在delete()中,我们从pq[1]中得到需要的返回元素,然后将pq[N]移动到pq[1],将N减1并使用sink()恢复堆的秩序。
- 同时我们还将不在使用的pq[N+1]设置为null,以便系统回收删除元素所占用的空间,也使用了动态调整数组大小的方法,具体见下压栈(LILO)详解。
2.性能分析
对于一个含有N个元素的基于堆的优先队列,插入元素操作只需不超过(lgN+1)次比较,删除最大元素的操作需要不超过2lgN次比较,以下是代码实现:
1 import java.util.Comparator; 2 import java.util.Iterator; 3 import java.util.NoSuchElementException; 4 5 public class MaxPQ<Key> implements Iterable<Key> { 6 private Key[] pq; // 完全二叉树 7 private int n; // 优先队列中元素数目 8 private Comparator<Key> comparator; // optional Comparator 9 10 public MaxPQ(int initCapacity) { 11 pq = (Key[]) new Object[initCapacity + 1]; 12 n = 0; // pq[0]没有使用 13 } 14 15 public MaxPQ() { this(1);} 16 17 public MaxPQ(int initCapacity, Comparator<Key> comparator) { 18 this.comparator = comparator; 19 pq = (Key[]) new Object[initCapacity + 1]; 20 n = 0; 21 } 22 23 public MaxPQ(Comparator<Key> comparator) { 24 this(1, comparator); 25 } 26 27 public MaxPQ(Key[] keys) { 28 n = keys.length; 29 pq = (Key[]) new Object[keys.length + 1]; 30 for (int i = 0; i < n; i++) 31 pq[i+1] = keys[i]; 32 for (int k = n/2; k >= 1; k--) 33 sink(k); 34 assert isMaxHeap(); 35 } 36 37 public boolean isEmpty() {return n == 0; } 38 39 public int size() {return n;} 40 41 public Key max() { 42 if (isEmpty()) throw new NoSuchElementException("Priority queue underflow"); 43 return pq[1]; 44 } 45 46 private void resize(int capacity) { 47 assert capacity > n; 48 Key[] temp = (Key[]) new Object[capacity]; 49 for (int i = 1; i <= n; i++) { 50 temp[i] = pq[i]; 51 } 52 pq = temp; 53 } 54 55 public void insert(Key x) { 56 57 // double size of array if necessary 58 if (n >= pq.length - 1) resize(2 * pq.length); 59 60 // add x, and percolate it up to maintain heap invariant 61 pq[++n] = x; 62 swim(n); 63 assert isMaxHeap(); 64 } 65 66 public Key delMax() { 67 if (isEmpty()) throw new NoSuchElementException("Priority queue underflow"); 68 Key max = pq[1]; 69 exch(1, n--); 70 sink(1); 71 pq[n+1] = null; // to avoid loiterig and help with garbage collection 72 if ((n > 0) && (n == (pq.length - 1) / 4)) resize(pq.length / 2); 73 assert isMaxHeap(); 74 return max; 75 } 76 77 private void swim(int k) { 78 while (k > 1 && less(k/2, k)) { 79 exch(k, k/2); 80 k = k/2; 81 } 82 } 83 84 private void sink(int k) { 85 while (2*k <= n) { 86 int j = 2*k; 87 if (j < n && less(j, j+1)) j++; 88 if (!less(k, j)) break; 89 exch(k, j); 90 k = j; 91 } 92 } 93 94 private boolean less(int i, int j) { 95 if (comparator == null) { 96 return ((Comparable<Key>) pq[i]).compareTo(pq[j]) < 0; 97 } 98 else { 99 return comparator.compare(pq[i], pq[j]) < 0; 100 } 101 } 102 103 private void exch(int i, int j) { 104 Key swap = pq[i]; 105 pq[i] = pq[j]; 106 pq[j] = swap; 107 } 108 109 // is pq[1..N] a max heap? 110 private boolean isMaxHeap() { 111 return isMaxHeap(1); 112 } 113 114 // is subtree of pq[1..n] rooted at k a max heap? 115 private boolean isMaxHeap(int k) { 116 if (k > n) return true; 117 int left = 2*k; 118 int right = 2*k + 1; 119 if (left <= n && less(k, left)) return false; 120 if (right <= n && less(k, right)) return false; 121 return isMaxHeap(left) && isMaxHeap(right); 122 } 123 124 public Iterator<Key> iterator() { 125 return new HeapIterator(); 126 } 127 128 private class HeapIterator implements Iterator<Key> { 129 130 // create a new pq 131 private MaxPQ<Key> copy; 132 133 // add all items to copy of heap 134 // takes linear time since already in heap order so no keys move 135 public HeapIterator() { 136 if (comparator == null) copy = new MaxPQ<Key>(size()); 137 else copy = new MaxPQ<Key>(size(), comparator); 138 for (int i = 1; i <= n; i++) 139 copy.insert(pq[i]); 140 } 141 142 public boolean hasNext() { return !copy.isEmpty(); } 143 public void remove() { throw new UnsupportedOperationException(); } 144 145 public Key next() { 146 if (!hasNext()) throw new NoSuchElementException(); 147 return copy.delMax(); 148 } 149 } 150 }
3.堆排序
1 public class Heap 2 { 3 public static void sort(Comparable[] a){ 4 int N = a.length; 5 for (int k = N/2; k >= 1; k--) sink(a, k, N); 6 7 while (N > 1){ 8 exch(a, 1, N); 9 sink(a, 1, --N); 10 } 11 } 12 13 private static void sink(Comparable[] a, int k, int N) 14 { /* as before */ } 15 private static boolean less(Comparable[] a, int i, int j) 16 { /* as before */ } 17 private static void exch(Comparable[] a, int i, int j) 18 { /* as before */ } 19 }
作者: 邹珍珍(Pearl_zhen)
出处: http://www.cnblogs.com/zouzz/
声明:本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出 原文链接 如有问题, 可邮件(zouzhenzhen@seu.edu.cn)咨询.