一直想把常见的排序算法,都写一遍。今天,终于完成了。打卡之堆排序。
堆排序算法,主要利用到完全二叉数以及大根堆的概念。完全二叉数: 若设二叉树的深度为h,除第 h 层外,其它各层 (1~h-1) 的结点数都达到最大个数,第 h 层所有的结点都连续集中在最左边,这就是完全二叉树。 大根堆:任何一颗子树的最大值都是它本身。
通常情况下,如果你理解了完全二叉数的概念,那么完全二叉数就可以用一个数组来进行表示。
eg:nums=[1,2,3,4,5,6]
如果用数组表示完全二叉数的话,假设下标为i的元素,那么它的左孩子:2*i+1 右孩子为2*i+2 它的父节点为(i-1)//2
ok,那我先来看看大根堆是如何实现的:
def heapinsert(alist, i):
while alist[i] > alist[(i-1)//2] and i!=0:
alist[i], alist[(i-1)//2] = alist[(i-1)//2], alist[i]
i = (i - 1) // 2
nums=[1,3,5,4,2,6]
for i in range(len(nums)):
heapinsert(nums,i)
我们从下往上将每一颗子树的根都调换成该子树的最大值。eg:nums=[1,3,5,4,2,6] 变成:nums=[6,4,5,1,2,3]
相当于做一次大根堆的操作,我们就得到了完全二叉数中的最大值。那我们就可以对数组每次操作大根堆,然后将下标为0的元素与数组最后一位进行元素交换。那数组中的最大值就得到了。 eg:nums=[6,4,5,1,2,3] 转变以后 nums=[3,4,5,1,2,6]。但是这会破坏大根堆的有序性。所以,我们每交换一次,则需要对大根堆进行调整。这个时候我们需要将置换厚的元素从上到下与其左右孩子做比较,往下沉,重新得到一个大根堆(注意,当我们将最大值交换以后,相当与数组的长度减1,不考虑数组末尾的值,因为此时末尾元素,已经排好序了,就是待排数组的最大值)。
def heapify(alist, index, size):
left = index*2+1
while left <= size:
largest = left+1 if left+1 <= size and alist[left] < alist[left+1] else left
largest = index if alist[index] > alist[largest] else largest
if largest == index:
break
alist[largest], alist[index] = alist[index], alist[largest]
index = largest
left = index*2+1
然后,我们来看看整体代码:
def heapsort(alist):
length = len(alist)
#首先对数组进行大根堆排序
for i in range(length):
heapinsert(alist, i)
for j in range(length-1, 0, -1):
#置换堆顶与树末尾的值
alist[0], alist[j] = alist[j], alist[0]
#每次对前j-1个数重新进行大根堆排序
heapify(alist, 0, j-1)
def heapinsert(alist, i):
while alist[i] > alist[(i-1)//2] and i!=0:
alist[i], alist[(i-1)//2] = alist[(i-1)//2], alist[i]
i = (i - 1) // 2
def heapify(alist, index, size):
left = index*2+1#获取当前节点的子节点
#限制数组边界,只排未排好序的数组
while left <= size:
#获取左右孩子较大值的下标
largest = left+1 if left+1 <= size and alist[left] < alist[left+1] else left
#获取当前节点与左右孩子中最大值中较大值的下标
largest = index if alist[index] > alist[largest] else largest
#对比完,如果当前仍是最大值,则已符合大根堆,则退出此次循环
if largest == index:
break
#不然,使子树中根的值最大
alist[largest], alist[index] = alist[index], alist[largest]
#将当前节点置为先前最大值的下标,继续排序
index = largest
left = index*2+1
if __name__ == '__main__':
#获取一个整数数组
nums = list(map(int, input().split()))
#对其进行排序
heapsort(nums)
print(nums)
堆排序的时间复杂度为 额外空间复杂度为
。如果只是建立一个堆,花费时间为
给个前面已完成的排序算法链接: