二叉链表实现哈夫曼树

#include <iostream>
#include <queue>
#include <string>
using namespace std;
const int N = 256;
struct Node {
	Node* left;
	Node* right;
	int weight;
	char letter;
};
struct HuffCode {
	char letter;
	string code;
};
class MinHeap {
public:
	int size;
	Node* tree;
	MinHeap() { 
		size = 0;
		tree = new Node[N];
		for (int i = 0; i < N; i++)
		{
			tree[i].left = NULL;
			tree[i].right = NULL;
		}
	};
	void Insert(Node *x);
	void BuildHeap(HuffCode *CodeTable,int arr[],int len);
	Node* Delete();
	~MinHeap() {
		delete[]tree;
	}
};
void MinHeap::Insert(Node *x)
{
	if (size == 0)
	{
		tree[0] = (*x);
		size++;
	}
	else {
		size++;
		tree[size - 1] = (*x);
		int j = x->weight;
		int i = size - 1;
		while (i > 0 && j <= tree[(i+1) / 2 - 1].weight)
		{
			tree[i] = tree[(i + 1) / 2 - 1];
			i = (i + 1) / 2 - 1;
		}
		tree[i] = (*x);
	}

}
void MinHeap::BuildHeap(HuffCode* CodeTable,int arr[],int len)
{
	int k = 0;
	Node* temp=new Node;
	for (int i = 0; i < len; i++)
	{
		if (arr[i])
		{
			temp->letter = char(i);
			temp->weight = arr[i];
			temp->left = NULL;
			temp->right = NULL;
			CodeTable[k++].letter = temp->letter;
			Insert(temp);
			
		}
	}
}
Node* MinHeap:: Delete() {
	Node* T = new Node;
	T->left = tree[0].left;
	T->right = tree[0].right;
	T->weight = tree[0].weight;
	T->letter = tree[0].letter;

	tree[0] = tree[--size];

	tree[size + 1] = tree[0];//因为size+1不会被访问到,为了节省空间不妨就用这块空间

	int child, parent;
	int y;
	for (parent=0; 2 * (parent + 1) - 1 < size;)
	{
		child = 2 * (parent + 1) - 1;
		y = tree[child].weight;

		if (child + 1 < size && tree[child].weight > tree[child + 1].weight)
			y = tree[++child].weight;
		
		if (y >= tree[parent].weight)
			break;
		else {
			tree[parent] = tree[child];
			parent = child;
		}

	}
	tree[parent] = tree[size+1];
	return T;
}

//制频度表,我们为了方便(其中元素又出又进的)
//故采用小顶堆的方式

class Huffman {
private:
	Node* root;
	HuffCode* CodeTable;
	int flag;
	int num;//字符数量
	string s;//输入的字符串
	string ans;//编码后的串
	
	string a;//解码后的串
	void code(HuffCode& c, Node* p, string temp)
	{//f为频度
		if (isLeaf(p))//只有是叶子节点我们才比较大小
		{
			if (p->letter == c.letter)
			{
				//p->weight *=(-1);//因为有频度相同的节点,故此处设置权重为0(使用完毕),防止重复
				c.code = temp;
				flag = 1;
				return;
			}
			else {
				temp = temp.substr(0, temp.size() - 1);//temp串仅仅去掉最后一个字符
				return;
			}
		}
		else
		{
			//其实这里跟回溯法思想挺像的,先往左探路,如果没找到对应的字符
			//就再向右探路
			code(c, p->left, temp + "0");
			if (flag == 0)//这里必须要设置一个flag,否则向左已经找到了,它还要重新向右走
			{//这不是我们想要的结果,故只有当flag==0,即向左没找到时再向右。
				code( c, p->right, temp + "1");
			}

		}
	}

public:
	Huffman() {
		a = "";
		ans = "";
		root = NULL;
		num = 0;
		flag = 0;
		CodeTable = NULL;
	};
	bool isLeaf(Node* q)
	{
		if (q->left == NULL && q->right == NULL)
			return true;
		else return false;
	}

	void Init() {
		
		getline(cin, s);
		int* p = new int[N];
		for (int i = 0; i < N; i++) p[i] = 0;//constant time
		for (int i = 0; i < s.length(); i++) //O(n)
			p[s[i]]++;

		CodeTable = new HuffCode[N];

		MinHeap john;
		john.BuildHeap(CodeTable,p, N);//造堆

		num = john.size;
		for (int i = 1; i < num; i++)
		{
			root = new Node;
			root->left = john.Delete();
			
			root->right = john.Delete();
			root->weight = root->left->weight + root->right->weight;

			john.Insert(root);
		}//注意这里执行size-1次,因为每次产生的root均是根节点
		//最后一次单独赋
		root = john.Delete();
		delete []p;
	}
	
	void CreateTable() {
		for (int i = 0; i < num; i++)
		{
			flag = 0;//每次初始化下flag
			code(CodeTable[i], root, "");
		}
		cout << "编码表为:" << endl;
		for (int i = 0; i < num; i++)
		{
			cout << CodeTable[i].letter << " : " << CodeTable[i].code << endl;
		}
	}

	void Encode()
	{
		for (int i = 0; i < s.length(); i++)
		{
			for (int j = 0; j < num; j++)
			{
				if (s[i] == CodeTable[j].letter)
				{
					ans.append(CodeTable[j].code);
				}
			}
		}
		cout << "编码后的字符串为:" << endl;
		cout << ans << endl;
	}
	void Decode() {
		string raw_s;//待解码的串
		raw_s = ans;
		int counter = 0;
		int i=0;
		Node* p=root;
		while (a.size()!=s.size())
		{

			if (raw_s[i] == '0')
				p = p->left;
			else if (raw_s[i] == '1')
				p = p->right;

			i++;
			if (isLeaf(p))
			{
				a.push_back(p->letter);
				counter++;
				p = root;
			}
		}
		cout <<"解码后的字符串为:"<< endl;
		cout << a<<endl;
	}

	void PrintTree() {
		int k = 0;
		int i = 1;
		queue <Node*> q;
		Node* p;
		p = root;
		q.push(p);
		while (!q.empty()) {
			p = q.front();
			if(isLeaf(p))
				cout << p->letter<<p->weight <<"  ";
			//塞就行了,每次我只用读取最前面的数据,因为每次代表一个节点的消失
			if (p->left) q.push(p->left);
			if (p->right) q.push(p->right);

			q.pop();




		}
	};

	~Huffman() {
		delete root;
		delete[]CodeTable;
	}
};
int main() {
	Huffman test;
	cout << "请输入字符串:" << endl;
	test.Init();
	test.CreateTable();
	test.Encode();
	test.PrintTree();
	cout << endl;
	test.Decode();
	cout << endl;
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值