是时候来写(水)今天的总结了,首先来填补昨天的遗憾,哈夫曼编码的实现,应老覃的要求我基本都是c语言手敲的代码,老规矩先讲思路。哈夫曼编码的来源是依次以叶子节点为出发点,向上回溯至根节点为止。回溯时走左分支则生成代码0(1),走右分支则生成代码1(0)。看下图:
举个栗子,权值为2的哈夫曼编码是010,3的是110,实现也非常简单,从叶子节点开始递归搜索到根节点就行,附上完整的哈夫曼树操作代码(建树编码):
#include<stdio.h>
struct data{
int fa,lc,rc,w;
};
struct data tree[100000];
int n,s1,s2,code[100][100];
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);
}
void hfmcode(int x){
int p,c,i=1;
p=tree[x].fa;
c=x;
while(p!=0){
if(tree[p].lc==c){
code[x][i]=0;
}
if(tree[p].rc==c){
code[x][i]=1;
}
c=p;
p=tree[p].fa;
i++;
}
code[x][i]=-1;
}
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);
printf("\n");
for(int i=1;i<=n;i++){
hfmcode(i);
printf("%d: ",tree[i].w);
for(int j=1;;j++){
if(code[i][j]==-1){
break;
}
printf("%d ",code[i][j]);
}
printf("\n");
}
return 0;
}
验证一下:
还有一个坑,单调队列的实现,我觉得单调队列很值得去学习,可以用来处理滑动窗口又能用来优化dp(dp是个大坑),我写的是模板题:滑动窗口 /【模板】单调队列 - 洛谷,以下是我的AC代码:
#include<stdio.h>
long long a[1000005],n,q[1000005],vis[1000005],k;
int head=1,tail=0;
int main(){
scanf("%lld%lld",&n,&k);
for(int i=1;i<=n;i++){
scanf("%lld",&a[i]);
}
for(int i=1;i<=n;i++){
vis[i]=a[i];
if(tail>=head&&q[head]<=i-k){
head++;
}
while(tail>=head&&vis[i]<vis[q[tail]]){
tail--;
}
q[++tail]=i;
if(i>=k){
printf("%d ",vis[q[head]]);
}
}
head=1,tail=0;
printf("\n");
for(int i=1;i<=n;i++){
if(tail>=head&&q[head]<=i-k){
head++;
}
while(tail>=head&&vis[i]>vis[q[tail]]){
tail--;
}
q[++tail]=i;
if(i>=k){
printf("%d ",vis[q[head]]);
}
}
return 0;
}
最小生成树(prim ):两个最小生成树的代码实现我都还在努力,今天先总结prim 算法的思路。
给我们一张图:
kruskal算法是选边而prim是选择点,我们从起点(假设是1)开始,我们把1打上标记代表他已经在最小数中了。
很显然按照贪心的思想我们肯定是找从1出发走最少路径能走到的点,很明显是点2,我们将其标记纳入树中(后宫)。
下一步就是找离1和2最近的点,是3,将其打上标记。
在重复以上过程直到所有点都被打上标记,我们得到最小生成树:
谢谢潘小蓝大佬的图片。