【算法】排序_堆排序

堆排序是一种基于比较的排序算法,具有O(N*logN)的时间复杂度。本文详细介绍了大根堆和小根堆的概念,以及通过HeapInsert和Heapify两种方法建立和调整堆的过程。提供了一个Java实现的堆排序示例,包括建堆和排序的完整流程,展示了如何通过不断交换和调整元素来完成排序。
摘要由CSDN通过智能技术生成

堆排序

定义

堆排序是利用堆这种数据结构所设计的一种排序算法。
堆是一种近似完全二叉树的结构,并同时满足堆积的性质:子结点的键值或索引总是小于(或者总是大于)它的父结点
子结点的键值或索引值总是小于其父结点:大根堆
子结点的键值或索引值总是大于其父结点:小根堆

稳定性

不稳定

时间复杂度

O(N*logN)

建堆方法

HeapInsert 上浮

描述

假定事先不知道有多少个元素,通过不断往堆里面插入元素进行调整来构建堆

步骤
  1. 增加堆的长度,在最末尾的地方加入最新插入的元素
  2. 比较当前元素和它的父结点值,如果比父结点值大,则交换两个元素,否则返回
  3. 重复2
时间复杂度

O(N * logN)

示例
  1. 给定⽆序序列,通过 HeapInsert 建⽴⼤根堆。
    在这里插入图片描述

  2. 插⼊节点[6],与⽗节点[3]进⾏⽐较,并交换。
    在这里插入图片描述

  3. 插⼊节点[8],与⽗节点[6]进⾏⽐较,并交换。
    在这里插入图片描述

  4. 插⼊节点[5],与⽗节点[3]进⾏⽐较,并交换。
    在这里插入图片描述

  5. 插⼊节点[9],与⽗节点[5]进⾏⽐较,并交换。
    在这里插入图片描述

  6. 节点[9]继续与⽗节点[8]进⾏⽐较,并交换。
    在这里插入图片描述

  7. 所有节点插⼊,建堆完成。
    在这里插入图片描述

Heapify 下沉

从最后一个非叶子结点一直到根结点进行堆化的调整。
以大根堆为例,如果当前结点小于某个自己的孩子结点,那么当前结点和这个孩子交换,并持续往下递归调整

时间复杂度

O(N)

第一个非叶子结点

如果根结点在数组中的索引为0,那么最后 一个非叶子结点计算公式lastNonLeaf = (arr.length - 2)/2
设最后一个非叶子结点的位置为x,则最后一个叶子结点一定是2x + 12x + 2

示例
  1. 给定⽆序序列,通过 Heapify 建⽴⼤根堆。
    在这里插入图片描述
  2. 从最后⼀个⾮叶⼦节点[6]开始调整(arr.length/2-1),与其孩⼦节点中的较⼤节点⽐较,并交换。
    在这里插入图片描述
  3. 调整节点[5],与其孩⼦节点中的较⼤节点⽐较,并交换。
    在这里插入图片描述
  4. 继续调整节点[5],与其新孩⼦节点中的较⼤节点⽐较,并交换。
    在这里插入图片描述
  5. 所有⾮叶⼦节点调整完成,建堆完成。
    在这里插入图片描述

代码

测试用例

int arr[] = {3,6,8,5,9};

原代码


/**
 * @Classname HeapSort
 * @Description 堆排序
 * 时间复杂度 O(N * logN)
 */
public class HeapSort {
    public static void main(String[] args) {
        // 测试数据
        int arr[] = {3,6,8,5,9};
        //测试
        maxHeapSort(arr,arr.length);

    }

    /**
     * 堆排序
     * @param arr
     * @param len
     */
    public static void maxHeapSort(int[] arr,int len){
        System.out.println("初始数据:"+Arrays.toString(arr));
        // 最后一个非叶子结点(len-2)/2  最后一个结点:len-1
        for(int i = len/2 - 1; i>=0; i--){
            System.out.println("父结点下标:"+i);
            maxHeapify(arr,i,len - 1);
        }
        // 建堆完毕后结果输出
        System.out.println("建堆完毕后结果:"+Arrays.toString(arr)+"\n");


        // 先将第一个元素和已排好的元素前一位做交换(将最大的元素放到后面,类似于直接插入排序),再重新调整,直到排序完毕
        for (int i = len -1;i>0;i--){
            System.out.println("本次排序范围:0~"+(i-1));
            swap(arr,0,i);
            maxHeapify(arr,0,i-1);
        }

        System.out.println("最终结果:"+Arrays.toString(arr));
    }

    /**
     * 建堆
     * @param arr
     * @param start
     * @param end
     */
    public static void maxHeapify(int arr[],int start,int end){
        // 父结点和子结点下标
        int dad = start;
        int son = dad * 2 +1;
        while(son <= end){ // 子结点在范围内
            if (son + 1 <= end && arr[son] < arr[son +1]){
                son++;
            }
            if (arr[dad]>arr[son]){ // 父结点大于子节点 跳出函数
                return;
            } else { // 否则交换父结点,并继续子结点和孙结点比较
                swap(arr,dad,son);
                dad = son;
                son = dad * 2 +1;
            }
            System.out.println("本次交换结果:"+Arrays.toString(arr));
        }
    }

    /**
     * 交换函数
     * @param arr
     * @param i
     * @param j
     */
    public static void swap(int[] arr,int i ,int j){
        int temp = arr[j];
        arr[j] = arr[i];
        arr[i] = temp;
    }

}

运行结果
在这里插入图片描述

步骤

将堆顶结点与末尾结点交换,使末尾结点最大。然后继续调整堆,再将堆顶结点与末尾结点交换,得到第二大结点。如此反复进行交换 重建

  1. 已有⼤根堆,对其进⾏堆排序。
    在这里插入图片描述
  2. 将堆顶节点[9]与末尾节点[5]进⾏交换,交换完成后将节点[9]移出⼆叉树,并对节点[5]进⾏Heapify调
    整。
    在这里插入图片描述
  3. 将堆顶节点[8]与末尾节点[3]进⾏交换,交换完成后将节点[8]移出⼆叉树,并对节点[3]进⾏Heapify调

    在这里插入图片描述
  4. 将堆顶节点[6]与末尾节点[5]进⾏交换,交换完成后将节点[6]移出⼆叉树,并对节点[5]进⾏Heapify调
    整。
    在这里插入图片描述
  5. 将堆顶节点[5]与末尾节点[3]进⾏交换,交换完成后将节点[5]移出⼆叉树,此时只剩⼀个节点,堆排序
    结束。
    在这里插入图片描述
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值