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

合集下载

贪心算法构造哈夫曼树

贪心算法构造哈夫曼树

软件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);}}。

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

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

实验四 哈夫曼编码的贪心算法设计(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次合并后,成为最终的一棵哈夫曼树。

哈夫曼编码贪心算法

哈夫曼编码贪心算法

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

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

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

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

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

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

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

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

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

5. 从哈夫曼树根节点出发,逐层往下搜索,左子节点编码为“0”,右子节点编码为“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)采用一种迭代的方式,逐步将初始解进行优化3)每一步都是基于当前状态的最优选择来进行优化4)直到无法再进行优化,得到问题的最优解由于贪心算法每一步都要选择局部最优解,因此贪心算法通常具有高效性。

然而,贪心算法并不适用于所有问题,其结果不一定是全局最优解。

因此,在使用贪心算法时需要注意问题的特性和约束条件,以免得到错误的结果。

2. 适用情况贪心算法通常适用于满足以下条件的问题:1)问题的最优解满足“最优子结构”性质:即问题的最优解包含了其子问题的最优解2)问题的求解过程具有“贪心选择性”:即每一步都选择当前状态下的最优解,并不需要考虑未来的后果3)问题的约束条件可以通过局部最优选择满足全局最优解:即问题的解空间中存在一些局部最优解,可以通过一系列的局部最优解构建全局最优解在实际应用中,贪心算法通常用于求解最优化问题,如最小生成树、最短路径、任务调度等问题。

由于贪心算法的高效性,它通常能够在较短的时间内得到较为接近最优解的结果。

然而,贪心算法并不适用于所有问题,对于一些问题,贪心算法将得到错误的结果。

因此,在使用贪心算法时需要谨慎选择问题类型和约束条件,以避免错误的结果。

3. 贪心算法实例在下面的部分,我们将介绍一些常见的贪心算法实例,包括背包问题、活动安排问题、霍夫曼编码等。

3.1 背包问题背包问题是一个经典的优化问题,它包括0-1背包问题、分数背包问题等多种类型。

在0-1背包问题中,给定n种物品和一个容量为C的背包,每种物品i的重量为w[i],价值为v[i],求在不超过背包容量的情况下,如何选择物品放入背包,可以使得背包中的总价值最大。

对于0-1背包问题,贪心算法通常不能得到最优解。

然而,在分数背包问题中,贪心算法通常可以得到近似的最优解。

贪心算法-哈弗曼编码、汽车加油问题

贪心算法-哈弗曼编码、汽车加油问题

1.问题描述哈夫曼编码(贪心策略)——要求给出算法思想、编码程序和译码程序,对样本数据“哈夫曼编码实验数据.dat”,要求提交符号的具体编码以及编码后的文件。

2.求解问题的贪心算法描述压缩数据由以下步骤组成:a)检查字符在数据中的出现频率。

b)构建哈夫曼树。

c)创建哈夫曼编码表。

d)生成压缩后结果,由一个文件头和压缩后的数据组成。

3.算法实现的关键技巧1.最小堆两种基本操作:插入新元素,抽取最小元素。

(1)插入新元素:把该元素放在二叉树的末端,然后从该新元素开始,向根节点方向进行交换,直到它到达最终位置。

(2)抽取最小元素:把根节点取走。

然后把二叉树的末端节点放到根节点上,然而把该节点向子结点反复交换,直到它到达最终位置。

2. 构建哈夫曼树:a)把所有出现的字符作为一个节点(单节点树),把这些树组装成最小堆;b)从该优先级队列中连续抽取两个频率最小的树分别作为左子树,右子树,将他们合并成一棵树(频率=两棵树频率之和),然后把这棵树插回队列中。

c)重复步骤b,每次合并都将使优先级队列的尺寸减小1,直到最后队列中只剩一棵树为止,就是我们需要的哈夫曼树。

3.压缩数据: 遍历输入的文本,对每个字符,根据编码表依次把当前字符的编码写入到编码结果中去。

File Header(文件头):unsigned int size; 被编码的文本长度(字符数);unsigned char freqs[ NUM_CHARS ]; 字符频率表compressed; (Bits: 压缩后的数据);4.贪心选择性质&最优子结构性质证明:即证明最优前缀码问题具有弹性选择性质和最优子结构性质.(1)贪心选择性质设C是编码字符集,C中字符c的频率为f(c)。

又设x,y是C中具有最小频率的两个字符,则存在C的最优前缀码使x,y具有相同码长且仅最后一位编码不同。

证明:设二叉树T表示C的任意一个最优前缀码。

对C左适当修改得到T“,使得在新树中,x和y是最深叶子且为兄弟。

哈夫曼编码算法思想总结

哈夫曼编码算法思想总结

哈夫曼编码算法思想总结哈夫曼编码算法是一种基于字符出现频率的编码方式,其主要思想是通过建立一颗哈夫曼树来实现最优的编码方案。

哈夫曼编码算法在数据压缩领域广泛应用,在传输和存储数据时可以减小数据的体积,提高数据的传输效率。

哈夫曼编码算法的核心思想是根据字符出现的频率,对字符进行编码,使得出现频率较高的字符获得较短的编码,出现频率较低的字符获得较长的编码。

这样可以有效地减小编码后的数据长度,并且在解码的时候不会存在二义性。

哈夫曼编码算法的实现过程主要分为三个步骤:建立字符频率统计表、构造哈夫曼树、生成编码表。

首先,需要统计字符出现的频率。

扫描待编码的文本,记录每个字符出现的次数。

可以使用哈希表或者数组来实现字符与频率之间的映射关系。

其次,根据字符频率构造哈夫曼树。

通过扫描字符频率统计表,将每个字符及其频率作为叶子节点构建一颗树。

每个节点的权值为其子树中所有叶子节点的权值之和。

不断地选择权值最小的两个节点,将它们合并为一个新的节点,并更新权值。

重复这个过程,直到只剩下一个节点,即为哈夫曼树的根节点。

最后,生成编码表。

从哈夫曼树的根节点开始,向下遍历每个节点,标记左分支为0,右分支为1。

这样可以得到每个字符对应的二进制编码。

将字符及其对应的编码存储在编码表中,供解码时使用。

利用生成的编码表,可以将待编码的文本转化为哈夫曼编码。

每个字符根据编码表中的对应编码进行替换,得到编码后的二进制数据。

由于频率高的字符对应的编码较短,所以编码后的数据长度相比原始数据大大减小。

在解码时,根据哈夫曼树的结构和编码表的内容,可以还原出原始的文本数据。

从根节点开始,根据编码依次遍历哈夫曼树,根据0或者1选择左分支或者右分支。

当到达叶子节点时,找到对应的字符,并将其输出到解码后的文本中。

哈夫曼编码算法的优点是编码后的数据长度相对较短,可以实现高效的数据压缩。

在数据传输和存储过程中,可以减少带宽的占用,提高传输效率。

同时,哈夫曼编码算法的解码过程也相对简单,可以快速还原出原始的文本数据。

列举贪心算法求解的经典问题

列举贪心算法求解的经典问题

列举贪心算法求解的经典问题
贪心算法是一种基于贪婪策略的算法,它在每一步选择中都采取当前状态下的最优决策,以期望能够得到全局最优解。

以下是一些常见的经典问题,可以通过贪心算法来求解:
1. 零钱兑换问题(Coin Change Problem):给定一些不同面额的硬币和一个要兑换的金额,找出使用最少的硬币数量来凑成该金额的方法。

2. 区间调度问题(Interval Scheduling Problem):给定一组区间,每个区间都有开始时间和结束时间,目标是找到最大的不重叠区间子集。

3. 活动选择问题(Activity Selection Problem):给定一组活动,每个活动都有开始时间和结束时间,目标是安排尽可能多的活动,使得它们不重叠。

4. 霍夫曼编码(Huffman Coding):给定一组字符及其出现频率,通过构建霍夫曼树来生成最优的编码方案,使得出现频率高的字符具有较短的编码。

5. 最小生成树(Minimum Spanning Tree):给定一个连通图,找到一个子图,使得它包含了所有的顶点且边的权重之和最小。

6. 最短路径问题(Shortest Path Problem):给定一个图和起点,找到从起点到其他顶点的最短路径,其中边的权重可以是正数、负数或零。

这些问题都可以通过贪心算法求解,但需要注意的是,贪心算法并不总是能得到全局最优解,因此在使用贪心算法时要仔细分析问题的性质,确保贪心策略的正确性。

哈夫曼解码

哈夫曼解码

哈夫曼解码
《哈夫曼编码》是由德国工程师和数学家卡尔哈夫曼发明的一种著名的无损数据压缩编码方式。

它是以数据文件中出现次数最多的字符编码为主,使用更短的位数,用以减少文件的大小。

哈夫曼编码的作用是能够将原本的编码数据进行压缩,使得例如传输效率、存储空间等受益。

哈夫曼编码是一种先进的编码方式,根据哈夫曼定理,它能够达到最小最优效果,使得文件可以被有效地压缩,从而节省更多的存储空间和传输时间。

而且哈夫曼编码是可逆的,因此文件接收者可以快速地将接收的文件还原成原本的格式。

哈夫曼编码是一种动态程序,它能够根据数据文件中出现次数最多的字符采用编码,以更短的位数来存储,从而压缩文件的大小。

它通过根据文件中字符的出现频率,利用贪心算法和二叉树原理,通过动态程序来构建哈夫曼树。

在构建哈夫曼树的同时,它也以左右孩子的方式编码,左孩子用0表示,右孩子用1表示。

哈夫曼编码能够发挥出优质效果,因此在日常使用领域得到了广泛应用。

例如GIF图片文件就采用了哈夫曼编码,它能够根据文件中数据的出现概率编码,从而节省大量的存储空间,使得文件体积变得更小。

此外,哈夫曼编码还可以用于视频文件的编码,以提高传输的流畅度,减少在线视频的卡顿状况。

哈夫曼编码是一种非常实用的编码方式,它利用了数据统计和逻辑算法,使得文件体积可以达到最小状态,从而节省更多的存储空间
和传输时间。

哈夫曼编码是一种普及的无损数据压缩编码,在计算机领域及其他电子领域得到了大量应用,为人们的生活提供了极大的便利。

huffman编码例题

huffman编码例题

huffman编码例题Huffman编码是一种流行的数据压缩技术,也是许多压缩软件中使用的算法。

它可以通过建立权值树来生成对应的固定长度编码,称为Huffman编码。

在本文中,我们将介绍Huffman编码的原理以及一个具体的例题。

一、Huffman编码原理Huffman编码的实现原理是基于贪心算法。

它的目的是将出现频率较高的字符用较短的编码表示,而将出现频率较低的字符用较长的编码表示,以达到压缩数据的目的。

具体实现步骤如下:1.统计每个字符出现的频率。

2.建立哈夫曼树,每个节点代表一个字符,节点的权重为字符出现的频率。

3.对哈夫曼树进行遍历,为每个字符生成对应的Huffman编码。

4.将字符串中的每个字符替换成对应的Huffman编码。

二、Huffman编码例题假设有一个字符串"hello world",请编写程序进行Huffman编码和解码。

1统计每个字符出现的频率 h:1 e:1 l:3 o:2 w:1 r:1 d:12建立哈夫曼树从频率最小的字符开始,依次合并至根节点,得到以下哈夫曼树:11/ \5 6/ \ / \2 3 3 3/ / \h r d3生成Huffman编码从根节点开始遍历哈夫曼树,向左走为"0",向右走为"1",生成以下Huffman编码: h: 100 e: 1010 l: 00 o: 1011 w: 1100 r: 1101 d: 11104进行编码和解码使用步骤三中的编码表,将字符串"hello world"分别编码为: 101000001111001011111001101000011010111011100解码时,从根节点开始依次读取编码,遇到"0"则向左走,遇到"1"则向右走,直到读取完整个编码,找到对应的字符。

将编码解析后得到的二进制数转成对应的字符,即可得到原字符串"hello world"。

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

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

哈夫曼编码是一种广泛应用于数据压缩领域的编码方式,而哈夫曼编码的贪心算法是实现这一编码方式的重要方法之一。

在本文中,我将深入探讨哈夫曼编码及其贪心算法的时间复杂度,并就此展开全面评估。

让我们简要回顾一下哈夫曼编码的基本概念。

哈夫曼编码是一种变长编码方式,通过将出现频率高的字符用较短的编码表示,而将出现频率低的字符用较长的编码表示,从而实现数据的有效压缩。

在这一编码方式中,贪心算法被广泛应用于构建哈夫曼树,以实现最优编码方案的选择。

那么,接下来我们将重点关注哈夫曼编码的贪心算法时间复杂度。

哈夫曼编码的贪心算法的时间复杂度主要取决于两个方面:构建哈夫曼树的时间复杂度和编码字符串的时间复杂度。

让我们来看构建哈夫曼树的时间复杂度。

在哈夫曼编码的贪心算法中,构建哈夫曼树的时间复杂度主要取决于构建最小堆(或最大堆)以及合并节点的操作。

在构建最小堆的过程中,需要对所有字符按照其频率进行排序,并将其依次插入最小堆中,这一操作的时间复杂度为O(nlogn)。

而在合并节点的过程中,需要不断从最小堆中取出两个频率最小的节点,并将其合并为一个新节点,然后再将新节点插入最小堆中,这一操作需要进行n-1次,所以合并节点的时间复杂度为O(nlogn)。

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

我们来看编码字符串的时间复杂度。

在使用哈夫曼编码对字符串进行编码时,需要根据构建好的哈夫曼树来进行编码,这一过程的时间复杂度主要取决于字符串的长度和哈夫曼树的深度。

由于哈夫曼树是一个二叉树,所以在最坏情况下,编码一个字符的时间复杂度为O(n),其中n为哈夫曼树的深度。

编码字符串的时间复杂度为O(kn),其中k 为字符串的长度。

哈夫曼编码的贪心算法的时间复杂度主要包括构建哈夫曼树的时间复杂度和编码字符串的时间复杂度,其中构建哈夫曼树的时间复杂度为O(nlogn),编码字符串的时间复杂度为O(kn)。

哈夫曼编码的贪心算法的时间复杂度为O(nlogn+kn)。

贪心法求哈夫曼编码

贪心法求哈夫曼编码

实验题目:设需要编码的字符集为{d1, d2, …, dn},它们出现的频率为{w1, w2, …, wn},应用哈夫曼树构造最短的不等长编码方案。

实验目的:(1)了解前缀编码的概念,理解数据压缩的基本方法;(2)掌握最优子结构性质的证明方法;(3)掌握贪心法的设计思想并能熟练运用。

实验内容:实验代码:#include <iostream>using namespace std;/** 霍夫曼树结构*/class HuffmanTree{public:unsigned int Weight, Parent, lChild, rChild;};typedef char **HuffmanCode;/** 从结点集合中选出权值最小的两个结点* 将值分别赋给s1和s2*/void Select(HuffmanTree* HT,int Count,int *s2,int *s1){unsigned int temp1=0;unsigned int temp2=0;unsigned int temp3;for(int i=1;i<=Count;i++){if(HT[i].Parent==0){if(temp1==0){temp1=HT[i].Weight;(*s1)=i;}else{if(temp2==0){temp2=HT[i].Weight;(*s2)=i;if(temp2<temp1){temp3=temp2;temp2=temp1;temp1=temp3;temp3=(*s2);(*s2)=(*s1);(*s1)=temp3;}}else{if(HT[i].Weight<temp1){temp2=temp1;temp1=HT[i].Weight;(*s2)=(*s1);(*s1)=i;}if(HT[i].Weight>temp1&&HT[i].Weight<temp2){temp2=HT[i].Weight;(*s2)=i;}}}}}}/** 霍夫曼编码函数*/void HuffmanCoding(HuffmanTree * HT,HuffmanCode * HC,int *Weight,int Count){int i;int s1,s2;int TotalLength;char* cd;unsigned int c;unsigned int f;int start;if(Count<=1) return;TotalLength=Count*2-1;HT = new HuffmanTree[(TotalLength+1)*sizeof(HuffmanTree)];for(i=1;i<=Count;i++){HT[i].Parent=0;HT[i].rChild=0;HT[i].lChild=0;HT[i].Weight=(*Weight);Weight++;}for(i=Count+1;i<=TotalLength;i++){HT[i].Weight=0;HT[i].Parent=0;HT[i].lChild=0;HT[i].rChild=0;}//建造霍夫曼树for(i=Count+1;i<=TotalLength;++i){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;}//输出霍夫曼编码(*HC)=(HuffmanCode)malloc((Count+1)*sizeof(char*));cd = new char[Count*sizeof(char)];cd[Count-1]='\0';for(i=1;i<=Count;++i){start=Count-1;for(c = i,f = HT[i].Parent; f != 0; c = f, f = HT[f].Parent){if(HT[f].lChild == c)cd[--start]='0';elsecd[--start]='1';(*HC)[i] = new char [(Count-start)*sizeof(char)];strcpy((*HC)[i], &cd[start]);}}delete [] HT;delete [] cd;}/** 在字符串中查找某个字符* 如果找到,则返回其位置*/int LookFor(char *str, char letter, int count){int i;for(i=0;i<count;i++){if(str[i]==letter) return i;}return -1;}void OutputWeight(char *Data,int Length,char **WhatLetter,int **Weight,int *Count){int i;char* Letter = new char[Length];int* LetterCount = new int[Length];int AllCount=0;int Index;int Sum=0;float Persent=0;for(i=0;i<Length;i++){if(i==0){Letter[0]=Data[i];LetterCount[0]=1;AllCount++;}else{Index=LookFor(Letter,Data[i],AllCount);if(Index==-1){Letter[AllCount]=Data[i];LetterCount[AllCount]=1;AllCount++;}else{LetterCount[Index]++;}}}for(i=0;i<AllCount;i++){Sum=Sum+LetterCount[i];}(*Weight) = new int[AllCount];(*WhatLetter) = new char[AllCount];for(i=0;i<AllCount;i++){Persent=(float)LetterCount[i]/(float)Sum;(*Weight)[i]=(int)(100*Persent);(*WhatLetter)[i]=Letter[i];}(*Count)=AllCount;delete [] Letter;delete [] LetterCount;}int main(){HuffmanTree * HT = NULL;HuffmanCode HC;char Data[100];char *WhatLetter;int *Weight;int Count;cout<<"请输入一行文本数据:"<<endl;cin>>Data;cout<<endl;OutputWeight(Data,strlen(Data),&WhatLetter,&Weight,&Count); HuffmanCoding(HT, &HC, Weight, Count);cout<<"字符出现频率编码结果"<<endl;for(int i = 0; i<Count; i++){cout<<WhatLetter[i]<<" ";cout<<Weight[i]<<"%\t";cout<<HC[i+1]<<endl;}cout<<endl;system("pause");return 0;}实验结果截图:哈夫曼算法描述:(1)初始化:将初始森林的各根结点(双亲)和左右孩子指针置为-1;(2)输入叶子权:叶子在向量T的前n个分量中,构成初始森林的n个根结点;(3)合并:对森林中的树进行n-1次合并,共产生n-1个新结点,依次放入向量T的第i 个分量(n<=i<=m-1)中,每次合并的步骤是:a、在当前森林的所有结点中,选取具有最小权值和次小权值的两个结点,分别用p1和p2记住这两个根节点在向量T中的下标;b、将根为T[p1]和T[p2]的两棵树合并,使其成为新结点T[i]的左右孩子,得到一棵以新结点T[i]为根的二叉树若取pi为叶结点的权,取编码长度li为叶结点的路径长度,则∑ pi ⨯li最小的问题就是带权路径长度最小的哈夫曼树的构造问题。

第4章贪心算法(002)

第4章贪心算法(002)
第4章贪心算法
贪心算法基本思想
找零钱问题
顾名思义,贪心算法总是作出在当前看来最好的选 择。也就是说贪心算法并不从整体最优考虑,它所 作出的选择只是在某种意义上的局部最优选择。当 然,我们希望贪心算法得到的最终结果也是整体最 优的。虽然贪心算法不能对所有问题都得到整体最 优解,但对许多问题它能产生整体最优解。如单源 最短路径问题,最小生成树问题等。在一些情况下 ,即使贪心算法不能得到整体最优解,其最终结果 却是最优解的很好近似。
实际上也是如此,动态规划算法的确可以有效地 解0-1背包问题。
4.3 最优装载
【问题描述】有一批集装箱要装上一艘载重量为c的轮 船。其中集装箱i的重量为Wi。最优装载问题要求确 定在装载体积不受限制的情况下,将尽可能多的集 装箱装上轮船。
最优装载问题可用贪心算法求解。采用重量最轻者
先装的贪心选择策略,可产生最优装载问题的最优
算法的计算过程如右图
所示。图中每行相应于
算法的一次迭代。阴影
长条表示的活动是已选
入集合A的活动,而空白
长条表示的活动是当前
正在检查相容性的活动。 若被检查的活动i的开始 时间Si小于最近选择的 活动j的结束时间fi,则 不选择活动i,否则选择 活动i加入集合A中。
4.1 活动安排问题
4.1 活动安排问题
解。该问题可形式化描述为
n
max xi i 1
n
wi xi c
i 1
xi {0,1},1 i n
其中变量xi=0表示不装入集装箱i,xi=1表示装入集 装箱i。
4.3 最优装载
1.算法描述
template<class Type> void Loading(int x[], Type w[], Type c, int n) {

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

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

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

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

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

核心源代码#include <>#include <>#include <>typedef struct{unsigned int weight; arent==0){min=i;break;}}for(i=1; i<=n; i++) ∑=j i k k a{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;}eight=w[i];(*ht)[i].LChild=0;(*ht)[i].parent=0;(*ht)[i].RChild=0;}for(i=n+1; i<=m; i++) eight=0;(*ht)[i].LChild=0;(*ht)[i].parent=0;(*ht)[i].RChild=0;}printf("\n哈夫曼树为: \n");for(i=n+1; i<=m; i++) arent=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");}arent; p!=0; c=p,p=(*ht)[p].parent) Child==c){cd[--start]='1'; eight,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次合并后,成为最终的一棵哈夫曼树。

哈夫曼树及哈夫曼编码的算法实现

哈夫曼树及哈夫曼编码的算法实现

哈夫曼树及哈夫曼编码的算法实现1. 哈夫曼树的概念和原理哈夫曼树是一种带权路径长度最短的树,也称最优二叉树。

它是由美国数学家大卫・哈夫曼发明的,用于数据压缩编码中。

哈夫曼树的构建原理是通过贪心算法,将权重较小的节点不断合并,直到所有节点都合并成为一个根节点,形成一棵树。

这样构建的哈夫曼树能够实现数据的高效压缩和解压缩。

2. 哈夫曼编码的概念和作用哈夫曼编码是一种可变长度编码,它根据字符在文本中出现的频率来进行编码,频率越高的字符编码越短,频率越低的字符编码越长。

这种编码方式能够实现数据的高效压缩,减小数据的存储空间,提高数据传输的效率。

3. 哈夫曼树和编码的算法实现在实现哈夫曼树和编码的算法过程中,首先需要统计文本中每个字符出现的频率,并根据频率构建哈夫曼树。

根据哈夫曼树的结构,确定每个字符的哈夫曼编码。

利用哈夫曼编码对文本进行压缩和解压缩。

4. 个人观点和理解哈夫曼树及哈夫曼编码算法是一种非常有效的数据压缩和编码方式,能够在保证数据完整性的前提下,减小数据的存储和传输成本。

在实际应用中,哈夫曼编码被广泛应用于通信领域、数据存储领域以及图像压缩等领域。

通过深入理解和掌握哈夫曼树及哈夫曼编码的算法实现,可以为我们在实际工作中处理大量数据时提供便利和效率。

5. 总结与回顾通过本篇文章的详细讲解,我深入了解了哈夫曼树及哈夫曼编码的算法原理和实现方式,对其在数据处理中的重要性有了更深刻的认识。

掌握了哈夫曼树及哈夫曼编码的算法实现,将为我未来的工作和学习提供更多的帮助和启发。

根据您提供的主题,本篇文章按照从简到繁、由浅入深的方式探讨了哈夫曼树及哈夫曼编码的算法实现。

文章共计超过3000字,深入剖析了哈夫曼树和编码的原理、实现方式以及应用场景,并结合个人观点进行了阐述。

希望本篇文章能够满足您的需求,如有任何修改意见或其他要求,欢迎随时告知。

哈夫曼树和哈夫曼编码是一种十分重要的数据压缩和编码方式,它们在实际的数据处理和传输中发挥着非常重要的作用。

哈夫曼编码 贪心算法

哈夫曼编码 贪心算法

淮海工学院计算机工程学院实验报告书课程名:《算法分析与设计》题目:实验3 贪心算法哈夫曼编码班级:软件081班学号:**********名:***实验3 贪心算法实验目的和要求(1)了解前缀编码的概念,理解数据压缩的基本方法; (2)掌握最优子结构性质的证明方法; (3)掌握贪心法的设计思想并能熟练运用 (4)证明哈夫曼树满足最优子结构性质; (5)设计贪心算法求解哈夫曼编码方案; (6)设计测试数据,写出程序文档。

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

实验环境Turbo C 或VC++ 实验学时2学时,必做实验 数据结构与算法typedef char *HuffmanCode; //动态分配数组,存储哈夫曼编码typedef struct {unsigned int weight; //用来存放各个结点的权值unsigned int parent,LChild,RChild; //指向双亲、孩子结点的指针 } HTNode, *HuffmanTree; //动态分配数组,存储哈夫曼树核心源代码#include <stdio.h> #include <stdlib.h> #include <string.h>typedef struct {unsigned int weight; //用来存放各个结点的权值unsigned int parent,LChild,RChild; //指向双亲、孩子结点的指针 } HTNode, *HuffmanTree; //动态分配数组,存储哈夫曼树typedef char *HuffmanCode; //动态分配数组,存储哈夫曼编码//选择两个parent 为0,且weight 最小的结点s1和s2 void Select(HuffmanTree *ht,int n,int *s1,int *s2)∑=ji k k aint 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、s2Select(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);}实验结果字符 1 2 3 4 5 6 7 8 平均码长权值0.32 0.16 0.07 0.22 0.23哈夫曼编码00 010 011 11 10 2.23等长编码000 001 010 011 100 3.00权值0.18 0.20 0.04 0.13 0.16 0.29哈夫曼编码11 10 0001 0000 001 01 2.5等长编码000 001 010 011 100 101 3.00权值0.17 0.8 0.23 0.04 0.35 0.28 0.01哈夫曼编码010 0110 11 01110 00 10 01111 2.8等长编码000 001 010 011 100 101 110 3.00权值0.27 0.09 0.03 0.12 0.05 0.24 0.16 0.04哈夫曼编码01 111 00101 110 0011 10 000 00100 2.68等长编码000 001 010 011 100 101 110 111 3.00说明:1,由表可知,哈夫曼编码的平均码长要比等长编码短。

分治法解决合并排序问题及动态规划解决矩阵连乘和最长公共子序列问题及贪心法解决哈夫曼编码问题

分治法解决合并排序问题及动态规划解决矩阵连乘和最长公共子序列问题及贪心法解决哈夫曼编码问题

分治法解决合并排序问题及动态规划解决矩阵连乘和最长公共⼦序列问题及贪⼼法解决哈夫曼编码问题分治法解决合并排序问题及动态规划解决矩阵连乘和最长公共⼦序列问题及贪⼼法解决哈夫曼编码问题⼀、课程设计⽬的本次课程设计可以说是我们学完《计算机算法设计与分析》这门课程后的⼀次综合性训练。

本课程设计的训练的⽬的是:1、巩固和掌握计算机算法设计和分析课程的基础知识。

2、培养使⽤计算机基本算法解决实际问题的能⼒。

3、提升使⽤程序设计语⾔对算法程序的开发、调试和测试能⼒。

4、对⼀定的实际问题,能够设计求解算法并分析算法的效率。

5、提⾼综合运⽤算法、程序设计语⾔等能⼒。

6、掌握⽂档的书写能⼒。

⼆、课程设计内容1、分治法(1)合并排序2、动态规划(1)矩阵连乘(2)最长公共⼦序列3、贪⼼法(1)哈夫曼编码三、概要设计1、分治法基本思想:将规模为n的问题分解为k个规模较⼩的⼦问题,使这些⼦问题相互独⽴可分别求解,再将k个⼦问题的解合并成原问题的解。

如⼦问题的规模仍很⼤,则反复分解直到问题⼩到可直接求解为⽌。

在分治法中,⼦问题的解法通常与原问题相同。

(1)合并排序[问题描述]将n个元素排成⾮递减顺序。

[算法思路]若n为1,算法终⽌;否则,将n个待排元素分割成k(k=2)个⼤致相等⼦集合A, B, 对每⼀个⼦集合分别递归排序,再将排好序的⼦集归并为⼀个集合。

2、动态规划基本思想:将问题的求解过程化为多步选择,在每⼀步选择上,列出各种可能的结果(各⼦问题的可⾏解),舍去那些肯定不能成为最优解的局部解。

最后⼀步得到的解必是最优解。

求解过程多为⾃底向上,求解过程产⽣多个选择序列, 下⼀步的选择依赖上⼀步的结果,总能得到最优解。

(1)矩阵连乘[问题描述]给定n个矩阵{A1,…,An},其中Ai与A(i-1)是可相乘的。

确定⼀个计算次序,使计算矩阵连乘积A1…An所需计算量最少。

例如,三个矩阵连乘,两种计算顺序(A*B)*C,A*(B*C)。

  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),程序主文件1.>2.eight = f[i];3. w[i].tree = z;4. }5.6.//建优先队列7. MinHeap<Huffman<Type>> Q(n);8.for(int i=1; i<=n; i++) (w[i]);9.10.//反复合并最小频率树11. Huffman<Type> x,y;12.*13.for(int i=1; i<n; i++)14. {15. x = ();16. y = ();17. (0,,;18. += ;19. = z;20. (x);21. }23.|24. x = ();25.26.delete[] w;27.28.return ;29.}(2) 二叉树实现1.#include<iostream>ing namespace std;3.~4.5.template<class T>6.struct BTNode7.{8. T data;9. BTNode<T> *lChild,*rChild;10.11. BTNode()12. {13. lChild=rChild=NULL;14.'15. }16.17. BTNode(const T &val,BTNode<T> *Childl=NULL,BTNode<T> *Childr=NULL)18. {19. data=val;20. lChild=Childl;21. rChild=Childr;22. }23.24. BTNode<T>* CopyTree()25.·26. {27. BTNode<T> *nl,*nr,*nn;28.29.if(&data==NULL)30.return NULL;31.32. nl=lChild->CopyTree();33. nr=rChild->CopyTree();35. nn=new BTNode<T>(data,nl,nr);36.《37.return nn;38. }39.};40.41.42.template<class T>43.class BinaryTree44.{45.public:46. BTNode<T> *root;47.·48. BinaryTree();49. ~BinaryTree();50.51.void Pre_Order();52.void In_Order();53.void Post_Order();54.55.int TreeHeight()const;56.int TreeNodeCount()const;57.58.;59.void DestroyTree();60.void MakeTree(T pData,BinaryTree<T> leftTree,BinaryTree<T> rightTree);61.void Change(BTNode<T> *r);62.63.private:64.void Destroy(BTNode<T> *&r);65.void PreOrder(BTNode<T> *r);66.void InOrder(BTNode<T> *r);67.void PostOrder(BTNode<T> *r);68.69.,70.int Height(const BTNode<T> *r)const;71.int NodeCount(const BTNode<T> *r)const;72.};73.74.template<class T>75.BinaryTree<T>::BinaryTree()76.{77. root=NULL;79.80.&81.template<class T>82.BinaryTree<T>::~BinaryTree()83.{84.85.}86.87.template<class T>88.void BinaryTree<T>::Pre_Order()89.{90. PreOrder(root);91.,92.}93.94.template<class T>95.void BinaryTree<T>::In_Order()96.{97. InOrder(root);98.}99.100.template<class T>101.void BinaryTree<T>::Post_Order() 102.—103.{104. PostOrder(root);105.}106.107.template<class T>108.int BinaryTree<T>::TreeHeight()const 109.{110.return Height(root);111.}112.113.、114.template<class T>115.int BinaryTree<T>::TreeNodeCount()const 116.{117.return NodeCount(root);118.}119.120.template<class T>121.void BinaryTree<T>::DestroyTree()123. Destroy(root);124.,125.}126.127.template<class T>128.void BinaryTree<T>::PreOrder(BTNode<T> *r)129.{130.if(r!=NULL)131. {132. cout<<r->data<<' ';133. PreOrder(r->lChild);134. PreOrder(r->rChild);135.~136. }137.}138.139.template<class T>140.void BinaryTree<T>::InOrder(BTNode<T> *r)141.{142.if(r!=NULL)143. {144. InOrder(r->lChild);145. cout<<r->data<<' ';146.—147. InOrder(r->rChild);148. }149.}150.151.template<class T>152.void BinaryTree<T>::PostOrder(BTNode<T> *r)153.{154.if(r!=NULL)155. {156. PostOrder(r->lChild);157."158. PostOrder(r->rChild);159. cout<<r->data<<' ';160. }161.}162.163.template<class T>164.int BinaryTree<T>::NodeCount(const BTNode<T> *r)const 165.{166.if(r==NULL)167.return 0;168.¥169.else170.return 1+NodeCount(r->lChild)+NodeCount(r->rChild);171.}172.173.template<class T>174.int BinaryTree<T>::Height(const BTNode<T> *r)const175.{176.if(r==NULL)177.return 0;178.else179.?180. {181.int lh,rh;182. lh=Height(r->lChild);183. rh=Height(r->rChild);184.return 1+(lh>rhlh:rh);185. }186.}187.188.template<class T>189.void BinaryTree<T>::Destroy(BTNode<T> *&r)190.-191.{192.if(r!=NULL)193. {194. Destroy(r->lChild);195. Destroy(r->rChild);196.delete r;197. r=NULL;198. }199.}200.201.#202.template<class T>203.void BinaryTree<T>::Change(BTNode<T> *r)//将二叉树bt所有结点的左右子树交换204.{205. BTNode<T> *p;206.if(r){207. p=r->lChild;208. r->lChild=r->rChild;209. r->rChild=p; //左右子女交换210. Change(r->lChild); //交换左子树上所有结点的左右子树211. Change(r->rChild); //交换右子树上所有结点的左右子树212.~213. }214.}215.216.template<class T>217.void BinaryTree<T>::MakeTree(T pData,BinaryTree<T> leftTree,BinaryTree<T> rightTree) 218.{219. root = new BTNode<T>();220. root->data = pData;221. root->lChild = ;222. root->rChild = ;223.]224.}(3) 最小堆实现1.#include <iostream>ing namespace std;3.template<class T>4.class MinHeap5.{6.private:7. T *heap; //元素数组,0号位置也储存元素8.!9.int CurrentSize; //目前元素个数10.int MaxSize; //可容纳的最多元素个数11.12.void FilterDown(const int start,const int end); //自上往下调整,使关键字小的节点在上13.void FilterUp(int start); //自下往上调整14.15.public:16. MinHeap(int n=1000);17. ~MinHeap();18.bool Insert(const T &x); //插入元素19.|20.21. T RemoveMin(); //删除最小元素22. T GetMin(); //取最小元素23.24.bool IsEmpty() const;25.bool IsFull() const;26.void Clear();27.};28.29.template<class T>30.,31.MinHeap<T>::MinHeap(int n)32.{33. MaxSize=n;34. heap=new T[MaxSize];35. CurrentSize=0;36.}37.38.template<class T>39.MinHeap<T>::~MinHeap()40.{41.!42.delete []heap;43.}44.45.template<class T>46.void MinHeap<T>::FilterUp(int start) //自下往上调整47.{48.int j=start,i=(j-1)/2; //i指向j的双亲节点49. T temp=heap[j];50.51.while(j>0)52.]53. {54.if(heap[i]<=temp)55.break;56.else57. {58. heap[j]=heap[i];59. j=i;60. i=(i-1)/2;61. }62. }63.'64. heap[j]=temp;65.}66.67.template<class T>68.void MinHeap<T>::FilterDown(const int start,const int end) //自上往下调整,使关键字小的节点在上69.{70.int i=start,j=2*i+1;71. T temp=heap[i];72.while(j<=end)73. {74.]75.if( (j<end) && (heap[j]>heap[j+1]) )76. j++;77.if(temp<=heap[j])78.break;79.else80. {81. heap[i]=heap[j];82. i=j;83. j=2*j+1;84. }85.*86. }87. heap[i]=temp;88.}89.90.template<class T>91.bool MinHeap<T>::Insert(const T &x)92.{93.if(CurrentSize==MaxSize)94.return false;95.96.$97. heap[CurrentSize]=x;98. FilterUp(CurrentSize);99.100. CurrentSize++;101.return true;102.}103.104.template<class T>105.T MinHeap<T>::RemoveMin( )106.{107.…108. T x=heap[0];109. heap[0]=heap[CurrentSize-1];110.111. CurrentSize--;112. FilterDown(0,CurrentSize-1); //调整新的根节点113.114.return x;115.}116.117.template<class T>118.!119.T MinHeap<T>::GetMin()120.{121.return heap[0];122.}123.124.template<class T>125.bool MinHeap<T>::IsEmpty() const126.{127.return CurrentSize==0;128.}129.130.template<class T>131.bool MinHeap<T>::IsFull() const132.{133.return CurrentSize==MaxSize;134.}135.136.template<class T>137.void MinHeap<T>::Clear()138.{139. CurrentSize=0;140.}3、贪心选择性质二叉树T表示字符集C的一个最优前缀码,证明可以对T作适当修改后得到一棵新的二叉树T”,在T”中x和y是最深叶子且为兄弟,同时T”表示的前缀码也是C的最优前缀码。

相关文档
最新文档