哈夫曼树

来源:互联网 发布:公司网络布局 编辑:程序博客网 时间:2024/06/08 17:50

哈夫曼树

给定n个权值作为n个叶子结点,构造一棵二叉树,若带权路径长度达到最小,称这样的二叉树为最优二叉树,也称为哈夫曼树(Huffman tree)。哈夫曼树是带权路径长度最短的树,权值较大的结点离根较近。节点的带权长度是这样定义的:节点的权值*根节点到该节点的路径长度。树的带权路径长度(Weighted Path Length of Tree,简记为WPL)则是指所有节点的带权长度和。哈夫曼树就是使WPL最小的一种树,并且哈夫曼树是满二叉树。它的构造方法是哈夫曼方法。哈夫曼树是这样构造的:

假设有n个权值,则构造出的哈夫曼树有n个叶子结点。 n个权值分别设为 w1、w2、…、wn,则哈夫曼树的构造规则为:(摘自百度百科)

  1. 将w1、w2、…,wn看成是有n 棵树的森林(每棵树仅有一个结点);
  2. 在森林中选出两个根结点的权值最小的树合并,作为一棵新树的左、右子树,且新树的根结点权值为其左、右子树根结点权值之和;
  3. 从森林中删除选取的两棵树,并将新树加入森林;
  4. 重复(2)、(3)步,直到森林中只剩一棵树为止,该树即为所求得的哈夫曼树。

哈夫曼编码

提到哈夫曼树,这就不得不提到哈夫曼编码。哈夫曼编码(Huffman Coding)是一种编码方式,哈夫曼编码是可变字长编码(VLC)的一种。Huffman于1952年提出一种编码方法,该方法完全依据字符出现概率来构造异字头的平均长度最短的码字,有时称之为最佳编码,一般就叫做Huffman编码(有时也称为霍夫曼编码)。它的构造比较简单:先建好哈夫曼树,左子树的路径标记为0,右子树的路径标记为1。叶节点的哈夫曼编码就是根节点到叶节点的简单路径上的0、1序列。

下面给出构建哈夫曼树和哈夫曼编码的c++代码:

代码

//Huffman树#include<iostream>#include<iomanip>#include<stack>using namespace std;//最大权值const int MAXWEIGHT = 100;//Huffman节点typedef struct{int weight;  //节点权值int parent;  //父节点下标int lchild;  //左孩子下标int rchild;  //右孩子下标}HuffNode, *HuffTree;//HuffCodetypedef struct{//权值int weight;//用栈存储编码stack<char> code;}HuffCode;/*创建Huffman树根据指定的权值数组创建Huffman树*/HuffNode *buildHuffTree(int weight[], int n){if (!weight || n < 1)return NULL;/*用顺序存储Huffman树Huffman树是满二叉树n为叶子节点数,则内部节点数是n-1,共有2*n-1个节点*/int m = 2 * n - 1;    //节点总数HuffNode* tree = new HuffNode[m];//初始化Huffman树int i, j;for (i = 0; i < m; i++){//把初始节点存储在数组前部if (i < n)tree[i].weight = weight[i];tree[i].parent = tree[i].lchild = tree[i].rchild = -1;}/*建树只需n-1次循环w1是当前最小权值,p1是其下标;w2是当前次最小权值,p2是其下标*/int w1, w2, p1, p2;for (i = n; i < m; i++){//每次循环前都得初始化权值和下标w1 = w2 = MAXWEIGHT;p1 = p2 = 0;//寻找最小和次最小权值节点for (j = 0; j < i; j++){//父节点下标为-1,说明该节点未被使用if (tree[j].parent == -1){if (tree[j].weight < w1){w2 = w1;p2 = p1;w1 = tree[j].weight;p1 = j;}else if (tree[j].weight < w2){w2 = tree[j].weight;p2 = j;}}}//把产生的新节点放入位置itree[p1].parent = tree[p2].parent = i;tree[i].weight = tree[p1].weight + tree[p2].weight;tree[i].lchild = p1;tree[i].rchild = p2;}return tree;}/*根据Huffman树构建Huffman编码从叶结点回溯:边回溯,边判断当前节点位于父节点的左子树or右子树这也是使用栈保存编码的原因:先进后出*/HuffCode* huffCode(HuffTree tree, int n){HuffCode* Code = new HuffCode[n];int i, child, parent;for (i = 0; i < n; i++){stack<char> stack;child = i;parent = tree[child].parent;while (parent != -1){//左孩子的分支是1if (tree[parent].lchild == child)stack.push('0');else//右孩子的分支是1stack.push('1');child = parent;parent = tree[child].parent;}Code[i].weight = tree[i].weight;Code[i].code = stack;}return Code;}//打印Huffman树void printHuffTree(HuffTree tree, int n){int i;cout.setf(ios::left);for (i = 0; i < n; i++){cout << setw(2) << i << " 权值:" << setw(3) << tree[i].weight << "父亲:" << setw(3) << tree[i].parent << "左孩子:" << setw(3) << tree[i].lchild << "右孩子:" << setw(3) << tree[i].rchild << endl;}}int main(){cout << "***Huffman树***by David***" << endl;int n;cout << "输入权值个数 ";while (cin >> n && n < 1)cout << "出错,重新输入 ";int *weight = new int[n];cout << "输入权值序列 ";int i = 0;while (i < n)cin >> weight[i++];cout << "创建Huffman树" << endl;HuffTree tree = buildHuffTree(weight, n);printHuffTree(tree, 2 * n - 1);cout << endl;cout << "进行Huffman编码" << endl;HuffCode *code = huffCode(tree, n);for (i = 0; i < n; i++){cout << "权值:" << setw(3) << code[i].weight << "Huffman编码:";stack<char> stack = code[i].code;while (!stack.empty()){cout << stack.top();stack.pop();}cout << endl;}//释放空间delete[]weight;delete[]tree;delete[]code;system("pause");return 0;}


运行


代码下载:哈夫曼树


转载请注明出处,本文地址:http://blog.csdn.net/zhangxiangdavaid/article/details/37696533


若有所帮助,顶一个哦!  


专栏目录

  • 数据结构与算法目录
所以内容的目录
  • CCPP Blog 目录


3 0
原创粉丝点击