排序算法-07堆排序

堆排序

1. 算法概述

堆排序是指利用堆这种数据结构所设计的一种排序算法。堆积是一个近似完全二叉树的结构,并同时满足堆积的性质:即子结点的键值或索引总是小于(或者大于)它的父节点。堆排序可以说是一种利用堆的概念来排序的选择排序。

堆分两种

  • 大顶堆:每个节点的值都大于或等于其子节点的值
  • 小顶堆:每个节点的值都小于或等于其子节点的值
  • 每一个节点下,左右元素大小关系不做要求

其实我们的堆排序算法就是抓住了堆的这一特点,每次都取堆顶的元素,将其放在序列最后面,然后将剩余的元素重新调整为最大堆,依次类推,最终得到排序的序列。堆排序的平均时间复杂度为 Ο(nlogn)。

2. 算法原理

  1. 将初始待排序关键字序列(R1,R2…Rn)构建成大顶堆,此堆为初始的无序区
  2. 将堆顶元素R[1]与最后一个元素R[n]交换,此时得到新的无序区(R1,R2,…Rn-1)和新的有序区(Rn)
  3. 由于交换后新的堆顶R[1]可能违反堆的性质,因此需要对当前无序区(R1,R2,…Rn-1)调整为新堆
  4. 然后再次将R[1]与无序区最后一个元素交换,得到新的无序区(R1,R2…Rn-2)和新的有序区(Rn-1,Rn)。
  5. 不断重复此过程直到有序区的元素个数为n-1,则整个排序过程完成

3. 动图演示

4. 代码实现

创建最大堆代码

 private void buildMaxHeap(int[] arr, int len) {
     /*
        任意一节点指针 i(parent):父节点:i==0 ? null : (i-1)/2
        左孩子(left):2*i + 1
       右孩子(right):2*i + 2
            n个节点的完全二叉树array[0,...,n-1],最后一个节点n-1是第(n-1-1)/2个节点的孩子。
            对第(n-1-1)/2个节点为根的子树调整,使该子树称为堆。
         */
     for (int i = (len - 1) >> 1; i >= 0; i--) {
         heapify(arr, i, len);
     }
 }

/**
     * 重新组装堆,将数组符合堆的要求
     *
     * @param arr
     * @param parent
     * @param len
     */
private void heapify(int[] arr, int parent, int len) {
    int left = 2 * parent + 1;
    int right = 2 * parent + 2;
    //最大数保存位置
    int largest = parent;

    //如果左节点 > 父节点,记录最大值为左节点
    if (left < len && arr[left] > arr[largest]) {
        largest = left;
    }

    //如果有节点 > 上步记录的节点,记录最大值为右节点
    if (right < len && arr[right] > arr[largest]) {
        largest = right;
    }
    //else 最大值为父节点

    //如果最大值不为父节点,则交换最大值所在节点和父节点的位置
    if (largest != parent) {
        //交换位置
        swap(arr, parent, largest);
        //重复上述步骤,递归建堆
        heapify(arr, largest, len);
    }
}

排序代码实现

@Test
public void sort() {
    arr = new int[]{2, 1, 4, 3, 7, 5, 6};
    int len = arr.length;
    //建立堆
    buildMaxHeap(arr, len);

    for (int i = len - 1; i > 0; i--) {
        //移除堆顶部最大值到尾部节点
        swap(arr, 0, i);
        //删除最后排序序的尾节点
        len--;
        //重新构造堆
        heapify(arr, 0, len);
    }

    log.info("排序后:{}", JSON.toJSONString(arr));
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值