优先队列之堆排序(一)

前言

为什么要是用优先队?优先队列又是什么?

许多应用程序都系要处理有序的元素,但不应定要求它们完全有序,或者不一定要一次就将它们排序。很多时候我们回收集一些元素,然后处处理当前键值最大的元素,然后再收集更多的元素,再狐狸当前键值最大的元素……举个生活中的例子吧,就像我们用的手机一样,手机里面有很多进程,但是有一个进程的优先级特别高,那就是来电显示(不然的话,放你在打游戏的时候也不会因为来电而退出,嘿嘿)。优先队列是局部有序的,它并不将所有的元素都排成成有序的。

基本函数

  1. Key delmax()用来删除队列中最大的元素
  2. void insert(Key value)用来插入新元素
  3. boolean isEmpty() 用来判断队列是否为空
  4. int getSize() 返回 队列中的元素个数
  5. Key getMax() 返回队列中的最大值元素
我们用二叉堆表示法来实现优先队列,首先来介绍一下什么是二叉堆

在一个二叉树里面,如果每个结点都大于等于它的两个子结点的时候,那么那它就是堆有序的;二叉堆是一组能够用堆有序的完全二叉树(若设二叉树的深度为h,除第 h 层外,其它各层 (1~h-1) 的结点数都达到最大个数,第 h 层所有的结点都连续集中在最左边,这就是完全二叉树。)排序的元素,并在数组中按照层级存储(为了表示方便,我们不使用数组中的第一个元素)。所以根节点的地方是队列中键值最大的元素。
完全二叉树如如下所示

函数具体实现(采用java语言)

堆实现的比较和交换算法

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;
    }

由下至上的堆有序化—Swim(上浮)

private void swim(int k) { // 上浮   元素最大比较次数为logN+1次 (也就是二叉树为满二叉树的时候,再往里面插入元素的时候)
        while(k>1&&less(k/2,k)) {
            exch(k/2,k);
            k=k/2;
        }
    }

由上至下的堆有序化—Sink(下沉)

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;
        }
    }

插入函数和删除最大元素最大函数

public void insert(int elem) {
        pq[++N]=elem;
        swim(N);
    }


    public int delMax() {
        int max=pq[1];
        exch(1, N--);//交换根节点和最后一个节点,并删除交换后的最后一     个节点 也就是之前的根节点
        //pq[N+1] = (Integer) null ;//防止对象游离  基本类型元素不可用
        sink(1);;//恢复堆得有序性
        return max;
    }

基于堆的优先队全部代码

package 优先队列1;

public class PriorityQueue {//堆顶元素最大
    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 PriorityQueue(int length) {
        this.pq=new int[length+1];  //下标为0的元素不用  元素从下标为1的地方开始
    }

    private void swim(int k) { 
        // 上浮   元素最大比较次数为logN+1次 (也就是二叉树为满二叉树的时候,再往里面插入元素的时候)
        while(k>1&&less(k/2,k)) {
            exch(k/2,k);
            k=k/2;
        }
    }

    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;
        }
    }

    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];
        exch(1, N--);//交换根节点和最后一个节点,并删除交换后的最后一个节点 也就是之前的根节点
        //pq[N+1] = (Integer) null ;//防止对象游离  基本类型元素不可用
        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) {
        // TODO Auto-generated method stub


        PriorityQueue PQ = new PriorityQueue(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());

    }
}

运行结果如下:

上述代码运行结果

算法分析

  • sink()方法中,一共循环logN次 元素最大比较次数为2*logN次
  • swim()方法中, 元素最大比较次数为logN+1次,也就是二叉树为满二叉树的时候,再往里面插入元素的时候。
  • 算法的分析 用图表表示为:

    其中,
  • 第一行unordered array是没有排序的数组,插入和删除还有求最大元素的算法复杂度分别为1,n,n。
  • 第二行ordered array是排好序的数组,插入和删除还有求最大元素的算法复杂度分别为n,1,1。
  • 第三行binary heap是二叉堆,插入和删除还有求最大元素的算法复杂度分别为log n,2*log n,1。
  • 第四行d-ary heap是d叉堆(也就是每个结点有d个子结点),插入和删除还有求最大元素的算法复杂度分别为logd n,d*logd n,1
  • sweet spot: d = 4 意思就是说最好的情况是当d=4的时候,算法效率比较好。

改进版的堆排序请参考

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

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值