堆,堆排序

很久没写博客了 还是抽时间写一写~

堆用来做优先队列 插入与删除平均时间复杂度都为O(n),以链表实现插入是O(1),删除是O(n),数组实现反之

JUC包就有优先队列的实现(后面深入并发后再补充)

判断:叶子节点为左节点时是完全二叉树,叶子节点为右节点时,不连续紧密排列,非完全二叉树

堆的定义:

堆是一颗完全二叉树。堆的基本要求是堆中所有结点的值必须大于(或小于)其孩子结点的值。

完全二叉树:

若设二叉树的高度为h,除第 h 层外,其它各层 (1~h-1) 的结点数都达到最大个数,第 h 层有叶子结点,并且叶子结点都是从左到右依次排布。

堆是数据结构(以大根堆为例)

public class Heap {
    // 容量
    private int capbility;
    // 元素大小
    private int count;
    // 元素数组
    private int[] data;

    Heap(int capbility) {
        this.data = new int[capbility+1];
        this.capbility = capbility;
        this.count = 0;
    }

}

注意 这里是capbility+1; 因为我们堆结点是从索引1开始,从1到capbility+1,data[0]是空

介绍两个性质

获取父结点 i/2

获取子结点 2*i 和 2*i+1

(如果是capbility则只是计算不同,获取父结点 (i-1)/2,子结点 2*i +1和 2*i+2 )

然后是插入操作

插入到是插入到最后的结点位置,容量++,为了保持堆的性质,开始上升shiftUp操作,不断与父结点比较,如果大于父结点则交换,到一个节点的时候或者满足堆的性质

 public void insert(int item) {
        // FIXME 可改为扩容操作
        assert(count + 1 <= capbility);
        count++;
        data[count] = item;
        shiftUp(count);
    }

    private void shiftUp(int k) {
        while (k > 1 && data[k] > data[k/2])
        {
            // 与父结点交换
            SortHelper.swap(data,k,k/2);
            // 继续比较
            k = k/2;
        }
    }

删除操作

把data[1](ps:data[0]为空)删除,容量--,为了保持堆的性质,把data[count]换到data[1],开始shiftDown操作,不断与子节点比较,与子节点中大的那个交换位置,到无子节点或者已经满足大于堆的性质

   public int delete(){
        assert( count > 0 );
        int item = data[1];
        data[1] = data[count];
        count --;
        shiftDown(1);
        return item;
    }

    private void shiftDown(int k) {
        // 如果有左节点
        while (2*k <= count){
            int j = 2*k;
            // data[j] 是 data[2*k]和data[2*k+1]中的最大值
            if (j+1 <= count && data[j] < data[j+1]) {
                j = j+1;
            }
            // 与子节点最大的进行比较 如果大于子节点 就直接跳出
            if( data[k] >= data[j] ) break;
            // 交换位置和索引继续比较
            SortHelper.swap(data,k,j);
            k = j;
        }
    }

测试结果

 

堆排序

由上面得出结论 我们按照顺序每次把删除的元素就保存就是堆排序 是不是很简单~

   public static void main(String[] args) {
        int n = 10;
        Heap heap = new Heap(n);
        for (int i = 0; i < 10; i++) {
            heap.insert((i+1)*2);
        }
        System.out.println("构建的堆:"+Arrays.toString(heap.getData()));
        int[] arr = new int[n];
        for (int i = 0; i < n ; i++) {
            arr[i] = heap.delete();
        }
        System.out.println("排序后的数组:"+Arrays.toString(arr));
    }

 构建堆优化

其实构建堆不用每次都加一个元素就shiftUp操作

叶子节点一开始就一个 无需进行任何操作,所以我们从第一个非叶子节点进行,也就是22(索引5)和之前的元素进行shiftDown操作 ,

获取第一个非叶子节点:获取最后一个叶子节点(data[count])的父结点(data[count/2])

public void heapify(int[] arr, int n) {
        for( int i = 0 ; i < n ; i ++ )
            data[i+1] = arr[i];
        count = n;
        for (int i = n/2; i >= 1; i--) {
            shiftDown(i);
        }
    }

注意我们这里是>= 1,因为我们从索引1开始计算

 

原地排序(不需要借助辅助空间) 

把最大的data[0]一直往数组最后放,放完该排序的数组大小-1,再重复进行这个操作

  public static void localSort(int[] arr,int n) {
        heapify(arr, n);
        for (int i = 0; i < n ; i++) {
            SortHelper.swap(arr,0,n-(i+1));
            __shiftDown(arr, n-(i+1), 0);
        }
    }

代码优化

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值