好久没有写博客了┭┮﹏┭┮这几天感冒了,不过今天更新啦!
一、完全二叉树
要学习堆,首先我们得回顾一下完全二叉树,以下是一棵完全二叉树。
完全二叉树是一种特殊的二叉树结构,它的定义如下:
- 在完全二叉树中,除了最后一层的叶节点可能不满外,其他各层的节点都必须填满。
- 叶节点尽量都集中在树的左侧。
- 如果某个节点的右子树为空,则其左子树也必须为空。
完全二叉树中,从上到下、从左到右依次编号,可以得到节点的编号序列。对于任意编号为n的节点:
- 如果n=1,它是根节点;
- 如果n>1,其父节点编号为n/2(取整);
- 如果2n <= 总节点数,其左子节点编号为2n;
- 如果2n+1 <= 总节点数,其右子节点编号为2n+1。
完全二叉树的特点使得它可以用数组来表示,数组的索引对应了节点的编号,数组中的值对应了节点的值。这种表示方法使得完全二叉树的操作更加高效。
这么多性质是不是非常头疼呢?
但是其实可以综合为图中的两个性质!
不过我们不一定要以1为起始节点,我们当然可以使用0作为根节点编号,此时左子树编号为2*i+1,右子树编号为2*i+2。但是我们会发现,这样如果使用编号去遍历二叉树子树的运算次数是4!但是以1作为根节点编号的二叉树子树的遍历运算次数为3!
所以我们说:以1为根节点编号的完全二叉树更优!
二、堆
现在,我们了解了完全二叉树的基础知识,可以进一步学习堆的知识了!
这边介绍两种最简单的堆:大顶堆和小顶堆。
①大顶堆
它满足以下性质:
- 堆中的任意父节点的值都大于或等于其子节点的值。
- 堆中的最大元素存储在根节点,也就是堆顶。
②小顶堆
它满足以下性质:
- 堆中的任意父节点的值都小于或等于其子节点的值。
- 堆中的最小元素存储在根节点,也就是堆顶。
三、堆的插入与弹出
1、堆的插入
堆的尾部插入调整
我们想在这个大顶堆中插入节点 13 ,如何操作呢?
首先在完全二叉树的末尾加入节点 13 ,如图所示:
我们依次去寻找它的父节点,如果比父节点大则交换,否则不变。
过程如下:
2、堆的弹出
堆的头部弹出调整
先弹出,将最后一个节点补到头部,再进行调整。
调整过程:找到父 左 右 三个节点最大的节点,如果不是父节点,则将父节点与之交换,否则不变。
四、优先队列
优先队列是一种特殊的队列,其中的元素具有优先级。元素的优先级可以是任意类型的,可以是整数、浮点数、字符串等。在优先队列中,元素按照优先级的顺序被访问和删除,而不是按照插入的顺序。
优先队列可以用于解决一些问题,如任务调度、最短路径问题等。在任务调度中,每个任务都有一个优先级,优先级高的任务会被优先执行;在最短路径问题中,每个节点都有一个权重,优先队列可以根据权重的大小来选择下一个要访问的节点。
代码实现:
#include<iostream>
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
#define ROOT 1
#define FATHER(i) ((i)/2)
#define LEFT(i) ((i)*2)
#define RIGHT(i) ((i)*2+1)
#define cmp >
#define swap(a,b){ \
__typeof(a) __c = (a); \
(a) = (b); \
(b) = __c; \
}
using namespace std;
typedef struct PriorityQueue{
int *__data,*data;//连续的一段数组空间
int size;//存储空间的大小
int n;//当前存储元素数
}PriorityQueue;
PriorityQueue *initPQ(int size){
PriorityQueue *p = new PriorityQueue;
p->__data = new int[size];
p->data = p->__data - 1;
p->size = size;
p->n = 0;
return p;
}
int empty(PriorityQueue *p){
return p->n == 0;
}
int full(PriorityQueue *p){
return p->n == p->size;
}
int top(PriorityQueue *p){
return p->data[ROOT];
}
void up_update(int *data,int i){
if(i == ROOT) return;
if(data[i] cmp data[FATHER(i)]){
swap(data[i],data[FATHER(i)]);
up_update(data,FATHER(i));
}
}
void down_update(int *data,int i,int n){
int ind;
while(LEFT(i) <= n){
ind = i;
int l = LEFT(i);
int r = RIGHT(i);
if (data[l] cmp data[ind]) ind = l;
if (r <= n && data[r] cmp data[ind]) ind = r;
if (ind == i) break;
swap(data[i],data[ind]);
i = ind;
}
return;
}
int push(PriorityQueue *p, int x){
if(full(p)) return 0;
p->n += 1;
p->data[p->n] = x;
up_update(p->data,p->n);//进行向上调整
return 1;
}
int pop(PriorityQueue *p){
if(empty(p)) return 0;
p->data[ROOT] = p->data[p->n];
p->n -= 1;
down_update(p->data,ROOT,p->n);//进行
return 1;
}
void clearPQ(PriorityQueue *p){
if(p == NULL) return;
delete(p->__data);
delete(p);
return;
}
void output(PriorityQueue *p){
printf("PQ(%d) :",p->n);
for(int i = 1;i <= p->n;i++){
printf("%d ",p->data[i]);
}
printf("\n");
return;
}
int main(){
int op,x;
#define MAX_OP 100
PriorityQueue *p = initPQ(MAX_OP);
while (~scanf("%d",&op)){
if(op == 1){
scanf("%d",&x);
printf("insert %d to priority_queue: \n",x);
push(p,x);
output(p);
}else {
printf("top : %d\n",top(p));
pop(p);
output(p);
}
}
return 0;
}