学习回顾算法(堆排序算法)

堆排序算法简介

堆排序(Heapsort)是指利用堆积数(堆)这种数据结构所设计的一种排序算法,它是选择排序的一种。可以利用数组的特点快速定位指定索引的元素。堆分为大根堆和小根堆,是完全二叉树。大根堆要求是每个节点的值都不大于其父节点的值。在数组的非降序排序中,需要使用大根堆。因为最大值一定在堆顶。

堆排序原理

堆排序利用了大根堆(或小根堆)堆顶记录的值最大(或最小)这一特征。
一、用大根堆排序的基本思想
1、先将初始数组A[0…n]构成一个大根堆。
2、将数组中最大的记录(堆顶)和无序区的最后一个元素交换A[n]交换,由此得到新的无序区A[0 … n]和有序区A[n],且满足A[0 … n-1] <= A[n]
3、由于交换后的新根A[0]可能违反堆行政,故应该将当前无序区A[0…n-1]调整为大根堆。然后再次将A[0…n-1]中R[0]和该区间的最后一个元素A[n-1]交换,由此得到新的无序区A[0…n-2]和有序区A[n-1, n],满足A[0…n-2] <= A[n-1]

知道无序区只有一个元素为止。

二、大根堆排序算法的基本操作
1、建堆,建堆是不断调整堆的过程,从len/2处开始调整,直到第一个节点,此处len是堆中元素的个数。建堆的过程是个线性的过程,从len/2到0处一直调整堆过程。
2、调整堆:调整堆在构建堆的过程中会用到,而且在堆排序过程中也会用到。利用的思想是比较节点i和其子节点left(i),right(i),选出三者最大者(或最小者),如果最大(小)者不是节点i而是它的一个孩子节点,那就狡猾节点i和该孩子节点。然后再调用调整堆过程。调整堆过程时间复杂度与堆的深度有关系,是lgn的操作。因为是沿着深度方向调整的。
3、堆排序:堆排序是利用上面两个过程来进行的。首先是根据元素构建堆。然后取出根节点,将前面len-1个节点进行堆调整过程,然后再将根节点取出,这样一直到所有的节点都取出。

算法分析

1、时间复杂度
堆排序过程中建堆的时间复杂度是O(n)只调用一次;调整堆的时间复杂度是lgn。调用了 n-1次。所以排序的时间复杂度为O(nlgn)

2、空间复杂度
直接交换元素即可,空间复杂度为O(1)

3、稳定性
它是不稳定的排序方法。(排序的稳定性是指如果在排序的序列中,存在前后相同的两个元素的话,排序前 和排序后他们的相对位置不发生变化)

实例分析

以初始元素为2, 4, 7, 5, 8, 1, 3, 6, 6
一、初始元素如下
初始值
二、创建堆
1、列表长度为9,调整堆,父节点为第四个,值为8,无子节点

2、父节点递减,取第3个元素,值为5,子节点为第7,第8个元素。由于子节点比父节点大,右子节点不比左子节点大,交换左节点(第7个元素)。
交换第3和第7个元素
3、父节点递减,取第2个元素,值为7,左子节点(第5个元素)是1,右子节点(第6个元素)是3。不交换

4、父节点递减,取第1个元素,值为1,左子节点(第3个元素)是6,右子节点(第4个元素)是8,由于右子节点最大,所以和父节点交换。交换第1个元素和第4个元素
交换第1个元素和第4个元素

5、父节点递减,取第0个元素,值为2,左子节点(第1个元素)是8,右子节点(第7个元素)是7。由于左子节点比右子节点大且比父节点大。所以和父节点交换,交换第0个元素和第一个元素。
这里写图片描述
这时候父节点为第一个元素,值为2,左子节点为6,右子节点为4,左子节点和父节点交换。
这里写图片描述
下一个循环,父节点是3个元素,左子节点是5,右子节点是6,交换父节点和右子节点。
这里写图片描述
三、堆排序
1、第一次排序
1)创建堆后第0个元素最大。和最后一个元素交换
这里写图片描述
2)调整堆,由于第0个元素值为2,其左子节点第1个元素值为6,右子节点值为7。交换父节点和右子节点。
这里写图片描述
下一个循环,父节点是第2个元素,值为2。左子节点为1,由子节点为3。交换父节点和右子节点。
这里写图片描述
这时候第0个元素最大了。
3)第0个元素和最后一个元素交换(最后一个是第7个了)
这里写图片描述
重复步骤2)1),同时未排序区长度逐一减小

代码实现

1、golang实现

package main

import (
    "fmt"
)

func HeadAdjust(values []int, i, length int) {
    var iChild int
    //
    for ; 2*i+1 < length; i = iChild {
        //获取子节点的位置
        iChild = 2*i + 1
        //如果有右子节点的话,获取较大子节点的位置
        if iChild < length-1 && values[iChild+1] > values[iChild] {
            iChild++
        }
        //如果父节点小于较大的子节点的话,交换父子节点,否则调整结束
        if values[i] < values[iChild] {
            values[i], values[iChild] = values[iChild], values[i]
        } else {
            break
        }
    }
}

func HeadSort(values []int) {
    iLen := len(values)

    //构造堆
    for i := iLen / 2; i >= 0; i-- {
        HeadAdjust(values, i, iLen)
        //fmt.Println(values)
    }

    //逐个提取最大元素。
    for i := iLen - 1; i > 0; i-- {
        values[0], values[i] = values[i], values[0]
        //fmt.Println(values)
        HeadAdjust(values, 0, i)
        //fmt.Println(values)
    }
}

func main() {
    values := []int{2, 4, 7, 5, 8, 1, 3, 6, 6}
    fmt.Println(values)
    HeadSort(values)
    fmt.Println(values)
    return
}

打开注释后的结果:

[2 4 7 5 8 1 3 6 6]
[2 4 7 5 8 1 3 6 6]
[2 4 7 6 8 1 3 5 6]
[2 4 7 6 8 1 3 5 6]
[2 8 7 6 4 1 3 5 6]
[8 6 7 6 4 1 3 5 2]
[2 6 7 6 4 1 3 5 8]
[7 6 3 6 4 1 2 5 8]
[5 6 3 6 4 1 2 7 8]
[6 6 3 5 4 1 2 7 8]
[2 6 3 5 4 1 6 7 8]
[6 5 3 2 4 1 6 7 8]
[1 5 3 2 4 6 6 7 8]
[5 4 3 2 1 6 6 7 8]
[1 4 3 2 5 6 6 7 8]
[4 2 3 1 5 6 6 7 8]
[1 2 3 4 5 6 6 7 8]
[3 2 1 4 5 6 6 7 8]
[1 2 3 4 5 6 6 7 8]
[2 1 3 4 5 6 6 7 8]
[1 2 3 4 5 6 6 7 8]
[1 2 3 4 5 6 6 7 8]
[1 2 3 4 5 6 6 7 8]
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值