前言:
在前面学习了许多种排序之后,相信大家都已经学腻了排序,心里都会发牢骚说算法难道就只有排序吗,今天我们就不学习算法了,我们开始学习新的数据结构——二叉堆。
二叉堆:
二叉堆就是二叉树,完全的二叉树中每一个父节点下有两个子节点。今天我们以max_heap为例,就是传说中的最大堆,即每一个父节点都要比自己的两个子节点大,根节点最大,它大于整个二叉堆中的所有节点值。
我们用一个vector来表示二叉堆。原因呢,一是c++中的vector可以不断变化大小,来满足二叉堆的不断变化;二是用vector能够很好的表示二叉堆。舍去第一个元素,从1开始数,更加好理解,1。。。2,3。。。4,5,6,7.。。。。即k节点的子节点是2k与2k+1。
来看看二叉堆的一些操作:
swim操作:
当子节点大于父节点时,这自然是不符合二叉堆规则的,于是有swim操作来比较子节点与父节点的大小,若子节点大,则跟换子节点与父节点的值,不断更换直到根节点。
sink操作:
当父节点小于子节点时,这也是不符合二叉堆规则的,于是有sink操作来比较子节点与父节点的大小,若子节点大,则跟换子节点与父节点的值,不断更换到节点末端为止。
insert操作:
向二叉堆中插入元素就是先在二叉堆末端插入元素,然后利用swim操作来调整二叉堆,使插入的元素到合适的位置。
delmax操作:
delmax操作就是删去整个二叉堆中的最大值,即删去根节点。然而删去根节点之后我们若是往上填补的话操作很复杂,所以我们将根节点与二叉树最末端的节点交换,然后删去末节点,在使用sink操作来重新调整二叉堆中数字的位置。
来看看c++ 代码:
class binary_heap{
public:
vector<int> pq;
int N;
binary_heap(int a[], int N = 0){
int i;
pq.push_back(-1);
for(i = 0; i < N; i++){
pq.push_back(a[i]);
}
}
bool isEmpty(){
return N == 0;
}
void insert(int x){
N = N + 1;
pq.push_back(x);
swim(N);
}
int delMax(){
int max;
max = pq[1];
exch(1, N);
N = N - 1;
pq.pop_back();
sink(1);
return max;
}
void swim(int k){
while(k > 1 && less(k / 2, k)){
exch(k, k / 2);
k = k / 2;
}
}
void sink(int k){
while(2 * k <= N){
int j = 2 * k;
if(j < N && less(j, j + 1)){
j = j + 1;
}
if(!less(k, j)){
break;
}
exch(k, j);
k = j;
}
}
bool less(int i, int j){
if(pq[i] < pq[j]){
return true;
}
else{
return false;
}
}
void exch(int i, int j){
int temp;
temp = pq[i];
pq[i] = pq[j];
pq[j] = temp;
}
};
总结:
随着堆的不断深入研究,发现了许多种堆,下面来看看这不同种堆的各种操作的复杂度:
(图片来自 Wikipedia,版权归其所有)
最后强烈推荐Coursera上普林斯顿大学的算法课点击打开链接
以上内容纯属个人学习总结,不代表任何团体或单位。若有理解不到之处请见谅!