堆排序python理解_堆排序-python

本文详细介绍了堆排序的概念和实现过程,包括最大堆的定义、堆排序的步骤、代码实现以及时间空间复杂度分析。通过实例展示了如何将列表调整为最大堆,交换堆顶元素并重新调整堆,最后得出堆排序的时间复杂度为O(nlogn)。
摘要由CSDN通过智能技术生成

复习之前学过的堆排序,发现掌握的不是特别牢固,又仔细阅读了几篇博文,整理出来这篇记录。

1 堆排序介绍

1.1 与堆有关的概念

二叉树:每个节点最多有两个子节点的树。

完全二叉树:除了最后一层之外,每一层都被完全填充并且最后一层左对齐的二叉树。

最大堆:每个父节点都大于孩子节点的完全二叉树。

最小堆:每个父节点都小于孩子节点的完全二叉树。

这里我们讨论最大堆,在最大堆中,如果节点标号从 1 开始,满足如下条件:

父节点标号为 i ,左孩子节点标号为 i*2, 右孩子节点标号为 i*2 + 1。

最后一个节点标号为 j ,则它的父节点的标号为 j // 2,也是最后一个有子节点。

1.2 堆排序

给定一个列表[90,50,80,16,30,60,70,10,2],在上面讲到,节点标号从1开始满足某些性质方便表示,为了和下标索引对应起来,在列表最前端添加一个占位元素0,此时列表变为[0,90,50,80,16,30,60,70,10,2]。

堆排序主要步骤如下:

首先我们将列表调整成一个最大堆。

此时堆首的元素最大,将它与堆最有一个元素交换。

将剩下的元素调整成一个最大堆。

循环执行,直到需要调整的堆中只有一个元素。

2堆排序步骤详解

将列表调整为一个最大堆,得到结果如下:

初始的最大堆

将最大的堆顶元素与最后一个元素交换,即90与2交换,得到如下结果:

将90与2交换后的堆

此时除了90剩下的元素构成的堆已经不是最大堆,调整剩下的元素,使它成为最大堆

调整元素,使它成为最大堆

调整结果如下,然后继续重复上述步骤,直到堆中只有一个元素。

剩下的元素变为最大堆

3 代码

整体的排序分为 2 块:

初始化最大堆

交换堆顶与堆尾元素

注:

1.在初始化最大堆的过程中,从最后一个有子节点开始,也就是从最后一个极小堆开始

2.在队首加入占位元素,如果直接用python内置的list,用.insert(0)方法时间复杂度为O(n),而用单链表时间复杂度仅为O(1)。文件头需要加入from collections import deque

def heap_sort(l):

# 加入占位元素

L = deque(l)

L.appendleft(0)

Length = len(L) - 1 #最大索引下标

first_sort_count = Length // 2 # 最后一个有子节点索引下标

# 初始化最大堆, 从最后一个有子节点开始

for i in range(first_sort_count):

heap_adjust(L, first_sort_count - i, Length)

# 开始排序

for i in range(Length-1):

swap_param(L, 1, Length-i)

heap_adjust(L, 1, Length-i-1)

return [L[i] for i in range(1, len(L))]

def heap_adjust(L, start, end):

tmp = L[start]

i = start

j = i*2

while j <= end:

if (j

j += 1

if tmp < L[j]:

L[i] = L[j]

i = j

j = i*2

else:

break

L[i] = tmp

def swap_param(L, i, j):

L[i], L[j] = L[j], L[i]

4 时间和空间复杂度

空间复杂度:O(1)。就地排序,全程并没有使用额外的空间。

时间复杂度:O(nlogn)。

分为初始化堆过程和每次选取最大数后重新建堆的过程。

1.初始化建堆过程时间:O(n)

首先要理解怎么计算这个堆化

假设高度为k,则从倒数第二层右边的节点开始,这一层的节点都要执行子节点比较然后交换(如果顺序是对的就不用交换);倒数第三层呢,则会选择其子节点进行比较和交换,如果没交换就可以不用再执行下去了。如果交换了,那么又要选择一支子树进行比较和交换;

那么第 i 层的时间计算为:s = 2^( i – 1 ) * ( k – i );其中 i 表示第几层,2^( i – 1) 表示该层上有多少个元素,( k – i) 表示子树上要比较的次数,如果在最差的条件下,就是比较次数后还要交换;因为这个是常数,所以提出来后可以忽略;

S = 2^(k-2) * 1 + 2^(k-3)2+…..+2(k-2) + 2^(0)*(k-1) ===> 因为叶子层不用交换,所以i从 k-1 开始到 1;

这个等式求解:等式左右乘上2,然后和原来的等式相减,就变成了:

S = 2^(k – 1) + 2^(k – 2) + 2^(k – 3) ….. + 2 – (k-1)。

除最后一项外,就是一个等比数列了,直接用求和公式:S =

S = 2^k -k -1;又因为k为完全二叉树的深度,所以 (2^k) <= n < (2^k -1 ),总之可以认为:k = logn

综上所述得到:S = n – longn -1,所以时间复杂度为:O(n)。

2.重构最大堆过程

在取出堆顶点放到对应位置并把原堆的最后一个节点填充到堆顶点之后,需要对堆进行重建,只需要对堆的顶点调用heap_adjust()函数。

每次重建意味着有一个节点出堆,所以需要将堆的容量减一。heap_adjust()函数的时间复杂度k=log(n),k为堆的层数。所以在每次重建时,随着堆的容量的减小,层数会下降,函数时间复杂度会变化。重建堆一共需要n-1次循环,每次循环的比较次数为log(i),则相加为:log2+log3+…+log(n-1)+log(n)≈log(n!)。可以证明log(n!)和nlog(n)是同阶函数,所以时间复杂度为O(nlogn)。

最终的时间复杂度为O(nlogn)。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值