一、简介
在很多应用中,允许用例对已经进入优先队列中的元素非常有必要。
一个简单的实现方法是对每个元素关联一个索引。
索引优先队列API:
二、实现
通过对普通优先队列的实现进行拓展来实现索引优先队列。
思路:
(1)增加数组keys用来保存优先队列元素(item or key)
(2)pq数组则改用于保存索引(pq[i] = k,k为索引)
这里的pq数组仍然保持堆有序,即pq[1]所对应的元素(keys[pq[1]])大于或等于pq[2]、pq[3]所对应的元素(keys[pq[2]]、keys[pq[3]])。
(3)增加数组qp,用于保存pq的逆序。
如果pq[i] = k,则qp[k] = i,也就是pq[qp[k]] = k。
如果k不在队列之中,则qp[k] = -1。
代码实现:
package com.qiusongde; import java.util.NoSuchElementException; public class IndexMinPQ<Key extends Comparable<Key>> { /* * k is index(0,maxN-1) * pq[i] = k means the ith node in the heap is keys[k] * qp[k] = i means keys[k] is the ith node in the heap * qp[k] = -1 if k(index) is not in the Priority Queue * */ private Key[] keys;//items with priorities private int[] pq;//binary heap using 1-based indexing private int[] qp;//inverse: qp[pq[i]] = pq[qp[i]] = i private int n;//number of elements on PQ private int maxN; public IndexMinPQ(int maxN) { if(maxN < 0) throw new IllegalArgumentException("The size of IndexMinPQ should larger than 0"); this.maxN = maxN; keys = (Key[]) new Comparable[maxN + 1]; pq = new int[maxN + 1]; qp = new int[maxN + 1]; for(int i = 0; i <= maxN; i++) qp[i] = -1; n = 0; } public boolean isEmpty() { return n == 0; } public int size() { return n; } public boolean contains(int k) { if(k < 0 || k >= maxN) throw new IndexOutOfBoundsException(); return qp[k] != -1; } public void insert(int k, Key key) { if (k < 0 || k >= maxN) throw new IndexOutOfBoundsException(); if (contains(k)) throw new IllegalArgumentException("index is already in the priority queue"); n++; qp[k] = n; pq[n] = k; keys[k] = key; swim(n); } public Key min() { if(n == 0) throw new NoSuchElementException("IndexMinPQ underflow"); return keys[pq[1]]; } public int minIndex() { if(n == 0) throw new NoSuchElementException("IndexMinPQ underflow"); return pq[1]; } public int delMin() { if(n == 0) throw new NoSuchElementException("IndexMinPQ underflow"); int indexOfMin = pq[1]; exch(1, n--); sink(1); keys[pq[n+1]] = null; qp[pq[n+1]] = -1; return indexOfMin; } public void delete(int i) { if (i < 0 || i >= maxN) throw new IndexOutOfBoundsException(); if (!contains(i)) throw new NoSuchElementException("index is not in the priority queue"); int index = qp[i]; exch(index, n--); swim(index); sink(index); keys[i] = null; qp[i] = -1; } public void changeKey(int i, Key key) { if (i < 0 || i >= maxN) throw new IndexOutOfBoundsException(); if (!contains(i)) throw new NoSuchElementException("index is not in the priority queue"); keys[i] = key; swim(qp[i]); sink(qp[i]); } /*************************************************************************** * General helper functions. ***************************************************************************/ private boolean greater(int i, int j) { return keys[pq[i]].compareTo(keys[pq[j]]) > 0; } private void exch(int i, int j) { int swap = pq[i]; pq[i] = pq[j]; pq[j] = swap; qp[pq[i]] = i; qp[pq[j]] = j; } /*************************************************************************** * Heap helper functions. ***************************************************************************/ private void swim(int k) { while (k > 1 && greater(k/2, k)) { exch(k, k/2); k = k/2; } } private void sink(int k) { while (2*k <= n) { int j = 2*k; if (j < n && greater(j, j+1)) j++; if (!greater(k, j)) break; exch(k, j); k = j; } } }
public static void main(String[] args) { // insert a bunch of strings String[] strings = { "it", "was", "the", "best", "of", "times", "it", "was", "the", "worst" }; IndexMinPQ<String> pq = new IndexMinPQ<String>(strings.length); for (int i = 0; i < strings.length; i++) { pq.insert(i, strings[i]); } // delete and print each key while (!pq.isEmpty()) { int i = pq.delMin(); StdOut.println(i + " " + strings[i]); } StdOut.println(); }
3 best 0 it 6 it 4 of 8 the 2 the 5 times 7 was 1 was 9 worst
三、应用
多向归并问题:
将多个有序的输入流归并成一个有序的输入流。使用优先队列,无论输入有多长都可以将这些数据读入并归并。
public class Multiway { // This class should not be instantiated. private Multiway() { } // merge together the sorted input streams and write the sorted result to standard output private static void merge(In[] streams) { int n = streams.length; IndexMinPQ<String> pq = new IndexMinPQ<String>(n); for (int i = 0; i < n; i++) if (!streams[i].isEmpty()) pq.insert(i, streams[i].readString()); // Extract and print min and read next from its stream. while (!pq.isEmpty()) { StdOut.print(pq.minKey() + " "); int i = pq.delMin(); if (!streams[i].isEmpty()) pq.insert(i, streams[i].readString()); } StdOut.println(); } /** * Reads sorted text files specified as command-line arguments; * merges them together into a sorted output; and writes * the results to standard output. * Note: this client does not check that the input files are sorted. * * @param args the command-line arguments */ public static void main(String[] args) { int n = args.length; In[] streams = new In[n]; for (int i = 0; i < n; i++) streams[i] = new In(args[i]); merge(streams); } }