优先队列之堆排序((二)升级版)

来源:互联网 发布:软件license开发 编辑:程序博客网 时间:2024/05/22 09:43

续 优先队列之堆排序(一)

上节在优先队列之堆排序(一)中介绍了优先队列的基本概念和实现算法,在本节中,我将为大家介绍一下关于对上节算法的改进一节该改进之后的算法复杂度。

在上节的delMAx()方法中,我们调用了sink(int k)方法。代码如下:

private void sink(int k) {         //下沉   一共循环logN次   元素比较次数为2*logN次        while(2*k<=N) {            int j=2*k;            if(j<N && less(j,j+1)) {                j++;            }            if(!less(k,j)) {                break;            }            exch(k,j);            k=j;        }}

这个方法的的主题思想是讲根节点要删除的元素与数组的最后一个元素(也就是二叉树的最后一个叶子节点)进行交换,然后删除交换之后的数组的最后一个元素(此时就是之前的根节点元素),在对现在的二叉树进行sink(int k)下沉操作,意思就是把根节点的那个小元素下沉到合适的位置。虽然这个算法可以使我们实现我们的目标,但是有没有发现这个方法中数组元素的比较次数是2*LogN次数,交换次数是logN,有没有一种方法可以减少元素之间的比较元素呢?答案当然是有的呀!

想一想,如果我们把数组最后的一个元素拿出来,单独放置在一个变量R中,然后直接忽视掉根节点(也就是直接删除掉根结点的元素,把根节点当做空的,里面没有存放任何值),对根结点下面的两个左右节点进行比较,选出两者之间值大的那个元素放在根节点 ,然后将这个子结点忽视掉,也就是把这个位置看作是空的、里面不放值得空结点,然后重复上一次的操作,再接着寻找这个被视作是空结点下面的左右子结点,取出最大的一个……直到这个二叉树的叶子节点处结束。

思考一下,经过上述操作现在的二叉树是什么样子的

此时的二叉树就是去掉最大元素和数组中一个元素(不一定是最小的那个元素)的其他元素所构成的优先队列的二叉树。还记得那个R变量吗,现在我们将那个R变量插入到数组中的末尾处,也就是插入到二叉树的最后一个叶子节点处,然后让这个节点进行swim()上浮,具体上浮到那一次不是确定的,但肯定上浮的高度比较小(解释一下为什么呢?你想一想,这个R元素是刚开始二叉树的最后一个叶子节点,它的值肯定是比较小的,但不一定是最小的,所以它上浮的高度不会很好,比较低)
升级版的代码如下:

private void new_sink(int k) { //下沉            int R=pq[N--];        while(2*k<=N) {            int j=2*k;            if(j<N) {  //防止出界  有左右两个节点存在的时候                if(less(j,j+1)) {                    pq[k]=pq[j+1];                    k=j+1;                }                else                {                    pq[k]=pq[j];                    k=j;                }            }            else { //如果最后的非叶子节点只有一个左节点的时候,直接将左节点放在其父节点的位置上面                pq[k]=pq[j];                k=j;            }            pq[N]=R;            swim(N);        }    }

简单说就是,删除完根节点之后,并不把最后一个元素R放在根节点的位置,把最后一个根节点单独取出来存储,直接比较根节点的两个 子节点,把最大的子节点放在根节点的位置,依次把树先排好序,然后再把R插入到二叉树的下面,进行swim上浮,此时上浮的高度比较低。 算法优点:元素比较的次数由原来的2*logN 变成 logN + x +1次(x为之后swim的时候所上浮的次数,至于为什么是logN + x +1而不是logN + x呢,是因为在最后一次上浮之后,再比较一次,没有比它更小的元素了,结果不满足上浮条件),但是不足之处是以牺牲x次交换次数来实现的,因为在上浮的时候,每一次比较都会交换一次会产生一些交换的 。

这种算法的名字也称为Floyd’s “bounce” heuristic,bounce的意思就是反弹,比如你从高处扔下来一个球,它反弹起来的高度 肯定会比之前的高度小。这个算法就是这个意思。

具体实现代码如下所示

package half_exchange_PQ;public class HF_PQ {    private int[] pq;    private int N = 0; //存储于pq[1...N]中,pq[0]没有使用    private boolean less(int i,int j) {        return pq[i] <pq[j];    }    private void exch(int i,int j) {        int temp = pq[i];        pq[i]=pq[j];        pq[j]=temp;    }    public HF_PQ(int length) {        this.pq=new int[length+1];  //下标为0的元素不用  元素从下标为1的地方开始    }    private void swim(int k) { // 上浮         while(k>1&&less(k/2,k)) {            exch(k/2,k);            k=k/2;        }    }    //half_exchange    Floyd's "bounce" heuristic.    private void new_sink(int k) { //下沉     删除完根节点之后,并不把最后一个元素R放在根节点的位置,把最后一个根节点单独取出来存储,                            //直接比较根节点的两个 子节点,把最大的子节点放在根节点的位置,依次把树先排好序,然后再把R插入到二叉树的下面,进行swim上浮                            //此时上浮的高度比较低。  算法优点:元素比较的次数由原来的2*logN 变成 logN + x+1 次(x为之后swim的时候所比较的次数)                             // 但是是以牺牲x次交换次数来实现的,因为在上浮的时候会产生一些交换的 。        int R=pq[N--];        while(2*k<=N) {            int j=2*k;            if(j<N) {  //防止出界  有左右两个节点存在的时候                if(less(j,j+1)) {                    pq[k]=pq[j+1];                    k=j+1;                }                else                {                    pq[k]=pq[j];                    k=j;                }            }            else { //如果最后的非叶子节点只有一个左节点的时候,直接将左节点放在其父节点的位置上面                pq[k]=pq[j];                k=j;            }            pq[N]=R;            swim(N);        }    }    public int getElemSize()    {        return N;    }    public boolean isEmpty() {        return N == 0;    }    public void insert(int elem) {        pq[++N]=elem;        swim(N);    }    public int delMax() {        int max=pq[1];        //half_exchange    Floyd's "bounce" heuristic.         //此处不采用交换的方法  用new_sink函数                            //exch(1, N--);//交换根节点和最后一个节点,并删除交换后的最后一个节点 也就是之前的根节点                            //pq[N+1] = (Integer) null ;//防止对象游离  基本类型元素不可用        new_sink(1);//恢复堆得有序性        return max;    }    private void display() {        for(int i=1;i<=N;++i)            System.out.print(pq[i] + " ");        System.out.println();    }    public static void main(String[] args) {        HF_PQ PQ=new HF_PQ(12);        PQ.insert(12);        PQ.insert(15);        PQ.insert(2);        PQ.insert(18);        PQ.insert(7);        PQ.insert(7);        System.out.println("The count of elem is "+PQ.getElemSize());        System.out.print("All elem are :");        PQ.display();        System.out.println("Now,the max elem in priorityQueue is "+PQ.delMax());        System.out.println("Now,the max elem in priorityQueue is "+PQ.delMax());        System.out.println("Now,the max elem in priorityQueue is "+PQ.delMax());    }}

运行结果为:
这里写图片描述