堆排序 HeapSort

堆排序 HeapSort


二叉堆是一组能够用堆有序完全二叉树排序的元素,并在数组中按照层级存储(不适用数组的一个位置,下标从1开始)!

二叉堆可以很好的实现优先队列的基本操作。优先队列是一种抽象的数据结构,主要有insert(插入元素) 和 delMax(删除最大操作)两个标志性操作。在一个堆中,位置k的节点的父节点的位置是是k/2,而他的两个子节点的位置则分别是2k,2k+1!利用数组中无需指针即可沿树上下移动的遍历,算法可以保证对数复杂度的性能。

堆的操作会首先进行一些简单的改动,打破堆的状态,然后再遍历堆并按照要求将对的状态恢复,这个过程被称为堆的有序化

堆的有序化

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

    如果堆的有序化因为某个节点变得比父节点而被打破,将它和父节点交换来修复堆。当这个节点可能比父节点更大,将这个节点不断上移直到我们遇到更大的父节点!也就是说当该结点不再大于它的父节点,堆的有序状态就恢复了

private void swim(int k){    
    while(k>1&&less(k/2,k){
       /*交换子节点和父节点*/
       exch(k/2,k);
       /*继续上移,知道父节点大于该节点*/
       k=k/2;
    }
}
  • 由上而下的堆有序化(下沉)
    如果堆的有序状态因为某节点比他的子节点小而被打破有序状态,那么我们可以通过将它的两个子节点中的较大者交换来恢复堆。
    交换可能在子结点出继续打破堆的有序状态,因此我们需要不断用相同的方式将其修复,将结点向下移动直到它的子结点都比它小或者到达了堆的底部
private void sink(int k){
    /*不是叶子结点*/
     while(2*k<=N){
         //子结点索引
         int j=2*k;
         //j保存较大子节点的索引
         if(j<N&&less(j,j+1))   j++;
         //如果该结点比子结点都大,则终止循环
         if(!less(k,j))   break;
         //和较大子节点交换
         exch(k,j);
         //下一层有序化
         k=j;
    }
 }

Insert 插入元素

将新元素插入到数组末尾增加堆的大小,并让这个新元素上浮到合适的位置

DelMax 删除最大元素

从数组的顶端删除最大元素,并将数组最后一个元素放到顶端缩小堆的大小并让这个元素下沉到合适位置


基于堆的优先队列

package DataStruct;

public class MaxHeapPQ<T extends Comparable<T>> {
    // 基于堆的完全二叉树
    private T[] pq;
    // 存储在pq[1....N]中,pq[0]不适用
    private int N = 0;

    // 插入元素
    public void insert(T t) {
        pq[++N] = t;
        swim(N);
    }

    // 删除最大元素
    public T delMax() {
        // 从根结点的得到最大元素
        T max = pq[1];
        // 将其和最后一个元素交换
        exch(1, N--);
        // 放置对象游离,有利于回收
        pq[N + 1] = null;
        // 下沉,恢复堆的有序性
        sink(1);
        return max;
    }

    // 上浮
    private void swim(int k) {
        while (k > 1 && less(k / 2, k)) {
            /* 交换子节点和父节点 */
            exch(k / 2, k);
            /* 继续上移,知道父节点大于该节点 */
            k = k / 2;
        }
    }

    // 下沉
    private void sink(int k) {
        /* 不是叶子结点 */
        while (2 * k <= N) {
            // 子结点索引
            int j = 2 * k;
            // j保存较大子节点的索引
            if (j < N && less(j, j + 1))
                j++;
            // 如果该结点比子结点都大,则终止循环
            if (!less(k, j))
                break;
            // 和较大子节点交换
            exch(k, j);
            // 下一层有序化
            k = j;
        }
    }

    // 小于
    private boolean less(int i, int j) {
        if (pq[i].compareTo(pq[j]) < 0)
            return true;
        else
            return false;

    }

    // 交换函数
    private void exch(int i, int j) {
        T temp = pq[i];
        pq[i] = pq[j];
        pq[j] = temp;

    }

}

堆排序

堆排序可以分为两个阶段:构建堆(将原始数组重新组织放到一个堆中),然后下沉排序,从堆中按照递减的顺序取出所有的元素并得到排序的结果!

堆的构建

从右到左用sink()函数构造子堆,开始是我们只需要扫描数组一般的元素(N/2),因为我们可以跳过大小为1的子堆

/*堆排序*/
public static void HeapSort(Comparable[] a){
    int N=a.length;
    //构建堆
    for(int k=N/2;k>=1;k--)
        sink(a,k,N);
    //下沉排序
    while(N>1){
        exch(a,1,N--);
        sink(a,1,N);
    }   
}
下沉排序

将堆中最大的元素删除,然后放入堆缩小后数组空出的位置

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值