哈夫曼树C++实现

哈夫曼树

给定一组具有确定权值的叶子结点,可以造出不同的二叉树,将其中带权路径长度最小的二叉树称为哈夫曼树(Huffman tree)。

哈夫曼节点类

哈夫曼节点会存储节点的权重以及,ch是节点对应的编码字符的下标,这里需要重载“<”,因为我们要使用最小优先级队列。

//哈夫曼树节点类
class hfTNode {
public:
	int weight;//节点的权重
	int ch;//存储节点的下标
	hfTNode* leftchild, * rightchild;//创建该结点的左右孩子指针
	hfTNode() ://哈夫曼树节点类初始化
		leftchild(nullptr), rightchild(nullptr) {}
	hfTNode(int data) ://哈夫曼树节点类初始化
		leftchild(nullptr), rightchild(nullptr), weight(data) {}
	//拷贝构造函数
	hfTNode(const hfTNode& N) {
		ch = N.ch;
		weight = N.weight;
		if (N.leftchild) {
			leftchild = new hfTNode();
			*leftchild = *(N.leftchild);
		}
		else
			leftchild = nullptr;
		if (N.rightchild) {
			rightchild = new hfTNode();
			*rightchild = *(N.rightchild);
		}
		else
			rightchild = nullptr;
	}
	// 重载"<"符号,令其为最小值优先
	bool operator < (const hfTNode& N) const{
		return weight > N.weight;
	}
};

哈夫曼树类

哈夫曼树类实现了树的构建,编码表的实现,压缩和解压缩文本功能

//哈夫曼树类
class Huffman
{
public:
	Huffman(const string &sample);// 构造函数
	~Huffman();// 析构函数

	bool IsLeaf(hfTNode* Root);//判断节点是否为叶子结点
	void GetFreq(vector<int> &des);//获取当前的权重数组
	void BuildTree();//构建一颗哈夫曼树
	void BuildCode();//根据哈夫曼树构建编码表
	void GetCodeList();//遍历编码表和编码表对应的编码

	//前序遍历和中序遍历是为了确定哈夫曼树的形状是否正确
	void PreOrder();
	void InOrder();

	string Expend(const string &des);//解压缩
	string Compress(const string &des);//压缩
	

private:
	void _del(hfTNode* root);//删除哈夫曼树
	void _build(hfTNode* root, string str);//生成map的key与value值
	void _PreOrder(hfTNode* root);
	void _InOrder(hfTNode* root);
	unordered_map<char, string> map;//key为编码表的字符,value为编码表的字符对应的编码形式例如“101”,“111”
	vector<int> freq; // 权重数组
	vector<char> st; // 编码表
	
	hfTNode* Root; // 哈夫曼树根节点
};

哈夫曼树的实现

简单来说,就是将节点压入最小优先级队列中(这里也可以使用最小堆完成,不过我上一节编写的最小堆没有写成模板类,所以就懒一点,直接用STL里面的优先级队列了),然后每次取出最小的两个节点,将这两个节点作为左孩子节点和右孩子节点,将这两个节点的权重再合并成一个节点,那么这个新的节点就是该子树的根结点,再将这个紫薯的根结点压入最小优先级队列中,重复这个过程,直至生成一棵树。

//构建一颗哈夫曼树
void Huffman::BuildTree()
{
	priority_queue<hfTNode> myqueue;//使用最小优先级队列存储节点
	for (int i = 0; i < freq.size(); i++){//节点初始化,并将节点压入最小优先级队列中
		hfTNode* temp = new hfTNode(freq[i]);
		temp->ch = i;
		myqueue.push(*temp);
	}
	//合并节点并生成树
	while (myqueue.size() > 1) {
		//从队列中取出两个最小元素作为新子树的左孩子和右孩子
		hfTNode left = myqueue.top();
		myqueue.pop();
		hfTNode right = myqueue.top();
		myqueue.pop();
		//创建新子树的根节点,并将其压入队列中
		hfTNode* parent = new hfTNode(left.weight + right.weight);
		parent->ch = -1;//设定子树生成的根结点下标值是-1,只做区分用
		parent->leftchild = &left;
		parent->rightchild = &right;
		myqueue.push(*parent);
	}
	//生成哈夫曼树的根结点
	Root = new hfTNode();
	*Root = myqueue.top();
	myqueue.pop();
}

依据哈夫曼树生成存储编码表的map

没啥好说的,就是递归,map的key是文本符号如‘A’,‘B’,value是文本需要压缩成的二进制字符串,如‘11’,‘110’

//根据哈夫曼树构建编码表
void Huffman::BuildCode()
{
	if (Root == nullptr) return;
	//string temp('\0');
	string temp;
	temp.clear();//初始化为空字符串
	_build(Root, temp);
}

void Huffman::_build(hfTNode* root, string str)
{
	if (IsLeaf(root) && root->ch >= 0) {
		cout << str << "    ";
		map[st[root->ch]] = str;
		return;
	}
	if(root->leftchild) _build(root->leftchild, str + '0');
	if(root->rightchild) _build(root->rightchild, str + '1');
}

压缩

//压缩
string Huffman::Compress(const string& des)
{
	string res;
	for (int i = 0; i < des.length(); ++i) {
		if (des[i] == '\n'||des[i]==' ')
			continue;
		res += map[des[i]];
	}	
	return res;
}

解压缩

//解压缩
string Huffman::Expend(const string& des)
{
	string res;
	int i(0), n(des.size());
	hfTNode* temp = new hfTNode();
	temp = Root;
	while (i < n) {	
		if (des[i]=='0')
		{
			temp = temp->leftchild;
			i++;
			if (IsLeaf(temp)) {
				res += st[temp->ch];
				temp = Root;
				continue;
			}
		}
		if (des[i] == '1')
		{
			temp = temp->rightchild;
			i++;
			if (IsLeaf(temp)) {
				res += st[temp->ch];
				temp = Root;
				continue;
			}
		}
	}
	return res;
}

全部代码

huffmanTree.h

#pragma once
#ifndef HUFFMANTREE_H
#define HUFFMANTREE_H
#include<iostream>
#include<vector>
#include<unordered_map>
#include<queue>
using namespace std;

//哈夫曼树节点类
class hfTNode {
public:
	int weight;//节点的权重
	int ch;//存储节点的下标
	hfTNode* leftchild, * rightchild;//创建该结点的左右孩子指针
	hfTNode() ://哈夫曼树节点类初始化
		leftchild(nullptr), rightchild(nullptr) {}
	hfTNode(int data) ://哈夫曼树节点类初始化
		leftchild(nullptr), rightchild(nullptr), weight(data) {}
	//拷贝构造函数
	hfTNode(const hfTNode& N) {
		ch = N.ch;
		weight = N.weight;
		if (N.leftchild) {
			leftchild = new hfTNode();
			*leftchild = *(N.leftchild);
		}
		else
			leftchild = nullptr;
		if (N.rightchild) {
			rightchild = new hfTNode();
			*rightchild = *(N.rightchild);
		}
		else
			rightchild = nullptr;
	}
	// 重载"<"符号,令其为最小值优先
	bool operator < (const hfTNode& N) const{
		return weight > N.weight;
	}
};

//哈夫曼树类
class Huffman
{
public:
	Huffman(const string &sample);// 构造函数
	~Huffman();// 析构函数

	bool IsLeaf(hfTNode* Root);//判断节点是否为叶子结点
	void GetFreq(vector<int> &des);//获取当前的权重数组
	void BuildTree();//构建一颗哈夫曼树
	void BuildCode();//根据哈夫曼树构建编码表
	void GetCodeList();//遍历编码表和编码表对应的编码

	//前序遍历和中序遍历是为了确定哈夫曼树的形状是否正确
	void PreOrder();
	void InOrder();

	string Expend(const string &des);//解压缩
	string Compress(const string &des);//压缩
	

private:
	void _del(hfTNode* root);//删除哈夫曼树
	void _build(hfTNode* root, string str);//生成map的key与value值
	void _PreOrder(hfTNode* root);
	void _InOrder(hfTNode* root);
	unordered_map<char, string> map;//key为编码表的字符,value为编码表的字符对应的编码形式例如“101”,“111”
	vector<int> freq; // 权重数组
	vector<char> st; // 编码表
	
	hfTNode* Root; // 哈夫曼树根节点
};




#endif // !HUFFMANTREE_H
#include "huffmanTree.h"

//依据字符串生成编码表以及权重数组
Huffman::Huffman(const string &sample)
{
	int len = sample.length();
	if (len == 0) { cout << "请重新输入字符串" << endl; exit(-1); }
	unordered_map<char, int> mymap;
	for (int i = 0; i < len; ++i) {//依据输入字符串初始化哈希表
		if (mymap.find(sample[i]) == mymap.end())
			mymap[sample[i]] = 1;
		else
			mymap[sample[i]] += 1;
	}
	for (const auto& pair : mymap) {//遍历哈希表,并将key与value分别压入编码表与频率数组中
		//unordered_map<char, int>::const_iterator pair
		st.push_back(pair.first);
		freq.push_back(pair.second);
	}
}

//析构函数
Huffman::~Huffman()
{
	_del(Root);
}
//判断节点是否为叶子结点
bool Huffman::IsLeaf(hfTNode* Root)
{
	if(Root==nullptr) return false;
	if (Root->leftchild == nullptr && Root->rightchild == nullptr) 
		return true;
	else 
		return false;
}

//获取当前的权重数组
void Huffman::GetFreq(vector<int> &des)
{
	for (int i = 0; i < freq.size(); i++)
		des.push_back(freq[i]);
}

//构建一颗哈夫曼树
void Huffman::BuildTree()
{
	priority_queue<hfTNode> myqueue;//使用最小优先级队列存储节点
	for (int i = 0; i < freq.size(); i++){//节点初始化,并将节点压入最小优先级队列中
		hfTNode* temp = new hfTNode(freq[i]);
		temp->ch = i;
		myqueue.push(*temp);
	}
	//合并节点并生成树
	while (myqueue.size() > 1) {
		//从队列中取出两个最小元素作为新子树的左孩子和右孩子
		hfTNode left = myqueue.top();
		myqueue.pop();
		hfTNode right = myqueue.top();
		myqueue.pop();
		//创建新子树的根节点,并将其压入队列中
		hfTNode* parent = new hfTNode(left.weight + right.weight);
		parent->ch = -1;//设定子树生成的根结点下标值是-1,只做区分用
		parent->leftchild = &left;
		parent->rightchild = &right;
		myqueue.push(*parent);
	}
	//生成哈夫曼树的根结点
	Root = new hfTNode();
	*Root = myqueue.top();
	myqueue.pop();
}

//根据哈夫曼树构建编码表
void Huffman::BuildCode()
{
	if (Root == nullptr) return;
	//string temp('\0');
	string temp;
	temp.clear();//初始化为空字符串
	_build(Root, temp);
}

//遍历编码表和编码表对应的编码
void Huffman::GetCodeList()
{
	for (const auto& pair : map) 
		cout << pair.first << ": " << pair.second << endl;
}

void Huffman::PreOrder()
{
	if (Root == nullptr) return;
	_PreOrder(Root);
}

void Huffman::InOrder()
{
	if (Root == nullptr) return;
	_InOrder(Root);
}

//解压缩
string Huffman::Expend(const string& des)
{
	string res;
	int i(0), n(des.size());
	hfTNode* temp = new hfTNode();
	temp = Root;
	while (i < n) {	
		if (des[i]=='0')
		{
			temp = temp->leftchild;
			i++;
			if (IsLeaf(temp)) {
				res += st[temp->ch];
				temp = Root;
				continue;
			}
		}
		if (des[i] == '1')
		{
			temp = temp->rightchild;
			i++;
			if (IsLeaf(temp)) {
				res += st[temp->ch];
				temp = Root;
				continue;
			}
		}
	}
	return res;
}

//压缩
string Huffman::Compress(const string& des)
{
	string res;
	for (int i = 0; i < des.length(); ++i) {
		if (des[i] == '\n'||des[i]==' ')
			continue;
		res += map[des[i]];
	}	
	return res;
}

void Huffman::_PreOrder(hfTNode* root)
{
	if (root == nullptr) return;
	cout << root->ch << " :" << root->weight << endl;
	_PreOrder(root->leftchild);
	_PreOrder(root->rightchild);
}


void Huffman::_InOrder(hfTNode* root)
{
	if (root == nullptr) return;
	_InOrder(root->leftchild);
	cout << root->ch << " :" << root->weight << endl;
	_InOrder(root->rightchild);
}

//删除哈夫曼树
void Huffman::_del(hfTNode* root)
{
	if (root == nullptr) 
		return;
	if (root->leftchild) 
		_del(root->leftchild);
	if (root->rightchild) 
		_del(root->rightchild);
}

void Huffman::_build(hfTNode* root, string str)
{
	if (IsLeaf(root) && root->ch >= 0) {
		cout << str << "    ";
		map[st[root->ch]] = str;
		return;
	}
	if(root->leftchild) _build(root->leftchild, str + '0');
	if(root->rightchild) _build(root->rightchild, str + '1');
}

测试代码:

// main.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//

#include"huffmanTree.h"

int main() {
	string sample = "AAAAACCCCCCBBBBDDDFFE";
	Huffman hf(sample);
	vector<int> des;
	hf.GetFreq(des);
	for (int i = 0; i < des.size(); ++i) {
		cout << des[i] << " ";
	}
	hf.BuildTree();
	cout << endl;
	hf.PreOrder();
	cout << endl << endl;
	hf.InOrder();
	hf.BuildCode();
	cout << endl << endl;
	hf.GetCodeList();

	cout << endl << endl;
	string res=hf.Compress(sample);
	cout << hf.Expend(res);
	return 1;
}

测试结果

  • 11
    点赞
  • 49
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
构造哈夫曼树是一种常用的数据压缩算法,下面是C++实现哈夫曼树的简单介绍: 1. 定义哈夫曼树的节点结构体,包括字符、权值、左右子节点等信息。 2. 定义一个哈夫曼树的节点比较器,用于按照权值从小到大排序。 3. 构造哈夫曼树时,首先将所有的字符和权值存储到一个vector中,并按照权值从小到大排序。 4. 从vector中取出前两个节点,将它们合并成一个新节点,并将新节点的权值设置为前两个节点的权值之和。 5. 将新节点插入到vector中,并按照权值从小到大排序。 6. 重复步骤4和5,直到vector中只剩下一个节点,此时就构造完成了哈夫曼树。 下面是一个C++实现哈夫曼树的例子: ``` #include <iostream> #include <vector> #include <algorithm> using namespace std; struct HuffmanNode { char c; int weight; HuffmanNode* left; HuffmanNode* right; HuffmanNode(char _c, int _weight) : c(_c), weight(_weight), left(nullptr), right(nullptr) {} }; struct Compare { bool operator()(HuffmanNode* a, HuffmanNode* b) { return a->weight > b->weight; } }; void buildHuffmanTree(vector<HuffmanNode*>& nodes) { while (nodes.size() > 1) { sort(nodes.begin(), nodes.end(), Compare()); HuffmanNode* left = nodes; HuffmanNode* right = nodes; nodes.erase(nodes.begin(), nodes.begin() + 2); HuffmanNode* parent = new HuffmanNode('\0', left->weight + right->weight); parent->left = left; parent->right = right; nodes.push_back(parent); } } int main() { string input = "hello world"; vector<int> weights(256, 0); for (char c : input) { weights[c]++; } vector<HuffmanNode*> nodes; for (int i = 0; i < 256; i++) { if (weights[i] > 0) { nodes.push_back(new HuffmanNode((char)i, weights[i])); } } buildHuffmanTree(nodes); return 0; } ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值