基于堆的优先队列及堆排序

来源:互联网 发布:服装销售软件 编辑:程序博客网 时间:2024/04/28 10:31

许多应用程序需要处理有序的元素,例如事件循环中要先处理高的事件。在这种情况下,我们设计的数据结构需要支持两种操作:删除最大元素和插入元素,这种数据类型就叫做优先队列,它可以用来解决TopM问题(找出M个最大的元素)或者多向归并问题(将多个有序的输出流合并成一个有序的输出流),在这两种情况下,问题的输入都可能是无限的,不可能先读取所有输入再进行排序,而优先队列适合解决这种问题。

先来看一下堆的概念,堆是二叉堆的简称,是一种每个节点的元素都大于两个子节点的完全二叉树,在优先队列中一般用数组表示。

优先队列的代码如下:

package chapter2;/** * Created by jia on 17-5-17. *///基于堆实现的优先队列public class MaxPQ<T extends Comparable<T>> {    private T[] pq;  //基于堆的完全二叉树    private int n;   //数据存储于pq[1..n]中,pq[0]没有用到    public MaxPQ(int num) {        pq = (T[]) new Comparable[num+1];    }    public boolean isEmpty() {        return n == 0;    }    public int size() {        return n;    }    public void insert(T t) {        pq[++n] = t;        swim(n);    }    public T delMax() {        T max = pq[1];      //根结点的元素值最大        swap(1, n--);    //把最大值和最后一个元素交换,并将数组大小减一        pq[n+1] = null;     //删除最大值        sink(1);         //恢复堆的有序性        return max;    }    //递归地把节点i跟其父节点比较以构造二叉堆    private void swim(int i) {        while (i > 1 && pq[i].compareTo(pq[i/2]) > 0) {            swap(i, i/2);            i /= 2;        }    }    //递归地把节点i跟其两个子节点比较以构造二叉堆    private void sink(int i) {        while (2*i <= n) {            int j = 2*i;            if (j < n && pq[j].compareTo(pq[j+1]) < 0) j++;            if (pq[i].compareTo(pq[j]) >= 0) break;            swap(i, j);            i = j;        }    }    private void swap(int i, int j) {        T temp = pq[i];        pq[i] = pq[j];        pq[j] = temp;    }}

堆排序的主要思想是构造一个最大元素的优先队列,然后再重复调用删除最大元素的方法来将它们按照顺序删除。堆排序分为两个阶段,先进行堆的构造,然后再用sink函数进行排序。代码如下:

package chapter2;/** * Created by jia on 17-5-17. */public class HeapSort {    public static void sort(Comparable[] a) {        //此时只能以a[0]为起点,为此要修改sink等函数        int n = a.length - 1;        //先用for循环构造一个最大堆        for (int i = (n-1)/2; i >= 0; i--) {            sink(a, i, n);        }        ArrayPrint.print(a);        //再用sink函数进行排序        while (n > 0) {            swap(a, 0, n);   //每次循环都将当前的最大值放到a[n]处            sink(a, 0, --n);  //再把n减去1,然后找出从0到n的最大值,放到a[0]处,下轮循环会把此值交换到a[n]        }    }    private static void swap(Comparable[] a, int i, int j) {        Comparable temp = a[i];        a[i] = a[j];        a[j] = temp;    }    private static void sink(Comparable[] a, int i, int n) {        //n代表sink函数截止的最后一个数组索引值        while (2*i +1 <= n) {            //以a[0]为起点的数组其元素a[i]的两个子节点是2*i+1和2*i+2            int j = 2*i + 1;            if (j < n && a[j].compareTo(a[j+1]) < 0) j++;            if (a[i].compareTo(a[j]) >= 0) break;            swap(a, i, j);            i = j;        }    }    public static void main(String[] args) {        String[] a = "21s1gwhfiajnc493tquwofadjst873ewigdshjk8y0qfwedsknlc49-ufqewojd;c2".split("");        sort(a);        ArrayPrint.print(a);    }}