#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;
}
二叉链表实现哈夫曼树
最新推荐文章于 2024-11-22 01:55:16 发布