今天着重学习了哈夫曼树,Kruskal算法还填补了昨天的遗憾补齐了优先队列,接下来开始今天的总结。
优先队列(堆):昨天对于优先队列的总结太水了(自己都看不下去),我们从新来一遍,优先队列是一棵特别的完全二叉树,它特别在它的节点的双亲节点比自己的子节点都要大或小,那么根节点就理所当然是整棵树中最大或最小的节点了。谈到优先队列的实现与操作,我个人认为是挺简单的,首先介绍向下调整,向下调整指的是在对一个节点做出更改操作后根据树的性质要向它的下方进行调整以达到让整个优先队列符合性质的状态,我们举个栗子:
我们将堆顶改成23,此时优先队列原本的顺序被打破,我们开始向下调整,首先23去对比他的两个子节点并将其与其中的较小值交换位置
我们重复次过程直到其成为叶子节点或其已经小于它所有的子节点为止
向上调整:当我们要向堆中 直接插入元素时,我们一般将其直接插在堆的末尾位置,这是我们就要向上调整,我们可以通过完全二叉树的性质找到它的父节点与其对比,若父节点大于插入元素便将其与父节点交换位置,重复这个过程直到其父节点不再大于插入元素为止。
附上我手打的优先队列(c++就几句话但我写的c语言的):
#include<stdio.h>
int a[1000000],n,ans,sum,head,tail;
void swap(int x,int y){
int t=a[x];
a[x]=a[y];
a[y]=t;
}
void setdown(int i){
int t,flag=0;
while(i*2<=n&&flag==0){
if(a[i]>a[i*2]){
t=i*2;
}else{
t=i;
}
if(i*2+1<=n){
if(a[i*2+1]<a[t]){
t=i*2+1;
}
}
if(t==i){
flag=1;
}else{
swap(i,t);
i=t;
}
}
}
void setup(int i){
int flag=0;
if(i==1) return ;
while(flag==0&&i!=1){
if(a[i]<a[i/2]){
swap(i,i/2);
}else{
flag=1;
}
i=i/2;
}
}
int shanchu(){
int t=a[1];
a[1]=a[n];
n--;
setdown(1);
return t;
}
int main(){
scanf("%d",&n);
head=1,tail=n;
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
}
for(int i=n/2;i>=1;i--){
setdown(i);
}
while(n>1){
int sum=0;
for(int i=1;i<=2;i++){
int t;
t=shanchu();
ans+=t;
sum+=t;
}
n++,a[n]=sum,setup(n);
}
printf("%d",ans);
return 0;
}
哈夫曼树:这是书上重点介绍的一种数据结构,关于哈夫曼树的定义,咳咳,哈夫曼树又称最优树,是一类带权路径长度最短的树,在实际中有广泛的用途(书上如此说道)。还有几个关键名词如图所示:
哈夫曼树的实现我也觉得是简单的毕竟树上讲的生动形象,我就直接附上我的建树与遍历代码代码:
#include<stdio.h>
struct data{
int fa,lc,rc,w;
};
struct data tree[100000];
int n,s1,s2;
void find(int k){
for(int j=1;j<=2;j++){
int min=99999999,minx;
for(int i=1;i<=k-1;i++){
if(i==s1||tree[i].fa!=0)
continue;
if(tree[i].w<min){
min=tree[i].w;
minx=i;
}
}
if(j==1){
s1=minx;
}
if(j==2){
s2=minx;
}
}
}
void dfs(int x){
printf("%d ",tree[x].w);
if(tree[x].lc==0&&tree[x].rc==0){
return ;
}
if(tree[x].lc!=0) dfs(tree[x].lc);
if(tree[x].rc!=0) dfs(tree[x].rc);
}
int main(){
scanf("%d",&n);
int m=2*n-1;
for(int i=1;i<=m;i++){
tree[i].fa=tree[i].lc=tree[i].rc=tree[i].w=0;
}
for(int i=1;i<=n;i++){
scanf("%d",&tree[i].w);
}
for(int i=n+1;i<=m;i++){
find(i);
tree[s1].fa=i,tree[s2].fa=i;
tree[i].lc=s1,tree[i].rc=s2;
tree[i].w=tree[s1].w+tree[s2].w;
}
dfs(m);
return 0;
}
至于哈夫曼编码,咳咳,我还暂时不知道怎么实现,先按下不表(狗头)。
Kruskal:常用的最小生成树算法有prim和Kruskal算法,今天学习了其中较简单的一种,Kruskal
当然Kruskal的简单建立在你已经学习过并查集的基础上(我已经学过一个多月了),Kruskal是一种贪心建树,思路很简单,每次都选权值最小的那条边,判断加入这条路径是否会形成环,若不会加入并标记两端顶点,若会就放弃再找下一条最小边。怎么判断是否成环,使用并查集对比将要加入的顶点的祖先是否和已加入的顶点相等就行,简单易懂好实现,苦于我的效率不高还没有自己敲过,我想很快我就会实现它。