文章目录
list
- 项目目录
- 项目描述
- 基本功能实现
- 数据生成器测试
- 时间复杂度理论推导
- 时间复杂度实验验证
- 如何设计实验
- 如何获取数据
- 如何绘制图像
- 和其他数据结构类型的比较
- 和普通数组的比较 v e c t o r vector vector
- 和链表list的比较 l i s t list list
- 和和普通堆的比较 p r i o r i t y _ q u e u e priority\_queue priority_queue
- 和左高树的比较
- 和红黑树的比较 m a p map map and $set $
- 和哈希的比较 h a s h hash hash
- 界面的制作
list
二项堆项目目录
二项堆命令行版(主要包含相关效率分析实验)
│ list.txt //项目目录
│ binaryHeap.h
| binaryHeap.cpp
│ list.txt //项目目录
│ vislization.cpp
| main.cpp //主函数文件
│ in.txt
│ graph.dot
│ graph.jpg
二项堆Qt版本(可视化操作)
| binaryheap.cpp
| binaryheap.h
| binaryHeap.pro
| binaryHeap.pro.user
| list.txt
| main.cpp
| mainwindow.cpp
| mainwindow.h
| mainwindow.ui
| visualization.cpp
| visualization.h
项目描述
二项堆的实现和分析
问题描述:
认识二项树、二项堆数据结构,并能应用该结构解决实际问题。
二项堆是二项树的集合,二项树是一种递归定义的有序树。它的递归定义如下:
(1) 二项树B0只有一个结点;
(2) 二项树Bk由两棵二项树B(k-1)组成的,其中一棵树是另一棵树根的最左孩子。
二项树具有如下的性质:
-
对于树Bk,该树含有2k个节点;
-
树的高度是k;
-
在深度为i中含有 节点,其中i = 0, 1,2 … , k;
二项堆H是具备如下性质的二项树的集合:
-
H中的每个二项树遵循最小堆性质;
-
对于任意的整数k的话,H中最多有一个二项树的根的度数是k;
基本要求:
① 设计二项堆ADT,其上的基本操作包括:
Make Heap ():初始化一个空堆;
Find-Min():返回一个指向最小关键字元素的指针
Union(H):与堆H进行合并,合并后的结果保存在当前堆,H变为空
Insert(x):插入元素x
Extract-Min():从堆中删除最小关键字元素,并返回指向删除元素的指针。
Decrease Key (x,k):将元素x的关键字赋予新值k;
Delete(x):从堆中删除元素x;
② 实现二项堆ADT,包括实现二项堆的存储结构以及其上的基本操作,并分析基本操作的时间复杂性。
③ 实现二项堆ADT的基本操作展示。
数据生成器
数据操作格式
第1行:初始化元素数n,操作个数m
第2~n+1行:n个元素的大小
接下来m行:<op,value1,value2=NULL> 格式
op:操作类型
value1,value2:大小
<insert,value>
添加
<erase,value>
删除元素
<erasemin>
删除最小
<update,old,new>
更新元素
特别注意:操作中删除操作和插入操作的数量关系
关于理论分析
时间复杂度的推导
应用贪心思想
二项堆各种操作的时间复杂度如下所示:
操作名称 | 操作时间复杂度 |
---|---|
合并 | O ( l o g n ) O(logn) O(logn) |
插入 | O ( l o g n ) O(logn) O(logn) |
删除 | O ( l o g n ) O(logn) O(logn) |
删除最小 | O ( l o g n ) O(logn) O(logn) |
减小元素大小 | O ( l o g n ) O(logn) O(logn) |
增大元素大小 | O ( l o g n ) O(logn) O(logn) |
查找最小 | O ( l o g n ) O(logn) O(logn) |
具体推导如下所示:
根据二项堆的定义可知,二项堆满足一下两条性质
1. 每棵二项树都满足最小堆性质。即,父节点的关键字 <= 它的孩子的关键字。
2. 不能有两棵或以上的二项树具有相同的度数(包括度数为0)。换句话说,具有度数k的二项树有0个或1个。
3. 二项堆是二项树的集合,且二项树B0只含有一个点(零图)
4. 二项树的递归定义:
B
k
=
2
B
k
−
1
B_k=2B_{k-1}
Bk=2Bk−1
5.二项堆中的任意两颗树的度数(即树中的节点个数)都不相同
二项堆的最重要性质
合并操作的时间复杂度
不妨设两个二项堆中含 n 1 n_1 n1和 n 2 n_2 n2和节点,且分别由 k 1 k_1 k1和 k 2 k_2 k2个二项树构成。
根据二项堆的性质4可知,
二
项
树
B
k
的
节
点
个
数
满
足
B
k
=
2
k
(
k
=
0
,
1
,
2
,
3...
)
二项树B_k的节点个数满足B_k=2^k (k=0,1,2,3...)\\
二项树Bk的节点个数满足Bk=2k(k=0,1,2,3...)
由于在合并两个二项堆的时候,首先应将两个二项堆的跟链表进行归并,即完成有序链表的归并,因此第一步归并操作的时间复杂度为
O
(
跟
链
表
长
度
1
+
跟
链
表
长
度
2
)
O(跟链表长度1+跟链表长度2)
O(跟链表长度1+跟链表长度2)
因此,当跟链表长度最大时,归并的时间复杂度最大。
以第一个二项堆为例,二项堆中的节点个数满足如下等式
二
项
堆
的
总
节
点
数
=
所
有
二
项
树
的
节
点
数
之
和
二项堆的总节点数=所有二项树的节点数之和
二项堆的总节点数=所有二项树的节点数之和
因此,带入通项公式
B
k
=
2
B
k
−
1
B_k=2B_{k-1}
Bk=2Bk−1有
n
1
=
2
x
1
+
2
x
2
+
2
x
3
+
2
x
4
+
.
.
.
n_1=2^{x_1}+2^{x_2}+2^{x_3}+2^{x_4}+...
n1=2x1+2x2+2x3+2x4+...
由于二项堆中的任意两颗树的度数(即树中的节点个数)都不相同因此
x
1
!
=
x
2
!
=
x
3
!
=
x
4
!
=
.
.
.
!
=
x
k
1
x_1!=x_2!=x_3!=x_4!=...!=x_{k_1}
x1!=x2!=x3!=x4!=...!=xk1
当总结点数
n
1
n_1
n1给定时,
x
1
,
x
2
,
x
3
,
x
4
x_1,x_2,x_3,x_4
x1,x2,x3,x4顺次从0开始时,求和最小(否则反证法可与推出更小的)。因此当满足
n
1
=
∑
i
=
0
k
1
2
i
n_1=\sum\limits_{i=0}^{k1}{2^i}
n1=i=0∑k12i
时, k 1 k_1 k1达到最小。
由等比数列求和公式可得,
n
1
=
B
0
(
1
−
2
k
1
)
1
−
2
n_1=\frac{B_0(1-2^{k_1})}{1-2}
n1=1−2B0(1−2k1)
化简可得
k
1
=
l
o
g
(
n
1
+
1
)
k_1=log(n_1+1)
k1=log(n1+1)
即
k
1
k_1
k1最大为
l
o
g
(
n
1
+
1
)
log(n_1+1)
log(n1+1).因此对于合并两个二项堆的时间复杂度为
O
(
l
o
g
(
n
1
+
1
)
)
+
O
(
l
o
g
(
n
2
+
1
)
)
=
O
(
l
o
g
n
)
O(log(n_1+1))+O(log(n_2+1))=O(logn)
O(log(n1+1))+O(log(n2+1))=O(logn)
关于合并操作时间复杂度的细节
假设
n
1
+
n
2
=
n
n_1+n_2=n
n1+n2=n,则
l
o
g
(
n
1
+
1
)
+
l
o
g
(
n
2
+
1
)
=
l
o
g
(
(
n
1
+
1
)
×
(
n
2
+
1
)
)
log(n_1+1)+log(n_2+1)=log((n_1+1)\times (n_2+1))\\
log(n1+1)+log(n2+1)=log((n1+1)×(n2+1))
令
f
(
n
1
,
n
2
)
=
(
n
1
+
1
)
×
(
n
2
+
1
)
f(n_1,n_2)=(n_1+1)\times (n_2+1)
f(n1,n2)=(n1+1)×(n2+1) 且
n
1
+
n
2
=
n
n_1+n_2=n
n1+n2=n,则由均值不等式可得
f
(
n
1
,
n
2
)
≤
(
n
1
+
n
2
+
2
2
)
2
f(n_1,n_2)\leq (\frac{n_1+n_2+2}{2})^2
f(n1,n2)≤(2n1+n2+2)2
当且仅当
n
1
+
1
=
n
2
+
1
n_1+1=n_2+1
n1+1=n2+1时等号成立。也就是说合并两个大小相近的二项堆所需时间更多
(也可以用拉格朗日乘数法对多元函数的条件极值求导,侧面反应外排序中每一个顺串的大小尽量相同时,时间复杂度最小)
使用拉格朗日乘数法进行严格的推导(避免基本不等式等号能否取到的判断)
插入操作时间复杂度
相当于原来的二项堆和一个只含一个元素的二项堆进行合并,带入上述公式可得,时间复杂度为 O ( l o g ( 1 + 1 ) ) + O ( l o g ( n + 1 ) ) = O ( l o g n ) O(log(1+1))+O(log(n+1))=O(logn) O(log(1+1))+O(log(n+1))=O(logn)