C++用优先队列实现哈夫曼树

个人觉得,教材上实现哈夫曼树地方法太繁琐了,容易劝退。。

先贴代码


const int MAXSIZE = 5;//字符数目
int w[MAXSIZE + 1] = { 0,1,2,3,4,5 }; //保存abcde对应的权重
string s[MAXSIZE+1];//abcde对应的编码
char a[MAXSIZE + 1] = { '#','a','b','c','d','e' };//出现的字符

//节点结构
typedef struct HTNode{
	 int weight; //该节点的权值
	 char c; // 该节点对应的字符,默认为#
	 HTNode* lchild, *rchild; //左右子树
	 bool operator<(const HTNode& a) {
		 return weight < a.weight;
	 }
} * HuffmanTree;

//自定义比较规则
struct cmp {
	bool operator()(const HuffmanTree& a, const HuffmanTree& b) {
		return a->weight > b->weight;
	}
};
//堆,优先队列
priority_queue <HuffmanTree,vector<HuffmanTree>,cmp> q;

//后序遍历哈夫曼树,获取字符编码
void back_for_each(HuffmanTree T, string code) {
	if (T) {
		back_for_each(T->lchild, code + "0");
		back_for_each(T->rchild, code + "1");
		if (T->c != '#') {
			s[(T->c) - 'a' + 1] = code;
		}
	}
}
//构建哈夫曼树
void HufffmanCoding() {
	//初始化节点
	int m = 2 * MAXSIZE - 1;
	HuffmanTree HT = new HTNode[m + 1]; // 不使用零号单元
	HuffmanTree p=HT+1;
	for (int i = 1; i <= MAXSIZE; ++i,++p) {
		*p = { w[i], a[i] , 0, 0 }; //初始化每一个基本节点abcde
		q.push(p);//放入优先队列中
	}	
	//构建哈夫曼树
	while (q.size() > 1) {
		HuffmanTree s1 = q.top();
		q.pop();
		HuffmanTree s2 = q.top();
		q.pop();
		if (s1->weight < s2->weight) {
			HuffmanTree ptr = new HTNode;
			(*ptr) = { s1->weight + s2->weight,'#',s1,s2 };
			q.push(ptr);
		}
		else {
			HuffmanTree ptr = new HTNode;
			(*ptr) = { s1->weight + s2->weight,'#',s1,s2 };
			q.push(ptr);
		}
	}
	HuffmanTree root = q.top();
	//获取字符编码
	back_for_each(root,"");
}
//编码
string encoding(const string &a) {
	string temp="";
	for (auto c : a) {
		temp = temp + s[c - 'a' + 1];
	}
	return temp;
}
//解码
string decoding(const string& a) {
	string temp = "", ans = "";

	for (auto c : a) {
		temp = temp + c;
		int flag = 0;
		int cnt=0;
		for (auto c : s) {
			if (cnt == 0) {
				cnt++;
				continue;
			}
			if (temp == c) {
				ans += char('a' - 1 + cnt);
				flag = 1;
			}
			cnt++;
		}
		if (flag)temp = "";
	}
	return ans;
}
int main() {
	
	HufffmanCoding();
	for (int i = 1; i <= 5; ++i) {
		cout << char('a'+i-1) << "的编码为:" << s[i] << endl;
	}
	cout << "请输入待编码的字符串:";
	string input;
	cin >> input;
	string ans = encoding(input);
	cout <<"编码为:"<< ans << endl;
	string ans2 = decoding(ans);
	cout << "解码为: " << ans2 << endl;
	return 0;
}

手动构建哈夫曼

首先,回想一下我们是如何构建哈夫曼树的:

   1.设初始每一个节点都是一颗树(只有一个节点)

   2.不断地从集合中挑两个权值最小的节点,合成一颗树,放回集合中;直至集合中只存在一个树

   3.取顶部的节点作为根节点

OK,接下来就是将这个过程进行代码实现:D

代码实现

首先定义节点结构

//节点结构
typedef struct HTNode{
	 int weight; //该节点的权值
	 char c; // 该节点对应的字符,默认为#
	 HTNode* lchild, *rchild; //左右子树
	 bool operator<(const HTNode& a) {
		 return weight < a.weight;
	 }
} * HuffmanTree;

然后开始建树

下面是定义一个集合,可以自动排序(优先队列)


//自定义比较规则
struct cmp {
	bool operator()(const HuffmanTree& a, const HuffmanTree& b) {
		return a->weight > b->weight;
	}
};

//堆,优先队列
priority_queue <HuffmanTree,vector<HuffmanTree>,cmp> q;

下面是不断选择、合并的过程

1.初始化节点

首先new出m+1个节点(HTNode)

按顺序给MAXSIZE个初始节点赋值(这里我从1号单元开始赋值,不用0号单元);

放入集合q中。

初始的节点,左右子树都为空

2.构建哈夫曼树

执行以下循环,直至q.size()==1:

  • 1、取集合(优先队列)前两个节点
  • 2、new一个新的节点,令其左右子树分别指向第一位和第二位
  • 3、设置其权值为第一位和第二位的和

令根指针指向集合中的最后一个节点


//构建哈夫曼树
void HufffmanCoding() {
	//初始化节点
	int m = 2 * MAXSIZE - 1;
	HuffmanTree HT = new HTNode[m + 1]; // 不使用零号单元
	HuffmanTree p=HT+1;
	for (int i = 1; i <= MAXSIZE; ++i,++p) {
		*p = { w[i], a[i] , 0, 0 }; //初始化每一个基本节点abcde
		q.push(p);//放入优先队列中
	}	
	//构建哈夫曼树
	while (q.size() > 1) {
		HuffmanTree s1 = q.top();
		q.pop();
		HuffmanTree s2 = q.top();
		q.pop();
		if (s1->weight < s2->weight) {
			HuffmanTree ptr = new HTNode;
			(*ptr) = { s1->weight + s2->weight,'#',s1,s2 };
			q.push(ptr);
		}
		else {
			HuffmanTree ptr = new HTNode;
			(*ptr) = { s1->weight + s2->weight,'#',s1,s2 };
			q.push(ptr);
		}
	}
	HuffmanTree root = q.top();
	//获取字符编码
	back_for_each(root,"");
}

以上,哈夫曼树的构建就完成了。

这里需要指出代码中的几个问题(我自己也没搞清楚):

  1. 在HTNode结构的代码中,重载<运算符是为了帮助优先队列排序,cmp()函数也是帮助优先队列排序,二者应该功能重复了,取一个即可。
  2. 在“构建哈夫曼树-初始化节点”中,我new了m=2*MAXSIZE+1个节点,但是在后面并没有用到。因为后面创建节点时,我又重新new了:D                                                                        故此处可以简化。
  3. 优先队列是真的香:)
  4. back_for_each()是后序遍历获取字符编码,其它的就不讲了。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是用C++实现哈夫曼树的代码示例: ```c++ #include <iostream> #include <queue> #include <vector> using namespace std; // 定义哈夫曼树结点 struct HuffmanNode { int weight; // 权重 char ch; // 字符 HuffmanNode *left, *right; // 左右子树指针 HuffmanNode(int w, char c) : weight(w), ch(c), left(nullptr), right(nullptr) {} }; // 定义比较函数,用于优先队列 struct cmp { bool operator()(HuffmanNode* a, HuffmanNode* b) { return a->weight > b->weight; } }; // 构造哈夫曼树 HuffmanNode* buildHuffmanTree(vector<int>& weights, vector<char>& chars) { priority_queue<HuffmanNode*, vector<HuffmanNode*>, cmp> pq; for (int i = 0; i < weights.size(); i++) { pq.push(new HuffmanNode(weights[i], chars[i])); } while (pq.size() > 1) { HuffmanNode* left = pq.top(); pq.pop(); HuffmanNode* right = pq.top(); pq.pop(); HuffmanNode* parent = new HuffmanNode(left->weight + right->weight, '\0'); parent->left = left; parent->right = right; pq.push(parent); } return pq.top(); } // 遍历哈夫曼树,生成哈夫曼编码 void generateHuffmanCode(HuffmanNode* root, string code, vector<string>& codes) { if (!root) { return; } if (root->ch != '\0') { codes[root->ch] = code; } generateHuffmanCode(root->left, code + "0", codes); generateHuffmanCode(root->right, code + "1", codes); } // 主函数 int main() { vector<int> weights = {5, 9, 12, 13, 16, 45}; vector<char> chars = {'a', 'b', 'c', 'd', 'e', 'f'}; HuffmanNode* root = buildHuffmanTree(weights, chars); vector<string> codes(128); generateHuffmanCode(root, "", codes); for (int i = 0; i < 128; i++) { if (!codes[i].empty()) { cout << (char)i << ": " << codes[i] << endl; } } return 0; } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值