• 哈夫曼


    ​ 说起哈夫曼树及其思想,最经典也是最早接触的当属合并果子了。哈夫曼反映的经典贪心思想与其数学逻辑在初学者中独占一席,虽然其思想简单易懂,但其代码实现起来并不简单易懂,网上的代码大多都是用指针、内定义函数、地址符等来实现的,像我这种蒟蒻总是容易看得一头雾水,十分的不友好,考虑到哈夫曼树通常是静态的,所以我就敲出来了稍微易于理解一点的静态实现代码。

    以下以哈夫曼编码为例

    题目链接

    上代码:
    #include <bits/stdc++.h>
    using namespace std;
    int n,cnt;
    struct haf{//哈夫曼树的每个节点
    	int f;//指向父亲
    	int num;//权值
    	int xh;//序号(在tr数组中的下标)
    	int l,r;//左右孩子
    	string ans;//用于存储哈夫曼编码
    	inline bool operator < (const haf &b)const{//重载运算符,以便使用优先队列
    		if(num!=b.num)return num>=b.num;
    		return ch>b.ch;
    	}
    }tr[10005];
    void dfs(int p,string now){//深搜遍历得出编码,p为当前节点,now为以记录了的编码
    	if(tr[p].l==-1&&tr[p].r==-1){
    		tr[p].ans=now;
    		return ;
    	}
    	dfs(tr[p].l,now+"0");
    	dfs(tr[p].r,now+"1");
    }
    int main() {
    	while(cin>>n){
    		priority_queue <haf> q;
    		cnt=n;
    		for(int i=1;i<=n;i++){
    			cin>>tr[i].ch>>tr[i].num;
    			tr[i].xh=i;
    			tr[i].l=tr[i].r=tr[i].f=-1;//f,l,r,全部初始化为-1
    			q.push(tr[i]);
    		}
    		for(int i=1;i<n;i++){
    			haf x=q.top();
    			q.pop();
    			haf y=q.top();
    			q.pop();//这里满足:y.num>x.num
    			tr[++cnt].num=x.num+y.num;
    			tr[cnt].xh=cnt;
    			if(x.num==y.num&&x.ch>y.ch)swap(x,y);//题目要求:x.ch<=y.ch
    			tr[cnt].r=y.xh;
    			tr[cnt].l=x.xh;
    			tr[cnt].ch=y.ch;
    			tr[y.xh].f=cnt;
    			tr[x.xh].f=cnt;
    			tr[cnt].f=-1;
    			q.push(tr[cnt]);//q里的元素为从大到小
    			/*
    			q2=q;
    			while(q2.size()){
    				cout<<q2.top().num<<' ';
    				q2.pop();
    			}
    			cout<<endl;
    			*/
                //上面的代码是可视化模拟哈夫曼合并过程功能的代码段
    		}
            /*
    		for(int i=1;i<=cnt;i++)cout<<"序号:"<<i<<" ch:"<<tr[i].ch<<" num:"<<tr[i].num<<" l:"<<tr[i].l<<" r:"<<tr[i].r<<" f:"<<tr[i].f<<endl;
    		*/
            //上面一段是检查哈夫曼树各个节点的情况的功能的代码段
    		dfs(cnt,""); 
    		for(int i=1;i<=n;i++){
    			if(i!=1)cout<<endl;
    			cout<<tr[i].ch<<":"<<tr[i].ans;
    		}
    		cout<<endl;
    	}
    	return 0;
    }
    
    补充:

    ​ 静态化的哈夫曼这里用到了优先队列来选择权值最小的两个结点,优点是代码短,但要重载运算符,还要去学priority_queue。还有种方法是用小根堆,优点是复杂度比优先队列忧、不需要拓展其它知识(除非你没学过堆),但这样的话代码又要多一倍,而且实现起来十分麻烦。

    ​ 代码中的dfs函数(也就是求哈夫曼编码的函数)使用了自顶向下的方法遍历,得到编码的遍历方法也可以是自底向上,两种方法各有优劣(想想具体是哪些优劣?),根据实际情况而定。

  • 相关阅读:
    【JQuery】JQuery属性
    C语言封装的环形队列
    C语言封装的栈结构
    再谈C语言宏定义
    url编码与解码(C语言)
    鼠标钩子安装,实现获取鼠标的全局点击位置
    分享一个MFC的内存DC封装类
    MDK5.30中出现警告 xxx.c(x): warning: no previous extern declaration for non-static variable 'in' [-Wmissing-variable-declarations]
    MFC中加载位图
    分享一个自己封装的WIN SOCKET类
  • 原文地址:https://www.cnblogs.com/returnG/p/13144418.html
Copyright © 2020-2023  润新知