一、堆
1、基本定义
堆是一棵完全二叉树,树中每一个结点的值都不小于(不大于)其左右孩子的值。
大顶堆:父亲结点的值大于等于孩子结点的值。
小顶堆:父亲结点的值小于等于孩子结点的值。
堆一般用于优先队列的实现,而优先队列一般使用大顶堆。
2、堆的操作
(1)堆的表示
const int maxn = 100;
int heap[maxn], n = 100;
(2)向下调整堆
在建立堆的过程中,总是将结点从上往下调整。
调整方法:总是将当前结点V与它的左右孩子比较(如果有的话),假如孩子中存在比结点V大的,就将其中权值最大的那个孩子结点与结点V交换;交换完毕后,继续让结点V与孩子比较,知道结点V的孩子权值都比V小或者是V不存在孩子结点。
调整代码==(时间复杂度是O(logn))== :
//对heap数组在[low, high]范围内进行向下调整
//其中low为欲调整结点的数组下标,high一般为堆的最后一个元素的数组下标
void downAdjust(int low, int high){
int i = low, j = i * 2;
while(j < high){
if(j + 1 < high && heap[j+1] > heap[j]){
j = j + 1;
}
if(heap[j] > heap[i]){
swap(heap[j], heap[i]);
i = j;
j = i * 2;
}else{
break;
}
}
}
(3)建堆
//建堆
void createHeap(){
for(int i = n / 2; i > 0; i--){
downAdjust(i, n);
}
}
(4)删除堆顶元素
思路:用最后一个元素覆盖堆顶元素,并且让总个数-1,最后再向下调整堆顶元素。
//删除堆顶元素
void deleteTop(){
heap[1] = heap[n--];
downAdjust(1, n);
}
(5)插入元素
思路:往数组末尾添加上欲插入的元素,之后向上调整。
向上调整:总是把欲操作结点与父结点进行比较,如果权值比父结点大,那么交换其与父结点,这样子反复比较,直到到达堆顶或者父亲结点的权值较大为止。
//heap数组在[low, high]范围内进行向上调整
//其中low一般设置为1,high为欲调整的数组下标。
void upAdjust(int low, int high){
int i = high, j = i / 2;
while( j >= low ){
if( heap[j] < heap[i]){
swap(heap[j], heap[i]);
i = j;
j = i / 2;
}else{
break;
}
}
}
//添加元素
void insert(int x){
heap[++n] = x;
upAdjust(1, n);
}
3、堆排序
堆排序:利用堆结构对一个序列进行排序,此处讨论递增排序的情况。
堆排序的直接思路:取出堆顶元素,然后将堆的最后一个元素替换至堆顶,在进行一次针对堆顶元素的向下调整——如此重复,直到堆中只剩下一个元素为止。
实现代码:
void heapSort(){
createHeap();
for(int i = n; i > 1; i--){
swap(heap[i], heap[1]);
downAdjust(1, i-1);
}
}
二、哈夫曼树
1、基本定义
叶子结点的路径长度:从根结点出发到达该结点所经过的边数
叶子结点的带权路径长度:把叶子结点的权值乘以其路径长度
树的带权路径长度:所有结点的带权路径长度之和
已知n个数,寻找一棵树,使得树的所有叶子结点的权值恰好为这n个数,并且使得这棵树的带权路径长度最小。带权路径长度最小的树称为哈夫曼树~
对于同一组叶子结点来说,哈夫曼树是不唯一的,但是最小带权路径长度一定是唯一的。
2、算法描述:
1、初始状态有n个结点,结点的权值分别是给定的n个数,将他们视为n棵一个结点的树。
2、合并其中根结点权值最小的两棵树,生成两棵树根结点的父亲结点,权值为这两棵树的权值之和,这样子树的数量就少了一个。
3、重复2、直到只剩下一棵树为止。
一般可以使用优先队列来实现哈夫曼树。
利用优先队列来实现哈夫曼树。
#include <cstdio>
#include <queue>
using namespace std;
priority_queue<long long, vector<long long>, greater<long long >> q;
int main(){
int n;
long long temp, x, y, ans = 0;
scanf("%d", &n);
for(int i = 0; i < n; i++){
scanf("%lld", &temp);
q.push(temp);
}
while(q.size() > 1){
x = q.top();
q.pop();
y = q.top();
q.pop();
q.push(x + y);
ans += x + y;
}
//ans为最小带权路径
printf("%lld\n", ans);
return 0;
}
3、哈夫曼编码
自己看哈~~(doge)