贪心算法-哈夫曼编码
2016-01-20 10:07
471 查看
《算法导论》引理16.2:令C为一个字母表,其中每个字符c属于C都有一个频率c.freq。令x和y是C中频率最低的两个字符,那么存在C的一个最优前缀码,x和y的码字长度相同,且只有最后一个二进制位不同。(证明问题具有贪心选择性质)
引理16.3 令C为一个给定的字符表,其中每个字符c属于C都定义了一个频率c.freq。令x和y是C中频率最低的两个字符。令C1为C去掉字符x和y,加入一个新字符z后得到的字母表。这里z.freq=x.freq+y.freq。令T1为C1的任意一个最优前缀对应的编码树。于是我们将T1中叶结点z替换为一个以x和y为孩子的内部结点,得到树T,T表示字母表C的一个最优前缀码。(证明了构造最优前缀码的问题具有最优子结构性质)
字母表每个元素的结构
引理16.3 令C为一个给定的字符表,其中每个字符c属于C都定义了一个频率c.freq。令x和y是C中频率最低的两个字符。令C1为C去掉字符x和y,加入一个新字符z后得到的字母表。这里z.freq=x.freq+y.freq。令T1为C1的任意一个最优前缀对应的编码树。于是我们将T1中叶结点z替换为一个以x和y为孩子的内部结点,得到树T,T表示字母表C的一个最优前缀码。(证明了构造最优前缀码的问题具有最优子结构性质)
字母表每个元素的结构
struct word { char c; int freq; string b; string code; int parents; int left; int right; word() { c = ' '; parents = -1; left = -1; right = -1; } word(char ch, int f) :c(ch), freq(f) { parents=-1; left=-1; right=-1; } };
//构造哈夫曼树 word huffman(word *w,int n) { int s = n; for (int i = 1; i <= n-1; i++) { int minL = 0, minR = 0; for (int j = 1; j <= s; j++) { //数组中的最小值 if (w[j].freq < w[minL].freq&&w[j].parents == -1) minL = j; } for (int j = 1; j <= s; j++) { //数组中的次小值(可以与最小值相等) if (w[j].freq >= w[minL].freq&&w[j].freq<w[minR].freq&&j!=minL&&w[j].parents == -1) minR = j; } s++; w[s].freq = w[minL].freq + w[minR].freq; w[s].left = minL; w[s].right = minR; w[minL].parents = s; //左孩子与父结点连接的边对应编码0 w[minL].b = "0"; w[minR].parents = s; //右孩子与父结点连接的边对应编码1 w[minR].b = "1"; } //if (s == 2 * n - 1) cout << "-----------right"; cout << w[s].freq; return w[s - 1]; } void huffmanCode(word *w,int n) { for (int i = 1; i <= n; i++) { int j = i; while (w[j].parents != -1) { w[i].code += w[j].b; j = w[j].parents; } //因为上面的循环过程是从叶子结点向根节点遍历,因此最后得到的编码要将顺序调换 reverse(w[i].code.begin(), w[i].code.end()); } } int main() { //这个元素便于后面比较 word w0(' ', 0x7fffffff); word w1('a', 45); word w2('b', 13); word w3('c', 12); word w4('d', 16); word w5('e', 9); word w6('f', 5); int n = 6; //哈夫曼树一定有2*n-1个结点,将树的结点都存到数组w中 word w[12] = { w0,w1,w2,w3,w4,w5,w6 }; huffman(w, 6); huffmanCode(w, 6); return 0; }
相关文章推荐