堆排序是非常好的排序方法,其时间复杂度不管什么情况一直都是在O(nlogn)O(nlogn)O(nlogn),空间复杂度还是O(1)O(1)O(1),可以说效果是非常好的。不过,堆排序首先要用到堆的概念,不如快速排序易于理解。而且,虽然其时间复杂度是O(nlogn)O(nlogn)O(nlogn),但是其在实现的过程中,其效率还是不如快速排序。
概念
堆排序是使用堆进行排序的。堆是一种特殊的完全二叉树。并且还区分为大根堆和小根堆。
堆: 可以分为大根堆和小根堆
大根堆: 每个节点的值都大于其左右孩子节点的值
小根堆: 每个节点的值都小于其左右孩子节点的值
完全二叉树: 除了最后一层之外其他每一层都被完全填充,并且所有结点都保持向左对齐的树。
算法步骤
Step 1: 首先将为排序的序列排成一个大根堆,堆顶就是整个序列的最大值
Step 2: 将堆顶和最下层的最后元素交换,也就是将最大值排序好。
Step 3: 交换之后,我们对没能排序的序列重新构建堆
Step 4: 重复执行步骤2和3,直到未排序的序列只有1个。
算法评价
方法 | 平均时间复杂度 | 最坏时间复杂度 | 最好时间复杂度 | 空间复杂度 |
---|---|---|---|---|
堆排序 | O(nlogn)O(nlogn)O(nlogn) | O(nlogn)O(nlogn)O(nlogn) | O(nlogn)O(nlogn)O(nlogn) | O(1)O(1)O(1) |
最后堆排序不是一个稳定的排序算法。相同的数字在重新排列大根堆的时候可能会优变化。
python实现
########################################################
##### 堆排序 #####
########################################################
#堆调整函数,在交换之后调用将其转换为堆
def heap_adjust(nums, start_index, end_index):
i = start_index
j = 2 * i
while j <= end_index:
if (j < end_index) and (nums[j] < nums[j + 1]):
j = j + 1
if nums[i] < nums[j]:
nums[i],nums[j] = nums[j],nums[i]
i = j
j = 2 * i
else:
break
def heap_sort(nums,order=1):
nums.insert(0,-987654321) #填一个数字占位,但是这个数不参加排序
nums_length = len(nums) - 1 #获取待排序列表的长度
parent = int(nums_length / 2) #找到所有的父节点
for i in range(parent):
heap_adjust(nums, parent - i, nums_length) #将最初的序列排成一个大根堆
for i in range(nums_length - 1):
nums[1],nums[nums_length-i] = nums[nums_length-i],nums[1] #交换序列首位和末尾
heap_adjust(nums, 1, nums_length - i - 1) #交换后排序成为大根堆
if order == 1:
return nums[1:]
else:
return nums[1:][::-1]
nums = [1,4,3,2,9,12,23,54,76,32]
print(heap_sort(nums))
"""
[1, 2, 3, 4, 9, 12, 23, 32, 54, 76]
"""
print(heap_sort(nums,0))
"""
[76, 54, 32, 23, 12, 9, 4, 3, 2, 1]
"""