堆排序-快速理解

这是一个很有意思的算法!

堆的基本概述:

堆的本质

实际上是一棵完全二叉树,所以将会有一下性质!

1、下标为 i 的节点的父节点下标 :( i - 1 ) / 2

2、下标为 i 的节点的左孩子下标 :i * 2 + 1

3、下标为 i 的节点的右孩子下标 :i * 2 + 2

堆的建立:

堆分为两类 :

大顶堆 : 根节点 > 左右孩子

小堆顶 : 根节点 < 左右孩子

注意:我们只是建堆并非排序、堆没有要求左右孩子值的关系!!

建堆(以大顶堆为例):

默认:从最后一个根节点开始建立,即 根 i =(n - 1 ) / 2

维护堆 :

判断根的左右孩子中最大值是否大于根节点

根节点数组下标 i  值arr[i]
寻找孩子中的大孩子 j = i * 2 + 1
左孩子值 arr[j]右孩子值 arr[j+1]
如果左孩子大,大孩子就为 j ,如果右孩子大,大孩子就为 j+1
if ( arr[j] < arr[j+1] )  j++  
找到大孩子后与根进行比较
大孩子值 < 根大孩子 > 根
swap(arr[i],arr[j])break
判断当前交换的孩子是否还有孩子,如果有将重复以上操作
i = j ; j = i * 2 +1;
    
static void HeapSort0(int arr[], int n) {
        //下标为i节点的父节点下标 : (i - 1)/2
        //下标为i节点的左孩子下标 : i * 2 + 1
        //下标为i节点的右孩子下标 : i * 2 + 2
        for (int i = (n - 1) / 2; i >= 0; i--) {
            //获取到所有根节点、从最后一个根节点开始
            SiftHeap(arr, i, n);
        }
}    


static void SiftHeap(int[] arr, int k, int n) {
        //建立大顶堆 根 > 左、右孩子的值
        //注意 : 没有要求左孩子与右孩子的关系!
        int i = k;
        int j = 2 * i + 1;
        while (j < n) {  //判断该根节点是否有叶子节点
            //左孩子为 j
            //右孩子为 j+1
            if (j < n - 1 && arr[j] < arr[j + 1]) j++; //判段俩个孩子的最大值
            if (arr[i] > arr[j]) break; //如果根节点大于孩子值break;
            else {      //将根节点与孩子值交互
                int temp = arr[i];
                arr[i] = arr[j];
                arr[j] = temp;
                i = j; j = 2 * i + 1;  //当新值出现后之前建立的将可能不满足条件、需要重新检查!
            }
        }
}

堆排序

可能会出乎你的意料: 大顶堆得到的是降序、小堆顶得到的是升序;

---

从堆的建立中我们会发现堆顶根永远为最大\最小

如此将堆顶根放到数组最后一个位置,并将数组的范围减去最后一个位置

大顶堆中、将堆顶根【当前树中最大元素】依次从数组最后一个位置放过来、就形成了升序队列

小顶堆中、将堆顶根【当前树中最大元素】依次从数组最后一个位置放过来、就形成了降序队列

static void HeapSort1(int arr[], int n) {
        for (int i = 1; i <= n - 1; i++) {
            //让所有根重新建堆
            int temp = arr[0];
            arr[0] = arr[n - i];
            arr[n - i] = temp;
            SiftHeap(arr, 0, n - i);
        }
    }

完整测试代码如下:

输入数组 {1, 5, 2, 6, 4, 10, 7, 21, 18, 3};

排序后    {1, 2, 3, 4, 5, 6, 7, 10, 18, 21 };

package ZuoYe;

public class 堆排序 {
    public static void main(String[] args) {
        int[] arr = {1, 5, 2, 6, 4, 10, 7, 21, 18, 3};
        HeapSort(arr, arr.length);
        for (int x : arr) {
            System.out.print(x + " ");
        }
    }

    static void SiftHeap(int[] arr, int k, int n) {
        //建立大顶堆 根 > 左、右孩子的值
        //注意 : 没有要求左孩子与右孩子的关系!
        int i = k;
        int j = 2 * i + 1;
        while (j < n) {  //判断该根节点是否有叶子节点
            //左孩子为 j
            //右孩子为 j+1
            if (j < n - 1 && arr[j] < arr[j + 1]) j++; //判段俩个孩子的最大值
            if (arr[i] > arr[j]) break; //如果根节点大于孩子值break;
            else {      //将根节点与孩子值交互
                int temp = arr[i];
                arr[i] = arr[j];
                arr[j] = temp;
                i = j; j = 2 * i + 1;  //当新值出现后之前建立的将可能不满足条件、需要重新检查!
            }
        }
    }
    static void HeapSort(int arr[], int n) {
        //下标为i节点的父节点下标 : (i - 1)/2
        //下标为i节点的左孩子下标 : i * 2 + 1
        //下标为i节点的右孩子下标 : i * 2 + 2
        for (int i = (n - 1) / 2; i >= 0; i--) {
            //获取到所有根节点、从最后一个根节点开始
            SiftHeap(arr, i, n);
        }
        //大顶堆建立完成
        //将大顶堆排序
        for (int i = 1; i <= n - 1; i++) {
            //让所有根重新建堆
            int temp = arr[0];
            arr[0] = arr[n - i];
            arr[n - i] = temp;
            //交换的目的:
            //arr[0] 大顶堆
            //n-i 当前数组中最后一个叶子
            //最大值就依次丢到最后、构成了升序数组!
            SiftHeap(arr, 0, n - i);
        }
    }

}

希望可以给你帮助! 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

续写少年!

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值