定义
优先队列是一种支持以下主要操作的抽象数据结构:
Insert(p):添加一个带有优先权p的新元素
ExtractMax():取出具有最大优先权的元素
其他操作:
Remove(it):删除it迭代器指向的一个元素
GetMax():返回具有最大优先权的元素(不改变元素集合)
ChangePriority(it,p):把it迭代器指向的元素权限改为p
使用优先队列的算法
Dijsktra’s algorithm
Prims’ algorithm
Huffman’s algorithm
Heap Sort
优先队列的实现
二叉堆
定义
二叉堆是每个结点值大于等于它的孩子结点值的二叉树(每个结点只有0、1或2个孩子结点)。也就是树的每一条枝上的结点值不小于它的孩子结点。
基本操作
GetMax()
直接返回根节点的值
Insert(it, p)
1.在任意一个叶子结点插入新结点(这会引起堆属性的变化)
2.调整。新结点上移,也就是问题结点和与它连接的父母结点交换直到满足堆的属性
ExtractMax()
1.利用任意一个叶子结点替换根节点(这会引起堆的属性发生变化)
2.调整。问题结点下移,也就是问题结点和与它连接的较大孩子结点交换,直到满足堆的属性
ChangPriority()
1.更改指定结点的优先级(这会引起堆属性的变化)
2.根据指定结点的优先级是增大还是减小,上移或下移问题结点,直至满足堆的属性
Remove()
1.把要删除的结点的优先级更改为无穷大(这会引起堆的属性发生变化)
2.调整。问题结点上移,也就是把要删除的结点和与它连接的父母节点交换,直至满足堆的属性
调用ExtractMax()删除根节点
总结
时间复杂度 | |
---|---|
GetMax() | O(1) |
Insert() | O(n) |
ExtractMax() | O(n) |
ChangePriority() | O(n) |
Remove | O(n) |
完全二叉树
定义
1.二叉树
2.最后一层的结点居于最左端
3.其它层结点数达到最大
优势
1.树的高度低
一颗包含n个结点的完全二叉树最大高度为O(log n)
2.可以存成数组
parent(i) = i/2(向下取整)
LeftChild(i) = 2 * i
RightChild(i) = 2 * i + 1
缺点
保持完全二叉树的属性
插入一个元素:把要插入的结点当做叶子结点插入到最后一层的最左端空白位置,然后上移元素
删除最大值:用最后的叶子结点替代根节点,然后下移替代根节点之后的叶子结点
伪代码实现
基本设置
1.maxSize:堆中元素个数的最大值
2.size:堆的大小
3.H[1…maxSize]:长度为maxSize的数组,用来保存堆
parent(i)
return i/2
LeftChild(i)
return 2 * i
RightChild(i)
return 2 * i + 1
SiftUp(i)
#循环方式实现
while i > 1 and H[i] > H[parent(i)]:
swap H[i] and H[parent(i)]
i <--- parent(i)
#递归函数实现
if i > 1 and H[i] > H[parent(i)]
swap H[i] and H[parent(i)]
i <--- parent(i)
SiftUp(i)
SiftDown(i)
#循环实现
while true
maxIndex <--- i
l <---- LeftChild(i)
if l <= size and H[l] > H[i]
maxIndex <--- l
r <--- RightChild(i)
if r <= size and H[r] > H[maxIndex]
maxIndex <--- r
if i != maxIndex
swap H[i] and H[maxIndex]
else
break
#递归函数实现
#找出该结点的较大孩子结点
maxIndex <--- i
l <--- LeftChild(i)
if l <= size and H[l] > H[maxIndex]
maxIndex <--- l
r <--- RightChild(i)
if r <= size and H[r] > H[maxIndex]
maxIndex <--- r
if i != maxIndex
swap H[i] and H[maxIndex]
SiftDown(maxIndex)
Insert(p)
if size = maxSize
return ERROR
else
size <--- size + 1
H[size] <--- p
SifUp(size)
ExtractMax()
if size = 1
return ERROR
else
result <--- H[1]
H[1] <--- H[size]
size <--- size - 1
SiftDown(1)
return result
remove(i)
H[i] <--- 1000000
SiftUp(i)
ExtractMax()
ChangePriority(i,p)
oldp <--- H[i]
H[i] <--- p
if oldp > p
SiftDown(i)
if oldp < p
SiftUp(i)
总结
实现结果:
1.快速。所有操作的时间复杂度为O(log n)(GetMax()的时间复杂度甚至为O(1))
2.空间利用率高。在数组中只存储优先级,不存父母结点和孩子结点之间的连接关系
3.容易实现。所有的操作用几行代码就可以实现。
应用
使用优先级队列排序
HeapSort(A)(不考虑空间使用)
1.创建一个空的优先队列
2.for i from 1 to n
Insert(i)
3.for i from n downto 1
A[i] <——– ExtractMax()
上述算法的特点:
1.基于比较且时间复杂度为O(n * log n)
2.自然生成选择排序,使用良好的数据结构替代了以往扫描整个数组寻找最大数的方法
3.使用了额外的内存空间
把数组转换成堆
主要思路:从下至上的调整修正堆的性质
1.首先,所有的叶子结点都是满足堆的性质的
2.然后,从深度为1的子树开始调整修正堆的性质
3.依次遍历,直到根节点时,整棵树都满足堆的性质
算法实现:(时间复杂度为n * log n)
BuildHeap(A)
size <--- n
for i from n/2 downto 1
SiftDown(i)
HeapSort(A)(限制存储空间的使用)
BuildHeap(A)
repeat (n - 1)times:
swap A[1] and A[size]
size <--- size - 1
SiftDown(1)
总结
此种堆排序和上面第一种相比,时间复杂度是相同的均为 O(n * log n),但是第二种堆排序不需要额外的内存空间
备注
以0为起始搜引号的数组:
parent(i)
return (i-1)/2
LeftChild(i)
return 2 * i + 1
RightChild(i)
return 2 * i +2
最小二叉堆:二叉堆是每个结点值大于等于它的孩子结点值的二叉树(每个结点只有0、1或2个孩子结点)。也就是树的每一条枝上的结点值不大于它的孩子结点。
d叉堆:和二叉堆类似,不同之处在于d叉堆的每个结点的孩子结点树最多有d个,对应树的高度为O(logd n)