八大排序之堆排序

        最近看了一下堆排序,整体来说,其实还是有些难度了,这里以大根堆为例,这里我将堆排序分成了二步:

第一步:将原数组变为一个大根堆(大根堆的特点:树的根节点永远大于子节点)

第二步:摘掉第一个元素(大根堆的根节点),使剩下的元素继续变为大根堆

现在我们考虑如何将原来的数组变为大根堆呢?

首先知道节点的父节点的位置

当前节点:index

父节点:(index-1)/2

想想如果当前节点小于父节点,是不是应该将两个节点进行交换,交换完后,当前节点的索引变为交换后位置的索引,继续找其父节点进行比较,直到他比父节点小,退出循环,index从0~len-1后,整个数组就变为了大根堆,废话不多说,看代码:

    //index索引从0开始
    private static void heapInsert(int[] arr, int index) {
        //当前节点大于父节点,进行交换
        while(arr[index] >arr[(index -1)/2]){
            swap(arr,index,(index-1)/2);
            index = (index-1)/2;
        }
    }

    private static void swap(int[] arr, int i, int j) {    
        int temp = arr[i];
        arr[i] = arr[j];
        arr[j] = temp;
    }

现在变为了大根堆,那么根节点肯定是最大的,这个时候我们把根节点和最后一个位置元素(heapSize-1)进行交换,heapSize--,即排序了一个最大元素,前面的元素必然不再是大根堆,(因为根节点必然小于左右节点,这时候我们就要找到它该待的位置)

当前节点:idnex

左节点:index * 2 +1

右节点:index* 2 +2

将当前节点和左右节点比较,情况如下:

private static void heapify(int[] arr, int index, int heapSize) {
        //找左孩子的下标
        int left = index * 2 + 1;
        //如果左孩子的下标 < heapSize,说明有孩子
        while(left < heapSize){
            //left +1 < heapSize,说明有有右孩子,找左右孩子的大的下标
            int largest = left +1 < heapSize && arr[left] < arr[left + 1]?left+1:left;

            //比较当前节点和孩子中最大的元素,选取下标
            largest = arr[index] > arr[largest]?index:largest;
            //如果最大就是自己,已经是堆了
            if(largest == index){
                break;
            }
            //如果不是,交换,继续和下一层的孩子比较
            swap(arr,index,largest);
            index = largest;
            left = index * 2+ 1;
        }
    }

进行完堆化操作后,此时根节点又是最大的一个元素,与heapSize-1的位置元素交换,heapSize--

循环堆化while(heapSize-1>0),取第一个元素和heapSize-1位置交换,最后整个数组有序

整个流程代码如下:

 public static void main(String[] args) throws InterruptedException {
       int[] arr = new int[]{3,6,9,8,4,5,7,1,2};
        heapSort(arr);
        print(arr);
    }

    public static void heapSort(int[] arr){
        if(arr == null || arr.length < 2){
            return;
        }
        //将数组变为大根堆
        for(int i = 0;i<arr.length;i++){
            heapInsert(arr,i);
        }
        int heapSize = arr.length;
        swap(arr,0,--heapSize);
        while(heapSize > 0){
            //交换后,最大的元素在最后,前面所有元素继续堆化
            heapify(arr,0,heapSize);
            swap(arr,0,--heapSize);
        }
    }

    private static void heapify(int[] arr, int index, int heapSize) {
        //找左孩子的下标
        int left = index * 2 + 1;
        //如果左孩子的下标 < heapSize,说明有孩子
        while(left < heapSize){
            //找左右孩子的大的下标
            int largest = left +1 < heapSize && arr[left] < arr[left + 1]?left+1:left;

            //比较当前节点和孩子中最大的元素,选取下标
            largest = arr[index] > arr[largest]?index:largest;
            //如果最大就是自己,已经是堆了
            if(largest == index){
                break;
            }
            //如果不是,交换,继续和下一层的孩子比较
            swap(arr,index,largest);
            index = largest;
            left = index * 2+ 1;
        }
    }

    private static void heapInsert(int[] arr, int index) {
        //当前节点大于父节点,进行交换
        while(arr[index] >arr[(index -1)/2]){
            swap(arr,index,(index-1)/2);
            index = (index-1)/2;
        }
    }

    private static void swap(int[] arr, int i, int j) {
        int temp = arr[i];
        arr[i] = arr[j];
        arr[j] = temp;
    }

    private static void print(int[] arr) {
        for(int i : arr){
            System.out.print(i + " ");
        }
    }

        有错误的地方或说的不明白的地方,希望谅解一下,本人也是个小白!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值