算法笔记——【贪心算法】哈夫曼编码问题

合集下载

贪心算法构造哈夫曼树

贪心算法构造哈夫曼树

软件02 1311611006 张松彬利用贪心算法构造哈夫曼树及输出对应的哈夫曼编码问题简述:两路合并最佳模式的贪心算法主要思想如下:(1)设w={w0,w1,......wn-1}是一组权值,以每个权值作为根结点值,构造n棵只有根的二叉树(2)选择两根结点权值最小的树,作为左右子树构造一棵新二叉树,新树根的权值是两棵子树根权值之和(3)重复(2),直到合并成一颗二叉树为一、实验目的(1)了解贪心算法和哈夫曼树的定义(2)掌握贪心法的设计思想并能熟练运用(3)设计贪心算法求解哈夫曼树(4)设计测试数据,写出程序文档二、实验内容(1)设计二叉树结点数据结构,编程实现对用户输入的一组权值构造哈夫曼树(2)设计函数,先序遍历输出哈夫曼树各结点3)设计函数,按树形输出哈夫曼树代码:#include <stdio.h>#include <string.h>#include <time.h>#include <stdlib.h>typedef struct Node{ //定义树结构int data;struct Node *leftchild;struct Node *rightchild;}Tree;typedef struct Data{ //定义字符及其对应的频率的结构int data;//字符对应的频率是随机产生的char c;};void Initiate(Tree **root);//初始化节点函数int getMin(struct Data a[],int n);//得到a中数值(频率)最小的数void toLength(char s[],int k);//设置有k个空格的串svoid set(struct Data a[],struct Data b[]);//初始化a,且将a备份至bchar getC(int x,struct Data a[]);//得到a中频率为x对应的字符void prin(struct Data a[]);//输出初始化后的字符及对应的频率int n;void main(){//srand((unsigned)time(NULL));Tree *root=NULL,*left=NULL,*right=NULL,*p=NULL; int min,num;int k=30,j,m;struct Data a[100];struct Data b[100];int i;char s[100]={'\0'},s1[100]={'\0'};char c;set(a,b);prin(a);Initiate(&root);Initiate(&left);Initiate(&right);Initiate(&p);//设置最底层的左节点min=getMin(a,n);left->data=min;left->leftchild=NULL;left->rightchild=NULL;//设置最底层的右节点min=getMin(a,n-1);right->data=min;right->leftchild=NULL;right->rightchild=NULL;root->data=left->data+right->data;Initiate(&root->leftchild);Initiate(&root->rightchild);//将设置好的左右节点插入到root中root->leftchild=left;root->rightchild=right;for(i=0;i<n-2;i++){min=getMin(a,n-2-i);Initiate(&left);Initiate(&right);if(min<root->data)//权值小的作为左节点{left->data=min;left->leftchild=NULL;left->rightchild=NULL;p->data=min+root->data;Initiate(&p->leftchild);Initiate(&p->rightchild);p->leftchild=left;p->rightchild=root;root=p;}else{right->data=min;right->leftchild=NULL;right->rightchild=NULL;p->data=min+root->data;Initiate(&p->leftchild);Initiate(&p->rightchild);p->leftchild=root;p->rightchild=right;root=p;}Initiate(&p);}num=n-1;p=root;printf("哈夫曼树如下图:\n");while(num){if(num==n-1){for(j=0;j<k-3;j++)printf(" ");printf("%d\n",root->data);}for(j=0;j<k-4;j++)printf(" ");printf("/ \\\n");for(j=0;j<k-5;j++)printf(" ");printf("%d",root->leftchild->data);printf(" %d\n",root->rightchild->data);if(root->leftchild->leftchild!=NULL){root=root->leftchild;k=k-2;}else{root=root->rightchild;k=k+3;}num--;}num=n-1;Initiate(&root);root=p;printf("各字符对应的编码如下:\n");while(num){if(root->leftchild->leftchild==NULL){strcpy(s1,s);m=root->leftchild->data;c=getC(m,b);printf("%c 【%d】:%s\n",c,m,strcat(s1,"0"));}if(root->rightchild->leftchild==NULL){strcpy(s1,s);m=root->rightchild->data;c=getC(m,b);printf("%c 【%d】:%s\n",c,m,strcat(s1,"1"));}if(root->leftchild->leftchild!=NULL){strcat(s,"0");root=root->leftchild;}if(root->rightchild->leftchild!=NULL){strcat(s,"1");root=root->rightchild;}num--;}}int getMin(struct Data a[],int n){int i,t;for(i=1;i<n;i++){if(a[i].data<a[0].data){t=a[i].data;a[i].data=a[0].data;a[0].data=t;}}t=a[0].data;for(i=0;i<n-1;i++){a[i]=a[i+1];}return t;}void toLength(char s[],int k){int i=0;for(;i<k;i++)strcat(s," ");}void Initiate(Tree **root){*root=(Tree *)malloc(sizeof(Tree));(*root)->leftchild=NULL;(*root)->rightchild=NULL;}void set(struct Data a[],struct Data b[]) {int i;srand((unsigned)time(NULL));n=rand()%10+2;for(i=0;i<n;i++){a[i].data=rand()%100+1;a[i].c=i+97;b[i].data=a[i].data;b[i].c=a[i].c;if(i>=0&&a[i].data==a[i-1].data)i--;}}char getC(int x,struct Data b[]){int i;for(i=0;i<n;i++){if(b[i].data==x){break;}}return b[i].c;}void prin(struct Data a[]){int i;printf("字符\t出现的频率\n");for(i=0;i<n;i++){printf("%c\t %d\n",a[i].c,a[i].data);}}。

哈夫曼编码问题总结

哈夫曼编码问题总结

哈夫曼编码问题总结
哈夫曼编码是一种常见的数据压缩算法,但在实践中常常遇到各种问题。

以下是哈夫曼编码问题的总结:
1. 频率表不准确:哈夫曼编码的核心是根据字符出现的频率构建树结构,如果频率表不准确,树的构建就会出现问题。

因此,在使用哈夫曼编码前,需要确保频率表的准确性。

2. 编码长度不一:哈夫曼编码是一种变长编码,不同字符的编码长度不一定相同。

如果出现某个字符的编码长度过长,就会导致压缩效果变差。

为了避免这种情况,可以使用贪心算法来构建哈夫曼树,使得每个字符的编码长度尽可能短。

3. 解码效率低:在解码哈夫曼编码时,需要遍历整个哈夫曼树,查找对应的字符。

如果哈夫曼树过大,解码效率就会变低。

为了提高解码效率,可以使用哈夫曼编码的反向索引表来加速查找。

4. 压缩效果不佳:尽管哈夫曼编码可以大幅度减少数据的存储空间,但在某些情况下,压缩效果可能不如其他算法。

例如,对于随机分布的数据,哈夫曼编码的效果很差。

因此,在选择数据压缩算法时,需要根据具体情况进行选择。

总之,哈夫曼编码是一种强大的数据压缩算法,但在实践中需要注意以上问题。

只有对这些问题有充分的了解和掌握,才能更好地运用哈夫曼编码来实现数据压缩。

- 1 -。

实验三.哈夫曼编码的贪心算法设计

实验三.哈夫曼编码的贪心算法设计

实验四 哈夫曼编码的贪心算法设计(4学时)[实验目的]1. 根据算法设计需要,掌握哈夫曼编码的二叉树结构表示方法;2. 编程实现哈夫曼编译码器;3. 掌握贪心算法的一般设计方法。

实验目的和要求(1)了解前缀编码的概念,理解数据压缩的基本方法;(2)掌握最优子结构性质的证明方法;(3)掌握贪心法的设计思想并能熟练运用(4)证明哈夫曼树满足最优子结构性质;(5)设计贪心算法求解哈夫曼编码方案;(6)设计测试数据,写出程序文档。

实验内容设需要编码的字符集为{d 1, d 2, …, dn },它们出现的频率为 {w 1, w 2, …, wn },应用哈夫曼树构造最短的不等长编码方案。

核心源代码#include <stdio.h>#include <stdlib.h>#include <string.h>typedef struct{unsigned int weight; //用来存放各个结点的权值unsigned int parent,LChild,RChild; //指向双亲、孩子结点的指针} HTNode, *HuffmanTree; //动态分配数组,存储哈夫曼树typedef char *HuffmanCode; //动态分配数组,存储哈夫曼编码∑=ji k k a//选择两个parent为0,且weight最小的结点s1和s2 void Select(HuffmanTree *ht,int n,int *s1,int *s2){int i,min;for(i=1; i<=n; i++){if((*ht)[i].parent==0){min=i;break;}}for(i=1; i<=n; i++){if((*ht)[i].parent==0){if((*ht)[i].weight<(*ht)[min].weight)min=i;}}*s1=min;for(i=1; i<=n; i++){if((*ht)[i].parent==0 && i!=(*s1)){min=i;break;}}for(i=1; i<=n; i++){if((*ht)[i].parent==0 && i!=(*s1)){if((*ht)[i].weight<(*ht)[min].weight)min=i;}}*s2=min;}//构造哈夫曼树ht,w存放已知的n个权值void CrtHuffmanTree(HuffmanTree *ht,int *w,int n) {int m,i,s1,s2;m=2*n-1; //总共的结点数*ht=(HuffmanTree)malloc((m+1)*sizeof(HTNode));for(i=1; i<=n; i++) //1--n号存放叶子结点,初始化{(*ht)[i].weight=w[i];(*ht)[i].LChild=0;(*ht)[i].parent=0;(*ht)[i].RChild=0;}for(i=n+1; i<=m; i++) //非叶子结点的初始化{(*ht)[i].weight=0;(*ht)[i].LChild=0;(*ht)[i].parent=0;(*ht)[i].RChild=0;}printf("\n哈夫曼树为: \n");for(i=n+1; i<=m; i++) //创建非叶子结点,建哈夫曼树{ //在(*ht)[1]~(*ht)[i-1]的范围内选择两个parent为0且weight最小的结点,其序号分别赋值给s1、s2 Select(ht,i-1,&s1,&s2);(*ht)[s1].parent=i;(*ht)[s2].parent=i;(*ht)[i].LChild=s1;(*ht)[i].RChild=s2;(*ht)[i].weight=(*ht)[s1].weight+(*ht)[s2].weight;printf("%d (%d, %d)\n",(*ht)[i].weight,(*ht)[s1].weight,(*ht)[s2].weight);}printf("\n");}//从叶子结点到根,逆向求每个叶子结点对应的哈夫曼编码void CrtHuffmanCode(HuffmanTree *ht, HuffmanCode *hc, int n){char *cd; //定义的存放编码的空间int a[100];int i,start,p,w=0;unsigned int c;hc=(HuffmanCode *)malloc((n+1)*sizeof(char *)); //分配n个编码的头指针cd=(char *)malloc(n*sizeof(char)); //分配求当前编码的工作空间cd[n-1]='\0'; //从右向左逐位存放编码,首先存放编码结束符for(i=1; i<=n; i++) //求n个叶子结点对应的哈夫曼编码{a[i]=0;start=n-1; //起始指针位置在最右边for(c=i,p=(*ht)[i].parent; p!=0; c=p,p=(*ht)[p].parent) //从叶子到根结点求编码{if( (*ht)[p].LChild==c){cd[--start]='1'; //左分支标1a[i]++;}else{cd[--start]='0'; //右分支标0a[i]++;}}hc[i]=(char *)malloc((n-start)*sizeof(char)); //为第i个编码分配空间strcpy(hc[i],&cd[start]); //将cd复制编码到hc}free(cd);for(i=1; i<=n; i++)printf(" 权值为%d的哈夫曼编码为:%s\n",(*ht)[i].weight,hc[i]);for(i=1; i<=n; i++)w+=(*ht)[i].weight*a[i];printf(" 带权路径为:%d\n",w);}void main(){HuffmanTree HT;HuffmanCode HC;int *w,i,n,wei;printf("**哈夫曼编码**\n" );printf("请输入结点个数:" );scanf("%d",&n);w=(int *)malloc((n+1)*sizeof(int)); printf("\n输入这%d个元素的权值:\n",n); for(i=1; i<=n; i++){printf("%d: ",i);fflush(stdin);scanf("%d",&wei);w[i]=wei;}CrtHuffmanTree(&HT,w,n); CrtHuffmanCode(&HT,&HC,n);}实验结果实验体会哈夫曼编码算法:每次将集合中两个权值最小的二叉树合并成一棵新二叉树,n-1次合并后,成为最终的一棵哈夫曼树。

哈夫曼编码贪心算法时间复杂度

哈夫曼编码贪心算法时间复杂度

哈夫曼编码贪心算法时间复杂度
哈夫曼编码的贪心算法时间复杂度为O(nlogn),其中n为待编
码的字符数量。

算法的主要步骤包括构建哈夫曼树和生成编码表两部分。

构建哈夫曼树的时间复杂度为O(nlogn),其中n为待编码的字
符数量。

构建哈夫曼树的过程涉及到对字符频次列表进行排序,并不断合并频次最小的两个节点,直至只剩下一个节点作为根节点。

排序的时间复杂度为O(nlogn),每次合并两个节点的时间复杂度为O(logn)。

因此,构建哈夫曼树的总时间复杂度为
O(nlogn)。

生成编码表的时间复杂度同样为O(nlogn),其中n为待编码的字符数量。

生成编码表的过程是遍历哈夫曼树的每个节点,并记录下每个字符所对应的编码。

由于哈夫曼树的每个叶子节点代表一个字符,因此遍历哈夫曼树的时间复杂度为O(n),并
且遍历过程的时间复杂度与树的高度相关,由于哈夫曼树是一个二叉树,因此树的高度为O(logn)。

因此,生成编码表的总
时间复杂度为O(nlogn)。

综上所述,哈夫曼编码的贪心算法的时间复杂度为O(nlogn)。

哈夫曼编码贪心算法

哈夫曼编码贪心算法

哈夫曼编码贪心算法
一、哈夫曼编码
哈夫曼编码(Huffman Coding)是一种著名的数据压缩算法,也称作霍夫曼编码,由美国信息论家杰弗里·哈夫曼在1952年提出[1]。

哈夫曼编码可以有效地将资料压缩至最小,它的原理是将资料中出现频率最高的字元编码为最短的码字,而出现频率低的字元编码为较长的码字,从而显著提高了信息的保密性和容量。

二、贪心算法
贪心算法(Greedy Algorithm)是一种计算机算法,它试图找到一种满足条件的最佳解决方案,通常每一步都是做出在当前状态下最佳的选择,而不考虑将来可能发生的结果。

哈夫曼编码贪心算法是利用贪心算法来实现哈夫曼编码的。

该算法的步骤如下:
1. 首先统计出每一个字符出现的次数,并以此建立森林。

森林
中的每一棵树都用一个节点表示,每个节点的数值为字符出现的次数。

2. 从森林中挑选出两个出现次数最少的字符,将它们作为左右
子树合成一颗新的树,新树的根节点的数值为两个孩子节点的和。

3. 将新树加入森林中,并删除左右子树对应的原节点。

4. 重复上述步骤,直到森林中只剩一颗树,这颗树就是哈夫曼树。

5. 从哈夫曼树根节点出发,逐层往下搜索,左子节点编码为“0”,右子节点编码为“1”,最终得到每个字符的哈夫曼编码。

huffman编码的基本原理

huffman编码的基本原理

huffman编码的基本原理
Huffman编码是一种基于贪心算法的压缩算法,其基本原理是将出现频率较高的字符用较短的编码表示,而将出现频率较低的字符用较长的编码表示,从而达到数据压缩的目的。

具体来说,Huffman编码的过程分为两步:首先统计所有字符出现的频率,并将其构建成一个树状结构;其次,根据该树状结构为每个字符生成对应的编码。

在构建树状结构时,我们可以使用最小堆或者优先队列等数据结构来实现。

具体来说,我们可以首先将所有字符出现的频率存入堆中,然后每次从堆中选取两个频率最小的字符节点,将它们合并成一个新的节点,并将其频率设为两个节点频率之和,最后再将新节点加入堆中。

这个过程将持续到堆中只剩下一个节点为止,这个节点即为哈夫曼树的根节点。

生成编码时,从根节点出发,每次遇到左孩子将编码加上0,每次遇到右孩子将编码加上1。

最终,每个字符都可以通过该树状结构得到唯一的编码。

编码的长度即为该字符在哈夫曼树中所处的深度。

Huffman编码的优点是可以根据数据本身的特点来生成最优编码,从而达到较好的压缩效果。

其缺点是需要占用额外的存储空间来存储哈夫曼树,同时编码和解码的速度较慢。

- 1 -。

贪心算法实现Huffman编码

贪心算法实现Huffman编码

算法分析与设计实验报告第次实验附录:完整代码#include <iostream>#include <string>#include<stdio.h>#include <time.h>#include <iomanip>#include <vector>#include<algorithm>using namespace std;class Huffman{public:char elementChar;//节点元素int weight;//权重char s;//哈夫曼编码Huffman* parent;//父节点Huffman* leftChild;//左孩子Huffman* rightChild;//右孩子public:Huffman();Huffman(char a, int weight);bool operator < (const Huffman &m)const { return weight < m.weight;} };Huffman::Huffman(){this->s = ' ';this->elementChar = '*';//非叶子节点this->parent = this->leftChild = this->rightChild = NULL;}Huffman::Huffman(char a, int weight):elementChar(a),weight(weight) {this->s = ' ';this->elementChar = '*';//非叶子节点this->parent = this->leftChild = this->rightChild = NULL;}//递归输出哈夫曼值void huffmanCode(Huffman & h){if(h.leftChild == NULL && h.rightChild == NULL){//如果是叶子节点,输出器哈夫曼编码string s;Huffman temp = h;while(temp.parent != NULL){s = temp.s + s;temp = *temp.parent;}cout << h.elementChar << "的哈夫曼编码是:" << s << endl; return;}//左孩子huffmanCode(*h.leftChild);//右孩子huffmanCode(*h.rightChild);}int main(){int l,p=0;double q=0.0;clock_t start,end,over;start=clock();end=clock();over=end-start;start=clock();string huffmanStr;cout << "请输入一串字符序列:" << endl;cin >> huffmanStr;//得到字符串信息int i=0,j,n,m[100],h,k=0;char cha[100];n = huffmanStr.length();cout << "字符串总共有字符" << n << "个" << endl;for(int i = 0; i < n; i++){j = 0; h = 0;while(huffmanStr[i] != huffmanStr[j])j++;if(j == i){cha[k] = huffmanStr[i];cout << "字符" << cha[k] << "出现";}//如果j !=i 则略过此次循环elsecontinue;for(j = i; j < n; j++){if(huffmanStr[i] == huffmanStr[j])h++;}cout << h << "次" << endl;m[k] = h;k++;}//哈夫曼编码Huffman huffmanTemp;vector < Huffman > huffmanQueue;//初始化队列for(int i = 0; i < k; i++){huffmanTemp.elementChar = cha[i];huffmanTemp.weight = m[i];huffmanQueue.push_back(huffmanTemp);}//得到哈夫曼树所有节点int huffmanQueue_index = 0;sort(huffmanQueue.begin(), huffmanQueue.end());while(huffmanQueue.size() < 2 * k - 1){//合成最小两个节点的父节点huffmanTemp.weight = huffmanQueue[huffmanQueue_index].weight + huffmanQueue[huffmanQueue_index + 1].weight;huffmanQueue[huffmanQueue_index].s = '0';huffmanQueue[huffmanQueue_index + 1].s = '1';huffmanTemp.elementChar = '*';//将父节点加入队列huffmanQueue.push_back(huffmanTemp);sort(huffmanQueue.begin(), huffmanQueue.end());huffmanQueue_index += 2;}//把所有节点构造成哈夫曼树int step = 0;//步长while(step + 2 < 2 * k){for(int j = step + 1; j <= huffmanQueue.size(); j++){if(huffmanQueue[j].elementChar == '*' && huffmanQueue[j].leftChild == NULL && (huffmanQueue[j].weight == huffmanQueue[step].weight + huffmanQueue[step+1].weight)){huffmanQueue[j].leftChild = &huffmanQueue[step];huffmanQueue[j].rightChild = &huffmanQueue[step+1];huffmanQueue[step].parent = huffmanQueue[step+1].parent = &huffmanQueue[j]; break;}}step += 2;}//序列最后一个元素,即哈弗曼树最顶端的节点huffmanTemp = huffmanQueue.back();huffmanCode(huffmanTemp);for(l=0;l<1000000000;l++)p=p+l;end=clock();printf("The time is %6.3f",(double)(end-start-over)/CLK_TCK);return 0;}。

哈夫曼编码的贪心算法时间复杂度

哈夫曼编码的贪心算法时间复杂度

哈夫曼编码的贪心算法时间复杂度哈夫曼编码的贪心算法时间复杂度在信息技术领域中,哈夫曼编码是一种被广泛应用的数据压缩技术,它利用了贪心算法的思想来设计。

贪心算法是一种在每一步都选择当前状态下最优解的方法,从而希望通过一系列局部最优解达到全局最优解。

在哈夫曼编码中,这个想法被巧妙地运用,从而有效地实现了数据的高效压缩和解压缩。

哈夫曼编码是由大名鼎鼎的大卫·哈夫曼(David A. Huffman)在1952年提出的,它通过将频率最高的字符赋予最短的编码,最低的字符赋予最长的编码,从而实现了对数据的高效压缩。

这种编码技术在通信领域、存储领域和计算机科学领域都有着广泛的应用,是一种非常重要的数据处理技术。

在哈夫曼编码的实现过程中,贪心算法的时间复杂度是非常重要的。

时间复杂度是用来衡量算法所需时间的数量级,通常使用大O记号(O(n))来表示。

对于哈夫曼编码的贪心算法来说,其时间复杂度主要取决于以下几个步骤:1. 需要对数据进行统计,以获取每个字符出现的频率。

这个步骤的时间复杂度是O(n),其中n表示字符的数量。

在实际应用中,这个步骤通常由哈希表或统计排序来实现,因此时间复杂度可以控制在O(n)的数量级。

2. 接下来,需要构建哈夫曼树。

哈夫曼树是一种特殊的二叉树,它的构建过程需要将频率最低的两个节点合并成一个新的节点,然后再对新节点进行排序。

这个过程会持续n-1次,直到所有节点都被合并到一棵树中。

构建哈夫曼树的时间复杂度是O(nlogn),其中n表示字符的数量。

3. 根据哈夫曼树生成每个字符的编码。

这个过程涉及到对哈夫曼树进行遍历,并记录下每个字符对应的编码。

由于哈夫曼树的特性,每个字符的编码可以通过从根节点到叶子节点的路径来得到。

这个步骤的时间复杂度是O(n),因为对于每个字符都需要进行一次遍历。

哈夫曼编码的贪心算法时间复杂度主要由构建哈夫曼树的步骤决定,为O(nlogn)。

这意味着在实际应用中,哈夫曼编码的运行时间随着字符数量的增加而增加,并且增长速度为nlogn的数量级。

  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

0023算法笔记——【贪心算法】哈夫曼编码问题1、问题描述哈夫曼编码是广泛地用于数据文件压缩的十分有效的编码方法。

其压缩率通常在20%~90%之间。

哈夫曼编码算法用字符在文件中出现的频率表来建立一个用0,1串表示各字符的最优表示方式。

一个包含100,000个字符的文件,各字符出现频率不同,如下表所示。

有多种方式表示文件中的信息,若用0,1码表示字符的方法,即每个字符用唯一的一个0,1串表示。

若采用定长编码表示,则需要3位表示一个字符,整个文件编码需要300,000位;若采用变长编码表示,给频率高的字符较短的编码;频率低的字符较长的编码,达到整体编码减少的目的,则整个文件编码需要(45×1+13×3+12×3+16×3+9×4+5×4)×1000=224,000位,由此可见,变长码比定长码方案好,总码长减小约25%。

前缀码:对每一个字符规定一个0,1串作为其代码,并要求任一字符的代码都不是其他字符代码的前缀。

这种编码称为前缀码。

编码的前缀性质可以使译码方法非常简单;例如001011101可以唯一的分解为0,0,101,1101,因而其译码为aabe。

译码过程需要方便的取出编码的前缀,因此需要表示前缀码的合适的数据结构。

为此,可以用二叉树作为前缀码的数据结构:树叶表示给定字符;从树根到树叶的路径当作该字符的前缀码;代码中每一位的0或1分别作为指示某节点到左儿子或右儿子的“路标”。

从上图可以看出,表示最优前缀码的二叉树总是一棵完全二叉树,即树中任意节点都有2个儿子。

图a表示定长编码方案不是最优的,其编码的二叉树不是一棵完全二叉树。

在一般情况下,若C是编码字符集,表示其最优前缀码的二叉树中恰有|C|个叶子。

每个叶子对应于字符集中的一个字符,该二叉树有|C|-1个内部节点。

给定编码字符集C及频率分布f,即C中任一字符c以频率f(c)在数据文件中出现。

C的一个前缀码编码方案对应于一棵二叉树T。

字符c 在树T中的深度记为d T(c)。

d T(c)也是字符c的前缀码长。

则平均码长定义为:使平均码长达到最小的前缀码编码方案称为C 的最优前缀码。

2、构造哈弗曼编码哈夫曼提出构造最优前缀码的贪心算法,由此产生的编码方案称为哈夫曼编码。

其构造步骤如下:(1)哈夫曼算法以自底向上的方式构造表示最优前缀码的二叉树T。

(2)算法以|C|个叶结点开始,执行|C|-1次的“合并”运算后产生最终所要求的树T。

(3)假设编码字符集中每一字符c的频率是f(c)。

以f为键值的优先队列Q用在贪心选择时有效地确定算法当前要合并的2棵具有最小频率的树。

一旦2棵具有最小频率的树合并后,产生一棵新的树,其频率为合并的2棵树的频率之和,并将新树插入优先队列Q。

经过n-1次的合并后,优先队列中只剩下一棵树,即所要求的树T。

构造过程如图所示:具体代码实现如下:(1)4d4.cpp,程序主文件[cpp]view plain copy1.//4d4 贪心算法哈夫曼算法2.#include "stdafx.h"3.#include "BinaryTree.h"4.#include "MinHeap.h"5.#include <iostream>ing namespace std。

7.8.const int N = 6。

9.10.template<class Type> class Huffman。

11.12.template<class Type>13.BinaryTree<int> HuffmanTree(Type f[],int n)。

14.15.template<class Type>16.class Huffman17.{18.friend BinaryTree<int> HuffmanTree(Type[],int)。

19.public:20. operator Type() const21. {22.return weight。

23. }24.//private:25. BinaryTree<int> tree。

26. Type weight。

27.}。

28.29.int main()30.{31.char c[] = {'0','a','b','c','d','e','f'}。

32.int f[] = {0,45,13,12,16,9,5}。

//下标从1开始33. BinaryTree<int> t = HuffmanTree(f,N)。

34.35. cout<<"各字符出现的对应频率分别为:"<<endl。

36.for(int i=1。

i<=N。

i++)37. {38. cout<<c[i]<<":"<<f[i]<<" "。

39. }40. cout<<endl。

41.42. cout<<"生成二叉树的前序遍历结果为:"<<endl。

43. t.Pre_Order()。

44. cout<<endl。

45.46. cout<<"生成二叉树的中序遍历结果为:"<<endl。

47. t.In_Order()。

48. cout<<endl。

49.50. t.DestroyTree()。

51.return 0。

52.}53.54.template<class Type>55.BinaryTree<int> HuffmanTree(Type f[],int n)56.{57.//生成单节点树58. Huffman<Type> *w = new Huffman<Type>[n+1]。

59. BinaryTree<int> z,zero。

60.61.for(int i=1。

i<=n。

i++)62. {63. z.MakeTree(i,zero,zero)。

64. w[i].weight = f[i]。

65. w[i].tree = z。

66. }67.68.//建优先队列69. MinHeap<Huffman<Type>> Q(n)。

70.for(int i=1。

i<=n。

i++) Q.Insert(w[i])。

71.72.//反复合并最小频率树73. Huffman<Type> x,y。

74.for(int i=1。

i<n。

i++)75. {76. x = Q.RemoveMin()。

77. y = Q.RemoveMin()。

78. z.MakeTree(0,x.tree,y.tree)。

79. x.weight += y.weight。

80. x.tree = z。

81. Q.Insert(x)。

82. }83.84. x = Q.RemoveMin()。

85.86.delete[] w。

87.88.return x.tree。

89.}(2)BinaryTree.h 二叉树实现[cpp]view plain copy1.#include<iostream>ing namespace std。

3.4.template<class T>5.struct BTNode6.{7. T data。

8. BTNode<T> *lChild,*rChild。

9.10. BTNode()11. {12. lChild=rChild=NULL。

13. }14.15. BTNode(const T &val,BTNode<T> *Childl=NULL,BTNode<T> *Childr=NULL)16. {17. data=val。

18. lChild=Childl。

19. rChild=Childr。

20. }21.22. BTNode<T>* CopyTree()23. {24. BTNode<T> *nl,*nr,*nn。

25.26.if(&data==NULL)27.return NULL。

28.29. nl=lChild->CopyTree()。

30. nr=rChild->CopyTree()。

31.32. nn=new BTNode<T>(data,nl,nr)。

33.return nn。

34. }35.}。

36.37.38.template<class T>39.class BinaryTree40.{41.public:42. BTNode<T> *root。

43. BinaryTree()。

44. ~BinaryTree()。

45.46.void Pre_Order()。

47.void In_Order()。

48.void Post_Order()。

49.50.int TreeHeight()const。

51.int TreeNodeCount()const。

52.53.void DestroyTree()。

54.void MakeTree(T pData,BinaryTree<T> leftTree,BinaryTree<T> rightTree)。

55.void Change(BTNode<T> *r)。

56.57.private:58.void Destroy(BTNode<T> *&r)。

59.void PreOrder(BTNode<T> *r)。

60.void InOrder(BTNode<T> *r)。

61.void PostOrder(BTNode<T> *r)。

62.63.int Height(const BTNode<T> *r)const。

64.int NodeCount(const BTNode<T> *r)const。

65.}。

66.67.template<class T>68.BinaryTree<T>::BinaryTree()69.{70. root=NULL。

相关文档
最新文档