除了最后一层,其余层都是满二叉树;最后一层只能是右侧缺失
1.编号为i的子结点:左孩子编号2i;右孩子编号2 * i + 1(从1开始编号) ***[方便]
编号为i的子结点:左孩子编号2i + 1;右孩子编号2 * i + 2(从0开始编号)
2.可用连续空间存储(数组),由于编号连续[通过计算得到孩子结点的编号,节省存储空间];
堆
大顶堆:二叉树中每三个单元看作一个元素,父结点的值永远大于等于子结点的值(根结点值最大,次结点值一定是左右孩子之一)
小顶堆:二叉树中每三个单元看作一个元素,父结点的值永远小于等于子结点的值
优先队列
由于在数组的0号位置存的是最大值(最小值),删除的时候0号位出队列
在堆中插入一个值时需要插进数组的末尾,所以时入队列
符合队列先入先出的性质同时又要满足堆的性质,故优先队列
堆排序
由小到大排列:大顶堆,对数组操作n-1次
(1)将堆顶元素与堆尾元素交换
(2)将此操作看作是堆顶元素弹出操作
(3)按照头部弹出以后的策略调整堆
深度与堆中元素关系为log(n),由于二叉树的深度是有保障的
堆排序的性能稳定在nlog(n) 普通队列 优先队列
尾部入队 | 尾部可以插入 |
头部出队 | 头部可以弹出 |
先进先出 | 每次出队权值最大/(最小)的一个元素 |
数组实现 | 数组实现,逻辑上看成一个堆 |
#include <stdio.h>
#include <stdlib.h>
#define swap(a, b){ \
__typeof(a) __temp = a; \
a = b; b = __temp; \
}
typedef struct PriorityQueue{
int *data;
int n, size;
}priorityQueue;
priorityQueue *init(int n){
priorityQueue *p = (priorityQueue *)malloc(sizeof(priorityQueue) * 1);
p->data = (int *)malloc(sizeof(int) * (n + 1));//从1开始编号,所开辟出的空间就要多一位;
p->n = 0;
p->size = n + 1;
return p;
}
int push(priorityQueue *p, int val){
if(p->n + 1 >= p->size){
return 0;
}
p->n += 1;
p->data[p->n] = val;
int ind = p->n;
while(ind > 1 && p->data[ind / 2] < p->data[ind]){
swap(p->data[ind / 2], p->data[ind]);
ind = ind / 2;//指向交换完成后的结点,也就是父结点;
}
return 1;
}
int empty(priorityQueue *p){
return p->n == 0;
}
int front(priorityQueue *p){
return p->data[1];
}
int pop(priorityQueue *p){
if(empty(p)){
return 0;
}
p->data[1] = p->data[p->n];
p->n -= 1;
int ind = 1;
while(ind * 2 <= p->n){
int ind1 = ind * 2;
int ind2 = ind * 2 + 1;
int max_ind = ind;//是否可以跳出了,当不需要再调整的时候可以退出;三个结点中下标最大的孩子结点的位置
if(p->data[ind1] > p->data[max_ind]) max_ind = ind1;
if(ind2 <= p->n && p->data[ind2] > p->data[max_ind]) max_ind = ind2;
if(max_ind == ind) break;//当前根结点已经是最大的了,不需要调整;
swap(p->data[ind], p->data[max_ind]);
ind = max_ind;
}
return 1;
}
void clear(priorityQueue *p){
if(p == NULL) return ;
free(p->data);
free(p);
return ;
}
int main(){
int n, a;
scanf("%d", &n);
priorityQueue *p = init(n);
for(int i = 0; i < n; i++){
scanf("%d", &a);
printf("push to queue : %d\n", a);
push(p, a);
printf("front queue : %d\n", front(p));
}
while(!empty(p)){
printf("%d ", front(p));
pop(p);
}
printf("\n");
return 0;
}
push to queue : 6
front queue : 6
push to queue : 5
front queue : 6
push to queue : 4
front queue : 6
push to queue : 8
front queue : 8
push to queue : 9
front queue : 9
push to queue : 10
front queue : 10
push to queue : 3
front queue : 10
push to queue : 2
front queue : 10
10 9 8 6 5 4 3 2