返回
堆排序算法——构建最大堆之陷阱
前端
2023-10-09 07:13:07
让我们从上一篇文章的结尾处开始,我们已经对堆进行了处理,掌握了shift up和shift down等操作。现在,让我们来看一个随机的数组,蓝色的部分表示的是叶子结点。
[15, 35, 22, 18, 43, 62, 30, 12, 10, 50]
我们从叶子结点的最后一个开始,它的值是62,索引是10。我们将它与它的父节点(索引是5)进行比较,发现62大于30,因此我们需要进行shift down操作。
首先,我们将62与它的左子节点(索引是10/2=5)比较,发现62大于15,因此我们将62移动到左子节点的位置。
然后,我们将15与它的右子节点(索引是10/2+1=6)比较,发现15小于35,因此我们将35移动到左子节点的位置。
现在,15的左子节点是35,右子节点是22,它们都大于15,因此shift down操作完成。
但是,这里有一个陷阱需要注意。如果在shift down操作过程中,我们发现一个节点的值小于它的子节点的值,那么我们就需要停止shift down操作,因为此时堆已经建好了。
在上面的例子中,当我们将15移动到左子节点的位置后,我们发现15的左子节点是35,右子节点是22,它们都大于15,因此shift down操作完成。
现在,我们已经将一个随机数组转换成了一个最大堆。
[50, 35, 43, 30, 22, 62, 18, 12, 10, 15]
最大堆的性质是,根节点的值总是最大的,并且每个节点的值都大于或等于其子节点的值。
最后,我们给出堆排序算法的完整代码,帮助读者更好地理解堆排序算法的实现。
def heap_sort(arr):
"""堆排序算法"""
# 将数组构建成最大堆
for i in range(len(arr) // 2 - 1, -1, -1):
heapify(arr, i, len(arr))
# 将最大堆排序
for i in range(len(arr) - 1, 0, -1):
arr[0], arr[i] = arr[i], arr[0] # 交换堆顶元素和最后一个元素
heapify(arr, 0, i) # 重新堆化
return arr
def heapify(arr, i, n):
"""堆化操作"""
largest = i # 假设父节点是最大值
left = 2 * i + 1 # 左子节点索引
right = 2 * i + 2 # 右子节点索引
# 如果左子节点大于父节点
if left < n and arr[left] > arr[largest]:
largest = left
# 如果右子节点大于父节点
if right < n and arr[right] > arr[largest]:
largest = right
# 如果父节点不是最大值
if largest != i:
arr[i], arr[largest] = arr[largest], arr[i] # 交换父节点和最大值
heapify(arr, largest, n) # 递归堆化
if __name__ == "__main__":
arr = [15, 35, 22, 18, 43, 62, 30, 12, 10, 50]
print(heap_sort(arr))
输出结果:
[10, 12, 15, 18, 22, 30, 35, 43, 50, 62]