学习记录2:优先队列 (二叉堆实现)
2016-07-26 11:23
555 查看
优先队列是一类特殊的队列,它允许至少下列两种操作的数据结构:Insert(插入),DeleteMin(删除最小者,或者满足某个条件的最靠近者什么的,这里就假设最小值优先)
这里的是用二叉堆实现的,还可以用链表,二叉查找树,平衡树等数据结构实现
先说下二叉堆;
堆,具有堆序性和结构性
二叉堆是一棵被完全填满的二叉树,但是它的底层可能出现例外,这种底层上的元素从右往左依次被填入的树被称为完全二叉树;
二叉堆是一棵完全二叉树
这里有个性质 : 一棵高为h的完全二叉树有2^h ~ 2^(h+1)-1个节点 (假设高为h ,这层完全被填满就有2^(h+1)-1 个点,如果底层只有1个几点就是2^h-1 +1 =2^h 个节点)
所以完全二叉树的高是 logN
我们运用这些数据结构的目的也是使操作更有效率的被执行,这里我们想要快速的找到最小的那个元素,所以最小的这个元素最好是放在根上,继续往下推,不妨设想每个子树都是一个满足这个条件的堆,也就是说,每个节点都小于它的的所有后裔;
这样一来,我们可以得到一个堆序性质,在一个堆中,对于除根节点外的其他节点X , X的父亲中的关键字小于或等于X中的关键字
基于以上的设想,我们有以下的操作
先是声明
再是初始化
Insert(插入)
插入之前,我们现在下一个空闲位置插入一个空穴(前面说了,二叉堆是一个完全二叉树);
如果X可以放在该空穴中而不破坏堆序性质,那么就完成插入了,否则,我们把空穴的父节点的元素移入空穴,这样空穴就朝根的方向上移了一步,这也称为上滤,因为按照堆序性质,由根节点开始到每个叶子节点的每条链都是顺序的,当我们插入一个新的X后,在X由空闲位置到目标位置到根节点的那条链上也是顺序的(参考插排),而其他的被向下移动的点,在它未移动的时候,已经是满足性质的,即每个以这个点开始的堆都是一个最小堆(这里设最小值优先),当我们把这个值向下移动后,那个空位被填入相等或更小的值,堆序性得以保留;每次最坏时间是O(logN)
下面是实现代码
DeleteMin(删除最小元)
删除最小元的时候,就是删除堆的根节点,删除比较容易,但是这样一来,在堆的根节点处就产生了一个空穴,为了满足完全二叉树的特性,我们应该把最后一个元素X移动到堆的某个地方,这样一来就有两种选择:
1. X可以被移入空穴,一般不大可能
2. 将空穴中较小的那个儿子移入空穴,重复步骤直至X能够移入空穴;、
这种方式称为下滤,因为我们是从其中选择较小元移入空穴,并使空穴下移,所以在那个未移动的兄弟,以它为起点的是满足最小堆的要求的。而变成他父节点的那个兄弟而由根到那个变更点的序也是满足要求的。及变更点的儿子也是满足要求的,这样一来,下推过程中,我们只要判断每个点的两个儿子是不是都比X大就可以了,这里将节点均当成有两个儿子,还要注意下滤的范围
BuildHeap(构建堆)
基于以上两种操作,我们可以看看构建堆,这里构建堆有两种方式
1. 通过Insert 操作将N个关键字插入到一个空堆中,时间是O(N);
2. 先将N个关键字以任意顺序放入树中,然后下滤;
这里又有一个定理:包含2^(h+1)-1个节点的理想二叉树的节点高度和为2^(h+1)-(h+1);
剩下这个是基于以上代码的一个排序的例子
这里的是用二叉堆实现的,还可以用链表,二叉查找树,平衡树等数据结构实现
先说下二叉堆;
堆,具有堆序性和结构性
二叉堆是一棵被完全填满的二叉树,但是它的底层可能出现例外,这种底层上的元素从右往左依次被填入的树被称为完全二叉树;
二叉堆是一棵完全二叉树
这里有个性质 : 一棵高为h的完全二叉树有2^h ~ 2^(h+1)-1个节点 (假设高为h ,这层完全被填满就有2^(h+1)-1 个点,如果底层只有1个几点就是2^h-1 +1 =2^h 个节点)
所以完全二叉树的高是 logN
我们运用这些数据结构的目的也是使操作更有效率的被执行,这里我们想要快速的找到最小的那个元素,所以最小的这个元素最好是放在根上,继续往下推,不妨设想每个子树都是一个满足这个条件的堆,也就是说,每个节点都小于它的的所有后裔;
这样一来,我们可以得到一个堆序性质,在一个堆中,对于除根节点外的其他节点X , X的父亲中的关键字小于或等于X中的关键字
基于以上的设想,我们有以下的操作
先是声明
typedef struct HeapStruct { int Capacity;//总容量 int Size;//目前的大小 int *Elements; } *PriorityQueue;
再是初始化
const int MinData = 0;//要取一个绝对小的数 来保证能在操作过程中跳出循环 PriorityQueue Initialize(int MaxElements) { PriorityQueue H = new HeapStruct; if (H == NULL) { cout << "out of space" << endl; return H; } H->Elements = new int[MaxElements]; if (H->Elements == NULL) { cout << "out of space" << endl; return H; } H->Capacity = MaxElements; H->Size = 0; H->Elements[0] = MinData; }
Insert(插入)
插入之前,我们现在下一个空闲位置插入一个空穴(前面说了,二叉堆是一个完全二叉树);
如果X可以放在该空穴中而不破坏堆序性质,那么就完成插入了,否则,我们把空穴的父节点的元素移入空穴,这样空穴就朝根的方向上移了一步,这也称为上滤,因为按照堆序性质,由根节点开始到每个叶子节点的每条链都是顺序的,当我们插入一个新的X后,在X由空闲位置到目标位置到根节点的那条链上也是顺序的(参考插排),而其他的被向下移动的点,在它未移动的时候,已经是满足性质的,即每个以这个点开始的堆都是一个最小堆(这里设最小值优先),当我们把这个值向下移动后,那个空位被填入相等或更小的值,堆序性得以保留;每次最坏时间是O(logN)
下面是实现代码
void Insert(int X, PriorityQueue H) { int i; for (i = ++H->Size; H->Elements[i / 2] > X; i /= 2) { H->Elements[i] = H->Elements[i / 2]; } H->Elements[i] = X; }
DeleteMin(删除最小元)
删除最小元的时候,就是删除堆的根节点,删除比较容易,但是这样一来,在堆的根节点处就产生了一个空穴,为了满足完全二叉树的特性,我们应该把最后一个元素X移动到堆的某个地方,这样一来就有两种选择:
1. X可以被移入空穴,一般不大可能
2. 将空穴中较小的那个儿子移入空穴,重复步骤直至X能够移入空穴;、
这种方式称为下滤,因为我们是从其中选择较小元移入空穴,并使空穴下移,所以在那个未移动的兄弟,以它为起点的是满足最小堆的要求的。而变成他父节点的那个兄弟而由根到那个变更点的序也是满足要求的。及变更点的儿子也是满足要求的,这样一来,下推过程中,我们只要判断每个点的两个儿子是不是都比X大就可以了,这里将节点均当成有两个儿子,还要注意下滤的范围
int DeleteMin(PriorityQueue H){ int i,Child; int MinElement,LastElement; MinElement=H->Elements[1]; LastElement=H->Elements[H->Size--]; for (i=1;i*2<=H->Size;i=Child){ Child=i*2; if (Child != H->Size&&H->Elements[Child+1]<H->Elements[Child]) Child++; if (LastElement>H->Elements[Child]) H->Elements[i]=H->Elements[Child]; else break; } H->Elements[i]=LastElement; return MinElement; }
BuildHeap(构建堆)
基于以上两种操作,我们可以看看构建堆,这里构建堆有两种方式
1. 通过Insert 操作将N个关键字插入到一个空堆中,时间是O(N);
2. 先将N个关键字以任意顺序放入树中,然后下滤;
这里又有一个定理:包含2^(h+1)-1个节点的理想二叉树的节点高度和为2^(h+1)-(h+1);
void BuildHeap1(int *Array, int ArraySize, PriorityQueue H) { for (int i = 0; i < ArraySize; ++i) { Insert(Array[i], H); } }
void Percolate(int i, PriorityQueue H) { int LastElement = H->Elements[i]; int Child; for (; i * 2 <= H->Size; i = Child) { Child = i * 2; if (Child != H->Size && H->Elements[Child + 1] < H->Elements[Child]) Child++; if (LastElement > H->Elements[Child]) H->Elements[i] = H->Elements[Child]; else break; } H->Elements[i] = LastElement; } void BuildHeap2(PriorityQueue H) { for (int i = H->Size / 2; i > 0; i--) Percolate(i, H); }
剩下这个是基于以上代码的一个排序的例子
#include<iostream>
#include<algorithm>
#include<cstdlib>
#include<cctype>
#include<cstdio>
#include<string>
#include<cstring>
#include<vector>
#include<set>
#include<map>
#include<queue>
#include<cmath>
#define pi acos(-1.0)
#define inf 1<<29
#define INF 0x3f3f3f3f
#define zero 1e-8
const int li[] = { -1, 0, 1, 0};
const int lj[] = {0, -1, 0, 1};
using namespace std;
typedef struct HeapStruct { int Capacity;//总容量 int Size;//目前的大小 int *Elements; } *PriorityQueue;
const int MinData = -inf;//要取一个绝对小的数 来保证能在操作过程中跳出循环
PriorityQueue Initialize(int MaxElements)
{
PriorityQueue H = new HeapStruct;
if (H == NULL) {
cout << "out of space" << endl;
return H;
}
H->Elements = new int[MaxElements + 10];
if (H->Elements == NULL) {
cout << "out of space" << endl;
return H;
}
H->Capacity = MaxElements;
H->Size = 0;
H->Elements[0] = MinData;
return H;
}
void Insert(int X, PriorityQueue H)
{
int i;
for (i = (++H->Size); H->Elements[i / 2] > X; i /= 2) {
H->Elements[i] = H->Elements[i / 2];
}
H->Elements[i] = X;
}
int DeleteMin(PriorityQueue H)
{
int i, Child;
int MinElement, LastElement;
MinElement = H->Elements[1];
LastElement = H->Elements[H->Size--];
for (i = 1; i * 2 <= H->Size; i = Child) {
Child = i * 2;
if (Child != H->Size && H->Elements[Child + 1] < H->Elements[Child])
Child++;
if (LastElement > H->Elements[Child])
H->Elements[i] = H->Elements[Child];
else
break;
}
H->Elements[i] = LastElement;
return MinElement;
}
void BuildHeap1(int *Array, int ArraySize, PriorityQueue H) { for (int i = 0; i < ArraySize; ++i) { Insert(Array[i], H); } }
void Percolate(int i, PriorityQueue H) { int LastElement = H->Elements[i]; int Child; for (; i * 2 <= H->Size; i = Child) { Child = i * 2; if (Child != H->Size && H->Elements[Child + 1] < H->Elements[Child]) Child++; if (LastElement > H->Elements[Child]) H->Elements[i] = H->Elements[Child]; else break; } H->Elements[i] = LastElement; } void BuildHeap2(PriorityQueue H) { for (int i = H->Size / 2; i > 0; i--) Percolate(i, H); }
int arr[10];
int main()
{
PriorityQueue H = Initialize(10);
for (int i = 0; i < 10; ++i)
cin >> arr[i];
BuildHeap1(arr, 10, H);
for (int i = 0; i < 10; ++i)
cout << DeleteMin(H) << " ";
cout << endl << "--------------------------------------------" << endl;
PriorityQueue H2 = Initialize(10);
for (int i = 1; i <= 10; ++i) {
cin >> H2->Elements[i];
H2->Size++;
}
BuildHeap2(H2);
for (int i = 0; i < 10; ++i)
cout << DeleteMin(H2) << " ";
cout << endl;
return 0;
}
相关文章推荐
- android悬浮窗口的实现
- Linux 用户管理之相关命令
- android.view.View.setWillNotDraw(boolean willNotDraw)
- C语言修饰词之violate使用
- 卸载 jenkins 教程
- 警告: No mapping found for HTTP request with URI [] in DispatcherServlet with name
- Linux 用户管理之相关配置文件
- MySQL存储过程的优化实例
- 美容院客户的需求分析模型(REA模型)
- 字符串的组合
- 一道movfuscator混淆过的简单逆向
- js注意事项06
- 是时候适配 Swift 3 了吗——专访 LINE iOS 开发工程师王巍
- 编辑器之神Vi
- Kali Linux 秘籍 第四章 信息收集
- VOD和NVOD
- kernel 源码中的 ACCESS_ONCE()
- python windows系统时间同步
- IOS:UITest单元测试
- 根据IP查询地理信息的工具类