堆排序是指利用堆这种数据结构所设计的一种排序算法。堆是一个近似完全二叉树的结构,并同时满足堆的性质:即子结点的键值或索引总是小于(或者大于)它的父节点(同层节点不进行比较)。并且一般来说,升序排列通过构造大顶堆来实现,降序排列通过构造小顶堆来实现。
这种算法不用额外的空间,空间复杂度为o(1),时间复杂度为o(nlogn)
堆是一种完全二叉树(完全二叉树 是 一种除了最后一层之外的其他每一层都被完全填充,并且所有结点都保持向左对齐的树)
堆有两种类型: 大顶堆和小顶堆:
大顶堆:每个结点的值都大于或等于左右孩子结点
小顶堆:每个结点的值都小于或等于左右孩子结点
对于这个算法的具体过程的图示,大家可以看一下堆排序图解这篇博客。
global length # 定义全局变量
def buildMaxHeap(numList):
# 构建最大堆,
global length
length = len(numList)
for i in range(length//2,-1,-1):
# 从最后一个有子节点的根节点节点开始构建的时候从下往上,从右向左构建
heapify(numList,i)
def heapify(numList,i): # 堆调整,将剩下的元素调整成最大堆
global left,right,largest,length
leftChildren = 2 * i + 1
rightChildren = 2 * i + 2
largest = i
if leftChildren < length and numList[leftChildren] > numList[largest]:
# leftChildren < length先判断是否有子节点,因为python数组下标从零开始的缘故,下标为length/2的元素可能会没有子节点。
largest = leftChildren
if rightChildren < length and numList[rightChildren] > numList[largest]:
largest = rightChildren
if largest != i:
# 如果largest(最大的节点所在的下标),不是根节点,说明这三个借点不满足堆的规则,
# 需要进行调整,根节点的下标是i,最大节点的下标是largest,交换即可。
swap(numList,i,largest)
# 当当前的根节点和子节点满足堆的关系之后,由子节点作为根节点的树可能又不满足了,必须在对下一层的树进行检查和调整。
heapify(numList,largest)
def swap(numList,i,j):
numList[i],numList[j] = numList[j],numList[i]
def heapSort(numList):
global length
buildMaxHeap(numList)
for i in range(len(numList)-1,0,-1):
swap(numList,0,i)
length -= 1 # 在调整的时候就不会在调整最后一个元素了
heapify(numList,0)
# 由于交换之前,已经都调整为最大堆了,而交换之后,大部分都符合堆的规则,
# 只从堆顶元素(下标为1)开始,只进行局部的调整就好,
# 这时候不用再像刚开始构建最大堆一样从下往上,从右往左调整交换了。
return numList
numlist = [4,5,4,1,8,7,2,6,3]
print(heapSort(numlist))
def swap(arr,i,j):
arr[i],arr[j] = arr[j],arr[i]
def heapAdjust(arr,start,end):
i = start
temp = arr[start]
j = 2 * i
while j <= end:
if j < end and arr[j] < arr[j+1]:
j += 1
if arr[i] < arr[j]:
arr[i] = arr[j]
i = j
j = 2 * i
else:
break
arr[i] = temp
def heapSort(numList):
numList.insert(0,0)
# 由于python,数组的下标从0开始,因此需要加入一个辅助元素,是所有的元素的下标都往后移动一位。
length = len(numList)-1
firstAdjustRoot = length // 2
for i in range(firstAdjustRoot):
# 在构造最大堆的时候,不会对最左侧的0进行处理,因为i不会取到firstAdjustRoot。
heapAdjust(numList,firstAdjustRoot-i,length)
for i in range(length-1):
swap(numList,1,length-i)
# 由于大顶堆的堆顶元素是最大的,把它和数组最后的元素(堆的最下层最右元素)
# 进行互换,就相当于把最大值放在了最后,下一次,把最后一个元素撇出来,对剩下来的在排序
heapAdjust(numList,1,length-i-1)
# 由于交换之前,已经都调整为最大堆了,而交换之后,大部分都符合堆的规则,
# 只从堆顶元素(下标为1)开始,只进行局部的调整就好,
# 这时候不用再像刚开始构建最大堆一样从下往上,从右往左调整交换了。
return [numList[i] for i in range(1,len(numList))]
numlist = [50, 16, 30, 10, 60, 90, 2, 80, 70]
print(heapSort(numlist))
有辅助元素的堆排序
https://www.cnblogs.com/chengxiao/p/6129630.html
https://www.cnblogs.com/onepixel/articles/7674659.html