题意:huffman编码,不懂看coolshell大神的博文,要求输出最优码长和原始码长以及比值。(原始码长就是每个字符都是按8个长度算)
分析:本来huffman编码还好,但是用建树敲完一遍调了半天,实在蛋疼(果然我编码能力太渣)。
于是我开始想不建树的算法,想了半天,终于YY出一个算法 = =。
(借用coolshell的例子来说)
字符 | 次数 |
‘b’ | 3 |
‘e’ | 4 |
‘p’ | 2 |
‘ ‘ | 2 |
‘o’ | 2 |
‘r’ | 1 |
‘!’ | 1 |
这时r和!的两个字符合并,构成子树的码长就是2,出现在字符串中的字母数是2。
第二次:
此时第一次建的树下降一层,每个字符的码长都会增加1,而这时这棵子树的码长就变成原来的码长+字符数了。而现在的树就是左子树+右子树,码长和字符数都要处理下。
依次类推,到最后一个数时的码长就是huffman编码需要的码长了。
总结起来就是不用建树,直接模拟计算码长的过程。
刚开始每个数都是一棵树,字符数和码长都是它在字符串中出现的次数,因为此时是独立的,每个字符的码长都是1。然后每次找字符数最小的两个合并,被合并的两棵树就会下降一层,也就是说它每个字符串的码长都增加1,那总码长就增加了它的字符数了。而合并时就是将俩树处理过后的码长和字符数分别相加。
这样合并到最后一棵树,全部字符就会整合成一棵树(不会表现出来),求出来的就是字符串huffman的总码长了。
代码:
/* * Author: illuz <iilluzen[at]gmail.com> * Blog: http://blog.csdn.net/hcbbt * File: live2068.cpp * Create Date: 2013-09-08 20:06:27 * Descripton: not-tree */ #include <cstdio> #include <cstring> #include <string> #include <queue> #include <iostream> using namespace std; const int MAXN = 1000; struct Alpha { int num; // alpha num of the sub-tree int len; // length of the sub-tree bool friend operator < (const Alpha& a, const Alpha& b) { return a.num > b.num; } } a[27]; string s; priority_queue<Alpha> q; int main() { while (cin >> s && s != "END") { while (!q.empty()) q.pop(); memset(a, 0, sizeof(a)); int l = s.size(); for (int i = 0; i < l; i++) if (s[i] == '_') a[26].num++; else a[s[i] - 'A'].num++; for (int i = 0; i < 27; i++) if (a[i].num) q.push(a[i]); Alpha tmp, t1, t2; if (q.size() == 1) { printf("%d %d 8.0\n", l * 8, l); continue; } while (q.size() != 1) { t1 = q.top(); q.pop(); t2 = q.top(); q.pop(); tmp.num = t1.num + t2.num; // 字符数=两棵树字符数和 tmp.len = tmp.num + t1.len + t2.len; // 总码长=两棵树的(码长数+字符数)的和 q.push(tmp); } printf("%d %d %.1lf\n", l * 8, tmp.len, double(l * 8) / tmp.len); } return 0; }