堆排序

本文详细介绍了堆的概念、堆的存储方式以及如何使用堆进行排序。通过实例演示了堆化数组的过程,并给出了最大堆和最小堆的具体实现代码。此外,还讲解了堆排序的基本思路及其在实际中的应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1、堆定义

堆定义如下:n个元素的序列{k1,k2,k3,…..,kn}当且仅当满足一下关系时,称为堆。
这里写图片描述这里写图片描述
就是这两个公式,仔细体会,i是指结点下标,ki指第i个结点,i的范围(i=i,2,……,n/2)n是要排序的长度。
将一维数组看成是一个完全二叉树,则堆的含义表明,完全二叉树中所有非终端结点(叶子结点)的值均不大于(或者均不小于)其左右孩子结点的值。
例如:[1,2,3,17,19,36,7,25,100] 这个数组,生成的堆就是如下图,
这里写图片描述
这是我们常用的二叉树堆,还有其他的一些二项式堆和斐波纳契堆等。

2、二叉树堆的存储

一般都用数组来存储堆,i结点的父节点就是(n-1)/2,它的左结点是2*i+1,右结点是2*i+2,这些特性都是二叉树的特性,一个完全二叉树。

3、如何使用堆排序

上述的两个公式定义的是公式中的最大堆和最小堆,就是父节点比左右结点大则为最大堆,父节点比左右结点小则为最小堆,我们第一步要先使用数组来构建一个最大堆或者最小堆,之后再进行堆排序。

1、堆化数组

把数组的原始序列默认为堆结构,之后进行调整使其满足上述的两个公式

例如:下面这个数组进行最小堆的堆化
这里写图片描述
这里写图片描述
这里面进行了四个步骤操作
1、A步骤,进行最后一个结点和父节点的调整,再堆化过程中我们都是先进行最后一个结点进行堆化,通过数组的长度就能找出这个最后的结点。i=(n-1)/2=4,i–
2、B步骤,进行i=3这个结点的调整,i–
3、C步骤,进行i=2结点的调整,i–
4、D步骤,进行i=1结点的调整,i–
5、最后依次,进行结点0的调整

直接上代码:

//创建最大树堆
func MakeMaxHeap(data []int) {
    n := len(data)
    for i := (n - 1) >> 1; i >= 0; i-- {
        MaxHeapFixDows(data, i, n)
    }
}

//最大堆:父节点比左右结点都大
//最小堆:父节点比左右结点都小
//如果n为奇数,则最后一个结点一定是包含一个左结点,n为偶数,则一定是一个满二叉树,则没有空余的子节点
func MaxHeapFixDows(data []int, i, n int) {
     //保存根元素
    root := data[i]
    //左孩子结点
    j := i<<1 + 1   
    for j < n {
    //右孩子和左孩子进行比较,j+1<n是为了防止数组下标越界
        if j+1 < n && data[j+1] > data[j] { 
         //如果右孩子较大则用右孩子和父节点做比较,否则用左孩子进行比较,j++就得到右孩子结点
            j++
        }
        //如果最大的孩子结点都小于父节点,则break
        if data[j] < root { 
            break
        }
         //这里的j,如果上面的if不成立,则说明是左孩子大,则j=i<<1+1,否则j=i<<1+2
        if data[j] > root {
            //如果孩子比父亲大,则进行交换,这里忽略一种情况就是相等就不进行交换了
            data[i] = data[j]
        }
        //这里是一个递归进入下一个结点的过程,比如:上述步骤中D,i=1,则子节点j等于3,到这里之后就会进入变为i=3,j=7就是它的下一个堆的左结点下标
        i = j 
        j = i<<1 + 1
    }
    data[i] = root
}

output:
[65 50 60 30 19 20 17 12 9 4],这就是最大堆的结果,看到这里要自己动手实现一个最小堆就能彻底理解了。

4、堆排序

//使用最小堆得到的是降序,最大堆得到的是升序
func HeapSort(data []int) {
    n := len(data)
    for i := (n - 1); i >= 1; i-- {
        data[i], data[0] = data[0], data[i]
        MinHeapFixdown(data, 0, i) 
        // MaxHeapFixDows(data, 0, i)
    }
}
output:
[4 9 17 12 19 20 60 65 30 50]
[65 60 50 30 20 19 17 12 9 4]

排序思路:如果是 最小堆,那么堆顶元素肯定是最小的,就把堆顶元素和最后一个元素进行互换,然后再进行堆”筛选“,进过一次的筛选之后,堆顶上的元素又是最小的,除去最后一个,这是再和倒数第二个元素交换,这样就得到了倒数第二小的了,依次就可以进行排序。

这里有一幅图,我觉得很生动,可以提高理解:
初始进行 最大堆堆化:
这里写图片描述
对最大堆进行排序:
这里写图片描述

我写这个主要是为了阅读golang源码中sort包的排序,sort包中也使用了堆排序,不过它实现不同。原理是相同。
参考的博文:

1、白话经典算法系列之七 堆与堆排序
2、堆排序 Heap Sort

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值