怎么实现huffman(哈夫曼编码)以及解码

 一、编码

【题目描述】

给定一篇用于通信的英文电文,统计该电文中每个字符出现的频率,按频率左小右大的方法为这些字符建立哈夫曼(Huffamn)树,并编出每个字符的哈夫曼树码,输出该电文的哈夫曼码译文。

 

【输入】

输入文件huffman.in是一篇用于通信的英文电文。

【输出】

输出文件huffman.out输出该电文的哈夫曼码译文。

 

【输入输出样例1】

huffman.in                             

huffman.out

aaccdddbacbcddddddd

011011000011101001100010001111111

【数据限制】

2<=英文电文字符数<=10000000

统计以上abcd出现的个数。

a:3   b:2    c:4    d:10

构造出哈夫曼树

a:011         b:010     c  :00          d:1

下面主要通过两个结构体数组来实现:

struct node1
{ int w, lch, rch, parent;
}ht[2*N0-1+1];

数组下标1234567
父节点的数组下标parent0000   
左孩子节点的数组下标lch0000   
右孩子节点的数组下标rch0000   
权值w32410   

-》

数组下标1234567
父节点的数组下标parent55000  
左孩子节点的数组下标lch00002  
右孩子节点的数组下标rch00001  
权值w324105  

-》.。。。。。。。。

数组下标1234567
父节点的数组下标parent5567670
左孩子节点的数组下标lch0000256
右孩子节点的数组下标rch0000134
权值w324105919

 

struct node2
{ char ch;//对应的字符abcd
 int start;//编码的起始位置 注意这个编码是倒着的 所以这里用start
 int code[N0];//这个是编码数组
}hc[N0+1];

大概图如下面

美工不好啊 大概将就看了啊

下面给出大家想要的程序部分

//#include "stdio.h"
//#include   "string.h " 
#include <iostream>
#include <string>
const int N0=10;
const int N=100;
const int INF=1000000;
struct node1 
{ int w, lch, rch, parent;
}ht[2*N0-1+1];
struct node2
{ char ch;
 int start;
 int code[N0];
}hc[N0+1];
int n,root;//n为叶子的个数
void readData()
{ char ch;
int num[256]={ 0 };
 n=0;
 freopen( "huffman.in", "r", stdin);//读文本文件
 while( (ch=getchar()) != EOF )
  num[ch]++;
 for( int i=0; i<=255; i++ )
 { if( num[i] )
  { n++;
   ht[n].w=num[i];
   hc[n].ch=i;
  }
 }
}
void select1( int t, int *s1, int *s2)//用两个数来记录没有在树上的最小的两个值,从而进一步生成树。
{ int w1,w2;
 w1=w2=INF;
 for( int i=1; i<=t; i++ )
  if( ht[i].parent==0 )
   if( ht[i].w<w1 )
   { w2=w1;
    *s2=*s1;
    w1=ht[i].w;
    *s1=i;
   }
   else if( ht[i].w<w2 )
   { w2=ht[i].w;
    *s2=i;
   }
}


void createHufTreeHuCode()
{ int i, s1, s2;
 int child, parent;
 root=2*n-1;
 for( i=n+1; i<=root; i++)
 { select1(i-1, &s1, &s2 );
  ht[i].w=ht[s1].w+ht[s2].w;
  ht[i].lch=s1;
  ht[i].rch=s2;
  ht[s1].parent=ht[s2].parent=i;
 }
 for(  i=1; i<=n; i++)
 { child=i;
  while( child != root )
  { parent=ht[child].parent;
   if( ht[parent].lch==child )
    hc[i].code[hc[i].start++]=0;
   else 
    hc[i].code[hc[i].start++]=1;
   child=parent;
  }
 }

}
void txt2code()
{
 int i,j,m;
 char ch1[N+1]={0};
 freopen( "huffman.in", "r", stdin);
 for (int k=1;k<N+1;k++)
 {
  scanf("%c",&ch1[k]);
 }
 for( j=1,i=1; i<=N; i++)
 { if (ch1[i]==0)
  {
   break;
  }
  while (ch1[i]!=hc[j].ch)
  {
   if (hc[j].ch==0)
   {continue;
   }
   j++;
  }
  for( m=hc[j].start-1; m>=0; m--)
   printf("%d", hc[j].code[m]);
  j=1;
 }
}


int main()
{ 
 readData();
 createHufTreeHuCode();
 freopen("huffman.out", "w", stdout);
 txt2code();
 return 0;
}

二、译码

【题目描述】

给定2个输入文件,第1个输入文件是用于通信的英文电文,统计该电文中每个字符出现的频率,按频率左小右大的方法为这些字符建立哈夫曼(Huffamn)树,并编出每个字符的哈夫曼树码;第2个输入文件是已经按第1个输入文件的英文电文编好的哈夫曼码,输出该哈夫曼码的对应的英文电文。

 

【输入】

第1个输入文件为huffman.in是用于通信的英文电文, 第2个输入文件codeToTxt.in是已经按第1个输入文件编好的哈夫曼码。

【输出】

输出文件codeToTxt.out输出codeToTxt.in文件内容的英文电文。

 

【输入输出样例1】

huffman.in                            

codeToTxt.in

codeToTxt.out

aaccdddbacbcddddddd

011111011000011101001100010001111

adddaccdddbacbcdddd

【数据限制】

2<=英文电文字符数<=10000000


 

#include <iostream>
#include <string>
const int N0=10;
const int N=100;
const int INF=1000000;
struct node1 
{ int w, lch, rch, parent;
}ht[2*N0-1+1];
struct node2
{ char ch;
 int start;
 int code[N0];
}hc[N0+1];
int n,root,num[256];
void readData()
{ char ch;

 n=0;
 freopen( "huffman.in", "r", stdin);
 while( (ch=getchar()) != EOF )
  num[ch]++;//同时得到了两个东西,一个是字符,一个是个数
 for( int i=0; i<=255; i++ )
 { if( num[i] )
  { n++;
   ht[n].w=num[i];//个数
   hc[n].ch=i;//字符
  }
 }
}
void select1( int t, int *s1, int *s2)
{ int w1,w2;
 w1=w2=INF;
 for( int i=1; i<=t; i++ )
  if( ht[i].parent==0 )
   if( ht[i].w<w1 )
   { w2=w1;
    *s2=*s1;
    w1=ht[i].w;
    *s1=i;
   }
   else if( ht[i].w<w2 )
   { w2=ht[i].w;
    *s2=i;
   }
}
void createHufTreeHuCode()
{ int i, s1, s2;
 int child, parent;
 root=2*n-1;
 for( i=n+1; i<=root; i++)
 { select1( i-1, &s1, &s2 );
  ht[i].w=ht[s1].w+ht[s2].w;
  ht[i].lch=s1;
  ht[i].rch=s2;
  ht[s1].parent=ht[s2].parent=i;
 }
 for(  i=1; i<=n; i++)
 { child=i;
  while( child != root )
  { parent=ht[child].parent;
   if( ht[parent].lch==child )
    hc[i].code[hc[i].start++]=0;
   else 
    hc[i].code[hc[i].start++]=1;
   child=parent;
  }
 }

}


void code2txt()
{ char ch=0;
int i=root;
 freopen( "codeToTxt.in", "r", stdin);
 freopen("codeToTxt.out", "w", stdout);
 while( (ch=getchar()) != EOF )
 {
 
   if(ht[i].lch&&ht[i].rch)
   {if(ch=='0')
    i=ht[i].lch;
   else
    i=ht[i].rch;
   }
   if(ht[i].lch==0&&ht[i].rch==0)
   { printf("%c",hc[i].ch);
    i=root;
   }
    
 }
}


int main()
{ readData();
 createHufTreeHuCode();
 code2txt();
 return 0;
}


 

  • 10
    点赞
  • 42
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
编码Huffman Coding)是一种基于字符出现频率的无损数据压缩算法。在哈编码中,出现频率高的字符被赋予较短的编码,出现频率低的字符被赋予较长的编码,从而实现数据的压缩。下面是使用C++实现编码解码的示例代码。 **哈树的实现** 首先需要实现树的数据结构和相关的操作。 ```cpp #include <iostream> #include <queue> #include <vector> using namespace std; // 哈树的节点结构体 struct HuffmanNode { char ch; // 字符 int freq; // 出现频率 HuffmanNode *left, *right; // 左右子节点 HuffmanNode(char c, int f) : ch(c), freq(f), left(nullptr), right(nullptr) {} }; // 用于比较两个节点的出现频率 struct CompareNode { bool operator()(HuffmanNode *a, HuffmanNode *b) { return a->freq > b->freq; } }; // 创建哈HuffmanNode *createHuffmanTree(string s) { int freq[256] = {0}; // 统计每个字符出现的次数 for (char c : s) { freq[c]++; } priority_queue<HuffmanNode *, vector<HuffmanNode *>, CompareNode> q; for (int i = 0; i < 256; i++) { if (freq[i] > 0) { q.push(new HuffmanNode(i, freq[i])); } } while (q.size() > 1) { // 构造哈HuffmanNode *left = q.top(); q.pop(); HuffmanNode *right = q.top(); q.pop(); HuffmanNode *parent = new HuffmanNode('$', left->freq + right->freq); parent->left = left; parent->right = right; q.push(parent); } return q.top(); } // 销毁哈树 void destroyHuffmanTree(HuffmanNode *root) { if (root) { destroyHuffmanTree(root->left); destroyHuffmanTree(root->right); delete root; } } ``` 上面的代码中,用一个整型数组 `freq` 来统计每个字符出现的次数,然后用一个优先队列 `q` 存储所有出现过的字符。在构造哈树的过程中,每次从 `q` 中取出出现频率最小的两个字符节点(即 `freq` 数组中出现次数最少的两个字符),将它们作为左右子节点组成一个新的父节点,并将这个新的父节点插入到 `q` 中。最终,`q` 中只剩下一个节点,它就是哈树的根节点。 **哈编码实现** 接着,我们需要实现编码的过程,将输入的字符串编码成压缩后的二进制字符串。 ```cpp #include <unordered_map> #include <bitset> // 生成哈编码表 void generateHuffmanCodeTable(HuffmanNode *root, string code, unordered_map<char, string> &table) { if (root->left == nullptr && root->right == nullptr) { table[root->ch] = code; return; } generateHuffmanCodeTable(root->left, code + "0", table); generateHuffmanCodeTable(root->right, code + "1", table); } // 对字符串进行哈编码 string encode(string s) { HuffmanNode *root = createHuffmanTree(s); // 创建哈树 unordered_map<char, string> table; // 哈编码表 generateHuffmanCodeTable(root, "", table); // 生成编码表 string encoded = ""; for (char c : s) { encoded += table[c]; // 将每个字符转换为对应的哈编码 } destroyHuffmanTree(root); // 销毁哈树 return encoded; } ``` 上面的代码中,`generateHuffmanCodeTable()` 函数用来生成哈编码表,它递归遍历哈树,将每个字符的编码存储到 `table` 中。`encode()` 函数用来对输入的字符串进行编码,它先创建哈树,然后调用 `generateHuffmanCodeTable()` 函数生成编码表,最后将每个字符转换为对应的哈编码。 **哈解码实现** 最后,我们需要实现解码的过程,将压缩后的二进制字符串解码成原始的字符串。 ```cpp // 对哈编码进行解码 string decode(string encoded, HuffmanNode *root) { string decoded = ""; HuffmanNode *node = root; for (char bit : encoded) { if (bit == '0') { node = node->left; } else { node = node->right; } if (node->left == nullptr && node->right == nullptr) { decoded += node->ch; node = root; } } return decoded; } ``` 上面的代码中,`decode()` 函数用来对压缩后的二进制字符串进行解码,它逐位遍历输入的字符串,根据每个字符是 '0' 还是 '1',向左或向右遍历哈树,直到找到叶子节点。找到叶子节点后,将该节点对应的字符加入到解码后的字符串中,并将当前节点重新指向哈树的根节点。 **完整代码** 下面是完整的哈编码解码的示例代码。 ```cpp #include <iostream> #include <queue> #include <vector> #include <unordered_map> #include <bitset> using namespace std; // 哈树的节点结构体 struct HuffmanNode { char ch; // 字符 int freq; // 出现频率 HuffmanNode *left, *right; // 左右子节点 HuffmanNode(char c, int f) : ch(c), freq(f), left(nullptr), right(nullptr) {} }; // 用于比较两个节点的出现频率 struct CompareNode { bool operator()(HuffmanNode *a, HuffmanNode *b) { return a->freq > b->freq; } }; // 创建哈HuffmanNode *createHuffmanTree(string s) { int freq[256] = {0}; // 统计每个字符出现的次数 for (char c : s) { freq[c]++; } priority_queue<HuffmanNode *, vector<HuffmanNode *>, CompareNode> q; for (int i = 0; i < 256; i++) { if (freq[i] > 0) { q.push(new HuffmanNode(i, freq[i])); } } while (q.size() > 1) { // 构造哈HuffmanNode *left = q.top(); q.pop(); HuffmanNode *right = q.top(); q.pop(); HuffmanNode *parent = new HuffmanNode('$', left->freq + right->freq); parent->left = left; parent->right = right; q.push(parent); } return q.top(); } // 销毁哈树 void destroyHuffmanTree(HuffmanNode *root) { if (root) { destroyHuffmanTree(root->left); destroyHuffmanTree(root->right); delete root; } } // 生成哈编码表 void generateHuffmanCodeTable(HuffmanNode *root, string code, unordered_map<char, string> &table) { if (root->left == nullptr && root->right == nullptr) { table[root->ch] = code; return; } generateHuffmanCodeTable(root->left, code + "0", table); generateHuffmanCodeTable(root->right, code + "1", table); } // 对字符串进行哈编码 string encode(string s) { HuffmanNode *root = createHuffmanTree(s); // 创建哈树 unordered_map<char, string> table; // 哈编码表 generateHuffmanCodeTable(root, "", table); // 生成编码表 string encoded = ""; for (char c : s) { encoded += table[c]; // 将每个字符转换为对应的哈编码 } destroyHuffmanTree(root); // 销毁哈树 return encoded; } // 对哈编码进行解码 string decode(string encoded, HuffmanNode *root) { string decoded = ""; HuffmanNode *node = root; for (char bit : encoded) { if (bit == '0') { node = node->left; } else { node = node->right; } if (node->left == nullptr && node->right == nullptr) { decoded += node->ch; node = root; } } return decoded; } int main() { string s = "hello world"; cout << "Original string: " << s << endl; string encoded = encode(s); cout << "Encoded string: " << encoded << endl; string decoded = decode(encoded, createHuffmanTree(s)); cout << "Decoded string: " << decoded << endl; return 0; } ```

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值