参考:
百度百科:堆排序
堆排序(heap sort)
关键名词
参考:
二叉树:每个节点最多有两个子树的树结构
完全二叉树:叶节点只能出现在最下层和次下层,并且最下面一层的节点都集中在该层最左边的若干位置的二叉树。设树层数为
h
,则1-(h-1)
层的节点数均达到了最大值,而第h
层的节点均集中在最左边- 将完全二叉树的节点值排成列表形式,则第
i
个节点的左右子树为2i
和2i+1
- 将完全二叉树的节点值排成列表形式,则第
堆分为 大根堆 和 小根堆
大根堆:根节点(或称为 堆顶)的值 大于等于 左右子树的完全二叉树
小根堆:根节点(或称为 堆顶)的值 小于等于 左右子树的完全二叉树
工作原理
根据堆的特性,其位于堆顶的值为最大值或者最小值,所以每次取堆顶节点值,删除堆顶节点,重新构建堆,直到堆为空为止,排序完成
算法思想
难点一:如何构建堆
堆既然是完全二叉树,所以可以用序列表示,每次新增加一个节点,将其加入到序列末尾,设为 n
,其根节点位置即为 n // 2
,比较两者的大小,进行替换,依次类推,调整堆的结构
为方便计算,设序列为 [-1, x1, x2, x3, ...]
,其第一个元素 -1
无效,从下标 1
开始计算
难点二:如何删除堆顶后,重新构建堆
删除堆顶后,取序列末尾元素,放在堆顶位置,重新计算堆的结构
对于大根堆来说,每次向下调整时,如果需要交换根和左右子树的值,和左右子树中较大值进行交换
对于小根堆来说,与之相反
思路
获得一个无序序列后,先将其构建成堆结构,然后每次取堆顶元素,重新构建堆,直到堆为空为止
Python 算法实现
实现一:使用一个辅助序列
# -*- coding: utf-8 -*-
"""
堆排序实现
"""
import random
import math
__author__ = 'zj'
def create_data(leng, min, max):
"""
创建待排序序列
:param leng: 序列长度
:param min: 最小值
:param max: 最大值
:return: 列表
"""
li = range(min, max)
return random.sample(li, leng)
def heap_sort_add(heap, v):
"""
在堆上加入一个元素
:param heap: 大根堆
:param v: 插入数据
:return:
"""
heap.append(v)
idx = len(heap) - 1
r = idx // 2
while r >= 1:
if heap[r] < heap[idx]:
heap[r], heap[idx] = heap[idx], heap[r]
idx = r
r = idx // 2
else:
break
return heap
def heap_sort_delete(heap):
"""
删除堆顶元素
:param heap: 大根堆
:return:
"""
if len(heap) == 1:
return heap
elif len(heap) == 2:
del heap[0]
return heap
heap[1] = heap[-1]
del heap[-1]
n = len(heap) - 1
idx = 1
n = len(heap)
while idx < n:
lchild = 2 * idx
rchild = 2 * idx + 1
if lchild >= n:
break
elif lchild < n and rchild >= n:
if heap[idx] < heap[lchild]:
heap[idx], heap[lchild] = heap[lchild], heap[idx]
idx = lchild
else:
break
elif lchild < n and rchild < n:
if heap[lchild] > heap[rchild]:
if heap[idx] < heap[lchild]:
heap[idx], heap[lchild] = heap[lchild], heap[idx]
idx = lchild
else:
break
else:
if heap[idx] < heap[rchild]:
heap[idx], heap[rchild] = heap[rchild], heap[idx]
idx = rchild
else:
break
return heap
def heap_sort(li, reverse=False):
"""
堆排序实现
:param li: 待排序列表
:param reverse: 是否从大到小排序,默认为False
:return: 已排序列表
"""
src = [0]
for item in li:
heap_sort_add(src, item)
n = len(li)
for i in xrange(n):
if reverse:
li[n - i - 1] = src[1]
else:
li[i] = src[1]
heap_sort_delete(src)
return li
if __name__ == '__main__':
da = create_data(10, 30, 60)
print da
heap_sort(da)
print da
实现二:使用自身序列,实现从小到大排序
# -*- coding: utf-8 -*-
"""
堆排序实现
"""
import random
import math
__author__ = 'zj'
def create_data(leng, min, max):
"""
创建待排序序列
:param leng: 序列长度
:param min: 最小值
:param max: 最大值
:return: 列表
"""
li = range(min, max)
return random.sample(li, leng)
def heap_sort_add_v2(heap, n):
"""
堆中加入新数据,下标为 n
:param heap: 堆和待排序序列
:param n: 前 n-1 个元素为堆,后面元素为待排序序列
:return:
"""
idx = n
r = idx // 2
while r >= 1:
if heap[r] < heap[idx]:
heap[r], heap[idx] = heap[idx], heap[r]
idx = r
r = idx // 2
else:
break
return heap
def heap_sort_delete_v2(heap, n):
"""
删除堆顶元素
:param heap: 大根堆
:return:
"""
heap[1], heap[n] = heap[n], heap[1]
idx = 1
while idx < n:
lchild = 2 * idx
rchild = 2 * idx + 1
if lchild >= n:
break
elif lchild < n and rchild >= n:
if heap[idx] < heap[lchild]:
heap[idx], heap[lchild] = heap[lchild], heap[idx]
idx = lchild
else:
break
elif lchild < n and rchild < n:
if heap[lchild] > heap[rchild]:
if heap[idx] < heap[lchild]:
heap[idx], heap[lchild] = heap[lchild], heap[idx]
idx = lchild
else:
break
else:
if heap[idx] < heap[rchild]:
heap[idx], heap[rchild] = heap[rchild], heap[idx]
idx = rchild
else:
break
return heap
def heap_sort_v2(li):
"""
从小到大排序
:param li:
:return:
"""
li.insert(0, -1)
for i in xrange(2, len(li)):
heap_sort_add_v2(li, i)
for i in range(2, len(li))[::-1]:
heap_sort_delete_v2(li, i)
del li[0]
return li
if __name__ == '__main__':
da = create_data(10, 30, 60)
print da
heap_sort_v2(da)
print da
性能分析
稳定性
堆排序是不稳定排序算法
在删除堆顶,重新构建堆的过程中,需要将末尾节点元素放置在堆顶,重新构建,这样会破坏相对位置关系
时间复杂度
其时间复杂度为 O(N*logN)
空间复杂度
空间复杂度为 O(1)