哈夫曼算法证明

哈夫曼算法是一种贪心算法,我们考虑证明其最优子结构和贪心选择性质:

  • 最优子结构:假设一个树是哈夫曼树,则以其任意节点为根节点的最大子树也是哈夫曼树。

    证明:子树的根节点的值是其所有叶子节点出现权值之和,因此无论子树是什么形式,对子树上方的节点计算WPL2都没有影响。

    根据哈夫曼树的定义:WPL最小的二叉树。如果子树不是哈夫曼树,其WPL1就不会是最小,那么整个树的WPL=WPL1+WPL2就不会是最小,这与哈夫曼树的定义相悖,因此子树是哈夫曼树。

  • 贪心选择性质(哈夫曼算法):每次去掉权值最低的两个节点作为叶子节点形成一颗二叉树,并将其父亲节点放入待选节点中。

    证明:对于哈夫曼树我们总可以通过取出两个节点作为叶子节点并将其父亲节点重新作为叶子节点的方式构造,需要证明的是是否每次选择权值最低的节点可以构造成功。

    当含有2个及以下的叶子节点的时候,显然正确。

    假设当含有小于k个叶子节点的时候哈夫曼算法可以形成哈夫曼树

    对于含有k个叶子节点形成的树,设其中权重最小的叶子节点为a,其中深度最深的叶子节点为b,h表示节点的深度,w表示节点的权值。则:
    WPL1=∑+wa∗ha+wb∗hbWPL_1=\sum+w_a*h_a+w_b*h_b WPL1​=∑+wa​∗ha​+wb​∗hb​
    交换这两个节点:
    WPL2=∑+wb∗ha+wa∗hbWPL_2=\sum+w_b*h_a+w_a*h_b WPL2​=∑+wb​∗ha​+wa​∗hb​

WPL1−WPL2=(wa−wb)∗ha+(wb−wa)∗hb=(hb−ha)∗(wb−wa)WPL_1-WPL_2=(w_a-w_b)*h_a+(w_b-w_a)*h_b=(h_b-h_a)*(w_b-w_a) WPL1​−WPL2​=(wa​−wb​)∗ha​+(wb​−wa​)∗hb​=(hb​−ha​)∗(wb​−wa​)

而hb−hah_b-h_ahb​−ha​和wb−waw_b-w_awb​−wa​都为正(由a,b节点的性质),所以我们得到结论:对于任何一棵树,将权值小的节点尽可能移动到较深层的节点会使整个树的WPL比较小。

对于k个节点,我们首先取出两个节点,将其合并成一个节点以后我们有k-1个节点,可以使用哈夫曼算法构造。

如果这两个节点不是所有节点中权值最小的两个,则我们总可以通过交换使得构造的树的WPL减小,因此不是哈夫曼树。只有两个节点是所有节点中权值最小的两个时我们无法再降低树的WPL。因为我们总可以构造成功,所以选择权值最小的节点构造的树就是哈夫曼树。证毕。

哈夫曼编码译码程序

#pragma once
#include<iostream>
#include<fstream>
#include<vector>
#include<queue>
#include<cstdio>
#include<string>using namespace std;static const int MAXN = 1005;struct times
{double weight;//字符的权值int num;//字符的序号,同时也是他的ASCALL值times(int _num=0,double _weight=0) :num(_num),weight(_weight){}friend bool operator < (const times & a,const times & b){return a.weight > b.weight;}
}p,q;struct Chara:times
{int father;//父亲的序号int lson, rson;//左右儿子的序号string code;Chara(int _num = 0, double _weight = 0, int _lson = 0, int _rson = 0, int _father = 0) :times(_num,_weight), lson(_lson), rson(_rson), father(_father){code = "";//没有编码}void operator = (const Chara& x){weight = x.weight; num = x.num; father = x.father; lson = x.lson; rson = x.rson;}
};struct txt
{string t;double weight;txt(string _t,double _weight):t(_t),weight(_weight){}
};class HuffmanCode
{public:int n=0;//字符的个数int cur;//当前所在位置int root;//哈夫曼树根节点Chara A[MAXN];//顺序表保存哈夫曼树priority_queue<times> T;//用来构建哈夫曼树void CreatHuffmanCode(int x, string now){if (A[x].lson != 0){CreatHuffmanCode(A[x].lson, now + "0");}if (A[x].rson != 0){CreatHuffmanCode(A[x].rson, now + "1");}if (A[x].lson == 0 && A[x].rson == 0)//说明是字符{A[x].code = now;}}void _HuffmanCode(int _n,vector<txt>& input){string tmp;n = _n;for (int i = 0; i < n; i++){tmp = input[i].t;A[tmp[0]].num = tmp[0];A[tmp[0]].weight = input[i].weight;T.push(times(tmp[0], A[tmp[0]].weight));}cur = 500;//构建哈夫曼树while (T.size() > 1){p = T.top(); T.pop(); q = T.top(); T.pop();A[cur] = Chara(cur, p.weight + q.weight, p.num, q.num, 0); A[p.num].father = A[q.num].father = cur;T.push(times(A[cur])); cur++;}T.pop(); root = cur-1;CreatHuffmanCode(root, "");}
};class Huffman
{int n;vector<txt> input;HuffmanCode x;
public:void _Huffman(){string t; double weight; n = 0;FILE* stream;freopen_s(&stream,"hfmTree.txt","r",stdin);while (1){cin >> t;if (t == "Esc") break;n++;cin >> weight;input.push_back(txt(t, weight));}freopen_s(&stream, "CON", "r", stdin);input.push_back(txt(" ", 10000.0));//给空格很大的权值n++;x._HuffmanCode(n, input);}Huffman(){string t; double weight; n = 0;cout << "请输入字符集 \n[Delete撤销输入,Esc退出输入]\n[直接输入Default按照默认文件组成哈夫曼编码]\n[空格已经编码]"<< endl;while(1){cout << "请输入字符:";cin >> t;if (n == 0 && t == "Default")//按照默认文件构造哈夫曼树{_Huffman();return;}else if (t == "Delete"){input.erase(input.end()-1);//删除最后一个输入的字符n--;continue;}else if (t == "Esc"){break;}n++;cout << "请输入" << t[0] << "的权重:";cin >> weight;input.push_back(txt(t, weight));}FILE* stream;freopen_s(&stream, "hfmTree.txt", "w", stdout);for (int i = 0; i < n; i++){cout << input[i].t << " " << input[i].weight << endl;}cout << "Esc" << endl;freopen_s(&stream, "CON", "w", stdout);input.push_back(txt(" ", 10000.0));//给空格很大的权值n++;x._HuffmanCode(n, input);}void HuffmanDisplay(){cout << "哈夫曼编码:" << endl;for (int i = 0; i < n; i++){cout << input[i].t[0] << ":" << x.A[input[i].t[0]].code << endl;}}void GenerateCode()//压缩文件{cout << "请输入需要压缩文件的路径[输入Default将打开默认文件ToBeTran.txt]" << endl;string in;cin >> in;if (in == "Default"){in = "ToBeTran.txt";}cout << "请输入保存压缩后文件的路径[输入Default将打开默认文件CodeFile.txt]" << endl;string out;cin >> out;if (out == "Default"){out = "CodeFile.txt";}ifstream infile(in, ios::in);ofstream outfile(out, ios::out);char c;bool flag = true;while ((c = infile.get()) != EOF){if (x.A[c].code == ""){flag = false;break;}outfile << x.A[c].code;}infile.close();outfile.close();if (!flag){cout << "压缩失败,文件中出现了字符集中未包含的字符" << endl;return;}//展示压缩的结果:infile.open(out, ios::in);string tmp;infile >> tmp;infile.close();cout << "编码后的文件为:" << endl;for (int i = 0; i < tmp.size(); i++){if (i && i % 50 == 0) cout << endl;cout << tmp[i];}cout << endl;//将结果放入文件中outfile.open("CodePrint.txt", ios::out);for (int i = 0; i < tmp.size(); i++){if (i % 50 == 0) outfile << endl;outfile << tmp[i];}outfile.close();}void Decode(){cout << "请输入需要解码文件的路径[输入Default将打开默认文件CodeFile.txt]" << endl;string in;cin >> in;if (in == "Default"){in = "CodeFile.txt";}cout << "请输入保存压缩后文件的路径[输入Default将打开默认文件TextFile.txt]" << endl;string out;cin >> out;if (out == "Default"){out = "TextFile.txt";}ifstream infile(in, ios::in);string ss;infile >> ss;//cout << "test:" << ss << endl;int i = 0;string sss;while (i < ss.size()){int t = x.root;while ((x.A[t].lson != 0 || x.A[t].rson != 0) && i < ss.size()){if (ss[i] == '0') t = x.A[t].lson;else t = x.A[t].rson;i++;}if (x.A[t].lson == 0 || x.A[t].rson == 0){sss = sss + (char)t;}}infile.close();cout << "解码后为:" << endl;cout << sss << endl;ofstream outfile(out,ios::out);outfile << sss << endl;outfile.close();}
};

测试代码

#include"Huffman.h"#include<iostream>using namespace std;int main()
{Huffman x;x.HuffmanDisplay();x.GenerateCode();x.Decode();}

测试结果

哈夫曼算法证明+哈夫曼编码译码程序实现相关推荐

  1. 霍夫曼算法_霍夫曼编码算法

    霍夫曼算法 In this tutorial, we'll be discussing and implementing the Huffman Coding Algorithm in Java. 在 ...

  2. matlab牛顿拉夫逊算法,牛顿拉夫逊法程序设计

    该楼层疑似违规已被系统折叠 隐藏此楼查看此楼 function gsj clear all; n=0; fidin=fopen('start3.txt'); while ~feof(fidin) % ...

  3. 哈夫曼树以及哈夫曼算法

    目录 一.哈夫曼树的定义 二.哈夫曼树的特点 三.哈夫曼算法(构造哈夫曼树的方法) 四.哈夫曼树的构造过程 五.哈夫曼树构造算法的实现 一.哈夫曼树的定义 1.哈夫曼树:最优树即带权路径长度(WPL) ...

  4. 【数据结构与算法】-哈夫曼树(Huffman Tree)与哈夫曼编码

    超详细讲解哈夫曼树(Huffman Tree)以及哈夫曼编码的构造原理.方法,并用代码实现. 1哈夫曼树基本概念 路径:从树中一个结点到另一个结点之间的分支构成这两个结点间的路径. 结点的路径长度:两 ...

  5. Java数据结构和算法:哈夫曼树

    本章介绍哈夫曼树.和以往一样,本文会先对哈夫曼树的理论知识进行简单介绍,然后给出C语言的实现.后续再分别给出C++和Java版本的实现:实现的语言虽不同,但是原理如出一辙,选择其中之一进行了解即可.若 ...

  6. 数据结构与算法学习④(哈夫曼树 图 分治回溯和递归)

    数据结构与算法学习④(哈夫曼树 图 回溯和递归 数据结构与算法学习④ 1.哈夫曼树 1.1.相关概念 1.2.哈夫曼树的构建 1.3.哈夫曼编码 1.4.面试题 2.图 2.1.图的相关概念 2.2. ...

  7. 使用libjpeg进行图片压缩(哈夫曼算法,无损压缩)

    Huffman算法也是一种无损压缩算法,但与LZW压缩算法不同,Huffman需要得到每种字符出现概率的先验知识.通过计算字符序列中每种字符出现的频率,为每种字符进行唯一的编码设计,使得频率高的字符占 ...

  8. 【数据结构与算法】哈夫曼树的Java实现

    哈夫曼树 最优二叉树也称哈夫曼树,讲的直白点就是每个结点都带权值,我们让大的值离根近.小的值离根远,实现整体权值(带权路径长度)最小化. 哈夫曼算法的思想我认为就是上面讲的,而它的算法实现思路是这样的 ...

  9. 利用C++实现哈夫曼算法

    我想每个计算机专业的学生或多或少都接触过哈夫曼编码,数据结构中的老问题了.大体就是给出一些字符,和这些字符的出现频率,让你为这些字符设计一个二进制编码,要求频率最高的字符的编码最短.解决的方法是构造一 ...

最新文章

  1. Mybatis 使用的 9 种设计模式,真是太有用了
  2. 解决调用HttpContext类
  3. 系统管理-第1部分 系统的易管理性
  4. Fedora中允许mysql远程访问的几种方式
  5. pyinstaller打包exe程序教程推荐及需要注意的点
  6. 如何生成WebPart的部署文件(wsp文件)
  7. 阿里云混合云的政企上云新路径
  8. 设置横坐标刻度_是不是快被Excel的时间刻度逼疯了,教你两招轻松解决
  9. 统计进仓和出仓数量随时间变化的剩余数量流程图
  10. jQuery学习之八---文档处理
  11. php 导航栏链接网页,怎样用php来给网页做导航栏_php实例
  12. 20200507:力扣151周赛下
  13. bat for 循环中定义变量(变量值不显示,通过使用「延期变量扩展」方式解决)
  14. scrapy 爬虫之爬取CSDN博客(一)
  15. 最新快递鸟接口快递公司编码汇总(国际版)
  16. 鸿蒙系统不可能有了,【图片】华为鸿蒙系统的厉害之处在于 你可能非用不可 !【手机吧】_百度贴吧...
  17. apk+html启动页,案例——APP启动页
  18. iPhone系统升级后怎么恢复以前的系统
  19. COVID-19检测方法汇总
  20. Vs Code中Vue代码格式插件,Vetur、ESLint 、Prettier - Code formatter的介绍使用及相关配置

热门文章

  1. 第4章 maven依赖特性
  2. 【原创】MapReduce编程系列之表连接
  3. ORACLE10g R2及PATH官方下载地址
  4. 关于 C语言的 按位取反 ~
  5. mysql pxc_MySQL之PXC集群搭建
  6. linux raw socket 例子,raw socket编程例子
  7. linux网络唤醒,如何在Ubuntu Server 18.04中启用网络唤醒(WOL)
  8. 对hash签名失败_vue项目中微信jssdk在ios签名失败
  9. 导出mysql excel数据字典_mysql导出 Excel数据字典(全)
  10. 怎么检测不到我的音频_Linux 上的最佳音频编辑工具推荐 | Linux 中国