优先队列和堆排序

1. 优先队列

优先队列支持的两种操作:删除最大(小)元素,插入元素。和队列以及栈类似。

可以将优先队列比作一个黑盒:里面存放最大(小)的若干元素,支持向里面添加元素,取出删除最大(小)元素。

jdk自带实现:PriorityQueue.

jdk还有双向顺序队列ArrayDeque和双向链式队列LinkedList。

实现方法:

  1).数组无序实现:insert()方法和栈的push()方法一样,删除最大元素可以内循环找出最大元素然后和边界元素交换删除。

  2).数组有序实现:插入时保持数组有序,删除时直接删除边界元素。

  3).链表实现。

  4).堆实现。

  比较:

数据结构 插入元素 删除元素
有序数组 N 1
无序数组 1 N
logN logN
理想情况 1   1

2.堆

用完全二叉树来表示堆,用数组即可实现。为方便表示不使用数组第一个位置,根节点在位置1,设一节点在位置k,则它的子节点在2k和2k+1,父节点在k/2。

堆里面最重要的操作就是要保证堆有序的两个操作:上浮swim()和下沉sink()。上浮是以此节点开始,比较它和父节点的值,若不满足堆,则交换位置,直到根节点。下沉类似:和其子节点比较,不满足堆则交换,直到到达堆的底部。

插入操作:将新元素放到数组末尾,增加堆的大小并上浮新元素到合适位置。

删除最大(小)元素:从数组顶端删除最大(小)元素,并将数组最后一个元素放到顶端,减小堆大小,下沉该元素到合适位置。

用堆实现的优先队列:

 1 //优先队列,用堆实现
 2 //可以向队列插入数据,并删除最大的数,插入删除的的复杂度都是logN
 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     public int size(){
16         return N;
17     }
18     public void insert(Key v){
19         pq[++N] = v;
20         swim(N);
21     }
22     private void swim(int n) {
23         while(n > 1 && less(n/2,n)){
24             exch(n/2,n);
25             n = n/2;
26         }
27     }
28 
29     private boolean less(int i, int j) {
30         return pq[i].compareTo(pq[j]) < 0;
31     }
32 
33     public Key delMax(){
34         Key max = pq[1];
35         exch(1,N--);
36         pq[N=1] = null;        //防止对象游离
37         sink(1);
38         return max;
39     }
40 
41     private void sink(int i) {
42         while(2 * i <= N){
43             int j = 2*i;
44             if(j < N && less(j,j+1)) j++;
45             if(!less(i,j)) break;
46             exch(i,j);
47             i = j;
48         }
49     }
50 
51     private void exch(int i, int j) {
52         Key t = pq[i];
53         pq[i] = pq[j];
54         pq[j] = t;
55     }
56 }
MaxPQ.java

 3.索引优先队列

它允许用例引用优先队列中的元素,方法是给每个元素一个索引。

 1 //索引优先队列,保存索引及其对应的项
 2 //用堆实现,复杂度为logN;
 3 public class IndexMinPQ<Item extends Comparable<Item>> {
 4     private int[] pq;    //保存索引
 5     private int[] qp;    //保存索引所对应堆中的下标
 6     private Item[] items;  //保存项
 7     private int N = 0;
 8     
 9     public IndexMinPQ(int maxN) {
10         pq = new int[maxN + 1];   //数组[0]未使用
11         qp = new int[maxN + 1];
12         items = (Item[]) new Comparable[maxN + 1];
13         for(int i = 0; i <= maxN; i++) qp[i] = -1;
14     }
15     public void insert(int k, Item item){
16         N++;
17         pq[N] = k;
18         qp[k] = N;
19         items[k] = item;
20         swim(N);
21     }
22     private void swim(int n) {
23         while(n > 1 && less(n, n/2)){
24             exch(n,n/2);
25             n = n/2;
26         }
27     }
28     private void exch(int i, int j) {
29         int t = pq[i];
30         pq[i] = pq[j];
31         pq[j] = t;
32         qp[pq[i]] = i;
33         qp[pq[j]] = j;
34     }
35     private boolean less(int i, int j) {
36         return items[pq[i]].compareTo(items[pq[j]]) < 0;
37     }
38     public void change(int k, Item item){
39         items[k] = item;
40         swim(qp[k]);
41         sink(qp[k]);
42     }
43     private void sink(int i) {
44         while(2*i <= N){
45             int j = 2*i;
46             if(j < N && less(j+1,j)) j++;
47             if(less(i,j)) break;
48             exch(i,j);
49             i = j;
50         }
51     }
52     public boolean contains(int k){
53         return qp[k] != -1;    
54     }
55     //删除索引为k的项
56     public void delete(int k){
57         int index = qp[k];
58         exch(index, N--);
59         swim(index);
60         sink(index);
61         qp[N+1] = -1;
62         items[pq[N+1]] = null;
63     }
64     public Item min(){
65         return items[pq[1]];
66     }
67     public int minIndex(){
68         return pq[1];
69     }
70     //删除最小项,并返回它的索引
71     public int delMin(){
72         int minIndex = pq[1];
73         exch(1,N--);
74         sink(1);
75         qp[N+1] = -1;
76         items[pq[N+1]] = null;
77         return minIndex;
78     }
79     public boolean isEmpty(){
80         return N == 0;
81     }
82     public int size(){
83         return N;
84     }
85 }
IndexMinPQ

4.堆排序

堆排序可以分为两个阶段:堆的构造阶段和下沉排序阶段。每次循环都将堆顶元素和最后一个交换,最后即为有序的。构造阶段的高效方法是从N/2开始,从右至左用sink()构造子堆。跳过大小为1的子堆。

下面的实现,我使用了数组的0下标,因为如果不使用0下标,在堆排序后还要用一层循环来给0号元素排序,麻烦。虽然这样在堆实现上有点麻烦。

 1 public class HeapSort {
 2     public static void main(String[] args) {
 3         String[] a = new String[]{"G","F","E","D","C","B","A","H","I","J","K"};
 4         heapSort(a);
 5         for(int i = 0; i < a.length; i++)
 6             System.out.print(a[i]);
 7     }
 8     public static void heapSort(Comparable a[]){
 9         int N = a.length-1;
10         for(int i = (N-1)/2; i >= 0; i--){
11             sink(a,i,N);
12         }
13         while(N > 0){
14             exch(a,0,N--);
15             sink(a,0,N);
16         }
17     }
18     private static void exch(Comparable[] a, int i, int j) {
19         Comparable t = a[i];
20         a[i] = a[j];
21         a[j] = t;
22     }
23     private static void sink(Comparable[] a, int i, int N) {
24         while(2*i+1 <= N){
25             int j = 2*i+1;
26             if(j < N && a[j].compareTo(a[j+1]) < 0) j++;
27             if(a[i].compareTo(a[j])> 0) break;
28             exch(a,i,j);
29             i = j;
30         }
31     }
32 }
HeapSort

 补充:

Java系统库中的优先队列:java.util.PriorityQueue.

该优先队列对基本数据类型默认使用自然顺序,即MinPq,小顶堆。可以在构造器中使用指定的比较器来使其成为MaxPQ。

相关方法:add;peek;poll;remove;clear;size;contains;toArray  等等。

不允许使用null元素。

默认构造器的初始容量为11.

内部实现:使用数组,对元素采用的是堆排序。

posted @ 2016-11-02 11:03  岳灵珊  阅读(810)  评论(0编辑  收藏  举报