贪心算法构造哈夫曼树

合集下载

哈夫曼编码贪心算法

哈夫曼编码贪心算法

哈夫曼编码贪心算法
一、哈夫曼编码
哈夫曼编码(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;}。

哈夫曼编码-贪心算法

哈夫曼编码-贪心算法

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

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

实验环境Turbo C 或VC++ 实验学时2学时,必做实验 数据结构与算法struct huffman { double weight; //用来存放各个结点的权值int lchild,rchild,parent; //指向双亲、孩子结点的指针};核心源代码#include<iostream> #include <string> using namespace std; struct huffman { double weight; int lchild,rchild,parent;};∑=jik k astatic int i1=0,i2=0;int Select(huffman huff[],int i){int min=11000;int min1;for(int k=0;k<i;k++){if(huff[k].weight<min&&huff[k].parent==-1){min=huff[k].weight;min1=k;}}huff[min1].parent=1;return min1;}void HuffmanTree(huffman huff[],int weight[],int n) {for(int i=0;i<2*n-1;i++){huff[i].lchild=-1;huff[i].parent=-1;huff[i].rchild=-1;}for(int l=0;l<n;l++){huff[l].weight=weight[l];}for(int k=n;k<2*n-1;k++){int i1=Select(huff,k);int i2=Select(huff,k);huff[i1].parent=k;huff[i2].parent=k;huff[k].weight= huff[i1].weight+huff[i2].weight;huff[k].lchild=i1;huff[k].rchild=i2;}}void huffmancode(huffman huff[],int n){string s;int j;for(int i=0;i<n;i++){s="";j=i;while(huff[j].parent!=-1){if(huff[huff[j].parent].lchild==j)s=s+"0";else s=s+"1";j=huff[j].parent;}cout<<i+1<<"的霍夫曼编码为:";for(int j=s.length();j>=0;j--){cout<<s[j];}cout<<endl;}}void main(){huffman huff[20];int n,w[20];cout<<"input the number of the elements:";cin>>n;cout<<"input the weight:";for(int i=0;i<n;i++){cin>>w[i];}HuffmanTree(huff,w,n);huffmancode(huff,n);}实验结果实验体会哈夫曼编码算法:每次将集合中两个权值最小的二叉树合并成一棵新二叉树,n-1次合并后,成为最终的一棵哈夫曼树。

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

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

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

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

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

哈夫曼编码是由大名鼎鼎的大卫·哈夫曼(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的数量级。

c语言哈夫曼树的构造及编码

c语言哈夫曼树的构造及编码

c语言哈夫曼树的构造及编码一、哈夫曼树概述哈夫曼树是一种特殊的二叉树,它的构建基于贪心算法。

它的主要应用是在数据压缩和编码中,可以将频率高的字符用较短的编码表示,从而减小数据存储和传输时所需的空间和时间。

二、哈夫曼树的构造1. 哈夫曼树的定义哈夫曼树是一棵带权路径长度最短的二叉树。

带权路径长度是指所有叶子节点到根节点之间路径长度与其权值乘积之和。

2. 构造步骤(1) 将待编码字符按照出现频率从小到大排序。

(2) 取出两个权值最小的节点作为左右子节点,构建一棵新的二叉树。

(3) 将新构建的二叉树加入到原来排序后队列中。

(4) 重复上述步骤,直到队列只剩下一个节点,该节点即为哈夫曼树的根节点。

3. C语言代码实现以下代码实现了一个简单版哈夫曼树构造函数:```ctypedef struct TreeNode {int weight; // 权重值struct TreeNode *leftChild; // 左子节点指针struct TreeNode *rightChild; // 右子节点指针} TreeNode;// 构造哈夫曼树函数TreeNode* createHuffmanTree(int* weights, int n) {// 根据权值数组构建节点队列,每个节点都是一棵单独的二叉树TreeNode** nodes = (TreeNode**)malloc(sizeof(TreeNode*) * n);for (int i = 0; i < n; i++) {nodes[i] = (TreeNode*)malloc(sizeof(TreeNode));nodes[i]->weight = weights[i];nodes[i]->leftChild = NULL;nodes[i]->rightChild = NULL;}// 构建哈夫曼树while (n > 1) {int minIndex1 = -1, minIndex2 = -1;for (int i = 0; i < n; i++) {if (nodes[i] != NULL) {if (minIndex1 == -1 || nodes[i]->weight < nodes[minIndex1]->weight) {minIndex2 = minIndex1;minIndex1 = i;} else if (minIndex2 == -1 || nodes[i]->weight < nodes[minIndex2]->weight) {minIndex2 = i;}}}TreeNode* newNode =(TreeNode*)malloc(sizeof(TreeNode));newNode->weight = nodes[minIndex1]->weight + nodes[minIndex2]->weight;newNode->leftChild = nodes[minIndex1];newNode->rightChild = nodes[minIndex2];// 将新构建的二叉树加入到原来排序后队列中nodes[minIndex1] = newNode;nodes[minIndex2] = NULL;n--;}return nodes[minIndex1];}```三、哈夫曼编码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是最深叶子且为兄弟。

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

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

哈夫曼树及哈夫曼编码的算法实现c语言1.引言1.1 概述哈夫曼树及哈夫曼编码是数据压缩和编码中常用的重要算法。

哈夫曼树由大卫·哈夫曼于1952年提出,用于根据字符出现的频率构建一种最优的前缀编码方式。

而哈夫曼编码则是根据哈夫曼树构建的编码表将字符进行编码的过程。

在现代通信和计算机领域,数据传输和存储中往往需要大量的空间。

为了有效利用有限的资源,减少数据的存储和传输成本,数据压缩成为一个重要的技术。

而哈夫曼树及哈夫曼编码正是数据压缩中常用的技术之一。

哈夫曼树的概念及原理是基于字符的频率和概率进行构建的。

在哈夫曼树中,字符出现频率越高的节点越接近根节点,出现频率越低的节点离根节点越远。

这种构建方式保证了哈夫曼树的最优性,即最小化编码的总长度。

哈夫曼编码的算法实现是根据哈夫曼树构建的编码表进行的。

编码表中,每个字符都与一段二进制编码相对应。

在进行数据压缩和解压缩时,通过查表的方式将字符转化为相应的二进制编码,或将二进制编码解析为原始字符。

本文旨在介绍哈夫曼树及哈夫曼编码的概念和原理,并通过C语言实现算法。

通过深入理解哈夫曼树及哈夫曼编码的实现过程,可以更好地理解数据压缩和编码的原理,为后续的研究和应用提供基础。

接下来,我们将首先介绍哈夫曼树的概念和原理,然后详细讲解哈夫曼编码的算法实现。

最后,我们将总结哈夫曼树及哈夫曼编码的重要性,并提出对哈夫曼树和哈夫曼编码进一步研究的方向。

让我们一起深入探索哈夫曼树及哈夫曼编码的奥秘吧!1.2 文章结构文章结构部分的内容可以包括以下内容:文章结构部分主要介绍了本文的组织结构和各个章节的内容概述,以帮助读者更好地理解全文的逻辑结构和内容安排。

首先,本文包括引言、正文和结论三个部分。

引言部分主要对哈夫曼树及哈夫曼编码的算法实现进行了概述,包括相关的概念、原理和目的。

正文部分则深入介绍了哈夫曼树的概念和原理,以及哈夫曼编码的算法实现。

最后,结论部分对本文的主要内容进行了总结,并提出了对哈夫曼树和哈夫曼编码的进一步研究方向。

哈夫曼算法的理解及原理分析算法实现构造哈夫曼树的算法

哈夫曼算法的理解及原理分析算法实现构造哈夫曼树的算法

哈夫曼算法的理解及原理分析算法实现构造哈夫曼树的算法哈夫曼算法(Huffman Algorithm)是一种贪心算法,用于构建最优二叉树(也称为哈夫曼树或者最优前缀编码树),主要用于数据压缩和编码。

它通过统计字符出现的频率来构建一个编码表,将较频繁出现的字符用较短的编码表示,从而减少存储空间和传输带宽。

原理分析:1.统计字符出现的频率:遍历待编码的字符串,统计每个字符出现的次数。

2.构建哈夫曼树:根据字符频率构建二叉树,频率越高的字符位置越靠近根节点,频率越低的字符位置越远离根节点。

构建哈夫曼树的通常做法是使用最小堆来存储频率,并反复合并堆中最小的两个节点,直到堆中只剩一个节点,即根节点。

3.生成编码表:从根节点开始,沿着左子树为0,右子树为1的路径将所有叶子节点的编码存储在一个编码表中。

算法实现:下面是一个简单的Python实现示例:```pythonclass Node:def __init__(self, char, freq):self.char = charself.freq = freqself.left = Noneself.right = Nonedef build_huffman_tree(text):#统计字符频率freq_dict = {}for char in text:if char in freq_dict:freq_dict[char] += 1else:freq_dict[char] = 1#构建最小堆heap = []for char, freq in freq_dict.items(: node = Node(char, freq)heap.append(node)heap.sort(key=lambda x: x.freq)#构建哈夫曼树while len(heap) > 1:left = heap.pop(0)right = heap.pop(0)parent = Node(None, left.freq + right.freq) parent.left = leftparent.right = rightheap.append(parent)heap.sort(key=lambda x: x.freq)root = heap[0]#生成编码表code_table = {}generate_code(root, "", code_table)return code_tabledef generate_code(node, code, code_table):if node.char:code_table[node.char] = codereturngenerate_code(node.left, code + "0", code_table) generate_code(node.right, code + "1", code_table) ```构造哈夫曼树的算法:上述的 `build_huffman_tree` 函数通过统计频率构建了最小堆`heap`,然后不断地合并最小的两个节点,直到堆中只剩下一个节点,即哈夫曼树的根节点。

acm贪心算法经典题型归纳

acm贪心算法经典题型归纳

acm贪心算法经典题型归纳
贪心算法是一种在求解最优化问题时常用的算法思想,它通常
用于解决那些具有最优子结构性质的问题。

在ACM竞赛中,贪心算
法经典题型主要包括以下几类:
1. 区间调度问题,这类问题要求在一系列区间中选择尽量多的
不重叠区间。

经典问题包括最大不重叠区间数量、最小区间覆盖等。

2. 背包问题,在给定背包容量和一系列物品的重量、价值的情
况下,选择装入背包的物品,使得背包内物品的总价值最大。

贪心
算法通常用于解决部分背包问题或者分数背包问题。

3. 最小生成树,贪心算法经典的应用之一是求解最小生成树,
其中Prim算法和Kruskal算法就是典型的贪心算法。

4. 最短路径问题,在有向图或者无向图中,求解起点到终点的
最短路径。

Dijkstra算法和Bellman-Ford算法都可以使用贪心思
想进行优化。

5. 哈夫曼编码,贪心算法还可以用于构造哈夫曼树,实现数据
的最优编码。

以上仅是贪心算法在ACM竞赛中的一些经典题型,实际上贪心算法还可以应用于很多其他问题的求解中。

在解决这些问题时,需要注意贪心选择性质和最优子结构性质,合理选择贪心策略,并证明其正确性。

同时,也需要注意到贪心算法并不适用于所有问题,有时候需要结合动态规划等其他算法来求解。

希望这些信息对你有帮助。

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

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

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

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

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

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

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

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

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

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

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

在构建最小堆的过程中,需要对所有字符按照其频率进行排序,并将其依次插入最小堆中,这一操作的时间复杂度为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最小的问题就是带权路径长度最小的哈夫曼树的构造问题。

贪心法构造哈夫曼树

贪心法构造哈夫曼树

实验报告( 2013 / 2014 学年第二学期)学院贝尔学院学生姓名任晓强班级学号 Q12010218 指导教师季一木指导单位计算机软件教学中心日期 2014年3月12日实验一:贪心算法构造哈夫曼树问题简述:两路合并最佳模式的贪心算法主要思想如下:(1)设w={w0,w1,......w}是一组权值,以每个权值作为根结点值,构造n棵只有根的n-1二叉树(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备份至b char 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);}}四、程序运行结果演示五、实验体会哈夫曼编码算法:每次将集合中两个权值最小的二叉树合并成一棵新二叉树,n-1次合并后,成为最终的一棵哈夫曼树。

贪心算法的概念

贪心算法的概念

贪心算法的概念贪心算法的概念概述贪心算法(Greedy Algorithm)是一种基于贪心策略的算法,它在每个阶段选择局部最优解,并以此来达到全局最优解的目标。

贪心算法通常用于求解最优化问题,如最小生成树、哈夫曼编码、背包问题等。

特点1. 贪心算法是一种简单而高效的算法,其时间复杂度通常较低。

2. 贪心算法只考虑当前状态下的最优解,不考虑未来可能出现的情况。

3. 贪心算法需要满足“无后效性”,即某个状态下的选择不会影响到之后状态的选择。

4. 贪心算法需要满足“局部最优性”,即每次选择都是当前状态下的最优解。

5. 贪心算法不能保证一定能得到全局最优解,但在某些情况下可以得到近似最优解。

应用1. 最小生成树:Kruskal和Prim两种方法都是基于贪心策略实现的。

2. 哈夫曼编码:通过构造一棵哈夫曼树来实现编码,其中每个字符对应一个叶子节点,权值为出现频率,通过合并权值较小的节点得到每个字符的编码。

3. 背包问题:贪心算法可以通过计算每个物品的单位价值(即价值与重量的比值)来选择最优解,但该方法只适用于部分背包问题。

4. 最短路径问题:Dijkstra算法和Bellman-Ford算法都是基于贪心策略实现的。

5. 调度问题:如任务调度、机器调度等,可以通过贪心算法得到近似最优解。

缺点1. 贪心算法不能保证一定能得到全局最优解,只能得到近似最优解。

2. 贪心算法需要满足“无后效性”和“局部最优性”,但这两个条件不一定容易满足,有时需要进行特殊处理。

3. 贪心算法可能会受到数据规模和数据分布等因素的影响,导致结果不准确或者无法得出结果。

总结贪心算法是一种简单而高效的算法,其时间复杂度通常较低。

它在求解最优化问题时具有一定的应用价值。

但贪心算法不能保证一定能得到全局最优解,只能得到近似最优解。

在实际应用中需要注意选择合适的贪心策略,并对特殊情况进行特殊处理。

贪心算法哈夫曼编码c语言

贪心算法哈夫曼编码c语言

贪心算法哈夫曼编码c语言哈夫曼编码的贪心算法可以分为以下几步:1. 读入需要编码的字符及其出现频率,并按照频率从小到大排序。

2. 构建哈夫曼树。

首先将所有字符看成只有一个节点的树,然后取出频率最小的两棵树,将它们合并成一棵树,这棵树的频率是两棵树的频率之和。

继续取出频率最小的两棵树,重复上述过程,直到只剩下一棵树为止,这就是哈夫曼树。

3. 对哈夫曼树进行编码。

从哈夫曼树的根节点开始,往左走为0,往右走为1,一直走到叶子节点,记录下这个叶子节点代表的字符的编码。

这就是哈夫曼编码。

以下是用C语言实现的贪心算法实现:```c#include <stdio.h>#include <stdlib.h>#include <string.h>#define MAX_N 256 // 假设字符集大小为256typedef struct node {char ch; // 字符int freq; // 频率struct node *left, *right; // 左右子节点} Node;// 建立一个新的节点Node* new_node(char ch, int freq) {Node *node = (Node*)malloc(sizeof(Node));node->ch = ch;node->freq = freq;node->left = node->right = NULL;return node;}// 在nodes数组中找寻最小的两个节点void find_min_two_nodes(Node **nodes, int size, int *min1, int *min2) {*min1 = *min2 = -1;for (int i = 0; i < size; i++) {if (nodes[i] == NULL) continue;if (*min1 == -1 || nodes[i]->freq < nodes[*min1]->freq) {*min2 = *min1;*min1 = i;} else if (*min2 == -1 || nodes[i]->freq < nodes[*min2]->freq) {*min2 = i;}}}// 构建哈夫曼树Node* build_huffman_tree(char *str, int *freq, int n) {Node *nodes[MAX_N];for (int i = 0; i < n; i++) {nodes[i] = new_node(str[i], freq[i]);}int size = n;while (size > 1) {int min1, min2;find_min_two_nodes(nodes, size, &min1, &min2);Node *node = new_node(0, nodes[min1]->freq +nodes[min2]->freq);node->left = nodes[min1];node->right = nodes[min2];nodes[min1] = node;nodes[min2] = NULL;size--;}return nodes[0];}// 递归生成哈夫曼编码void gen_huffman_code(Node *root, char *code, int depth, char **table) {if (root == NULL) return;if (root->left == NULL && root->right == NULL) {code[depth] = '\0';table[root->ch] = (char*)malloc((depth + 1) * sizeof(char)); strcpy(table[root->ch], code);return;}code[depth] = '0';gen_huffman_code(root->left, code, depth + 1, table);code[depth] = '1';gen_huffman_code(root->right, code, depth + 1, table); }// 哈夫曼编码char** huffman_code(char *str, int *freq, int n) {Node *root = build_huffman_tree(str, freq, n);char **table = (char**)malloc(MAX_N * sizeof(char*)); char code[MAX_N];gen_huffman_code(root, code, 0, table);return table;}int main() {char str[] = "ABACCABB";int freq[] = {2, 3, 1, 2, 1, 1, 1, 1};int n = strlen(str);char **table = huffman_code(str, freq, n);for (int i = 0; i < n; i++) {printf("char: %c, code: %s\n", str[i], table[str[i]]);}return 0;}```输出结果:```char: A, code: 11char: B, code: 0char: A, code: 11char: C, code: 100char: C, code: 100char: A, code: 11char: B, code: 1char: B, code: 01```这就是对字符串"ABACCABB"进行哈夫曼编码的结果。

构造一棵哈夫曼树并输出叶子结点的哈夫曼编码

构造一棵哈夫曼树并输出叶子结点的哈夫曼编码

构造一棵哈夫曼树并输出叶子结点的哈夫曼编码1. 前言哈夫曼树是一种经典的树形结构,通常用于数据压缩和编码。

在哈夫曼树中,叶子结点代表不同的字符或符号,而内部结点则代表字符的频率或权重。

构造哈夫曼树的过程可以通过贪心算法来实现,这个过程非常有趣而且具有一定的挑战性。

本文将通过详细的步骤来介绍如何构造一棵哈夫曼树,并输出叶子结点的哈夫曼编码。

2. 基本概念在构造哈夫曼树之前,我们首先需要了解一些基本概念:- 哈夫曼树:由n个叶子结点构成的二叉树,具有最小的带权路径长度,即具有最小的总路径长度。

- 叶子结点:树中没有子结点的结点,代表字符或符号。

- 带权路径长度:从根结点到叶子结点的路径长度与叶子结点的权重(频率)的乘积之和。

- 哈夫曼编码:叶子结点的路径可以表示为0和1的序列,用来表示字符或符号。

3. 构造哈夫曼树的步骤下面,我们将通过详细的步骤来构造一棵哈夫曼树,并输出叶子结点的哈夫曼编码:3.1 初始化我们需要准备一组具有权重的叶子结点,代表不同的字符或符号。

每个叶子结点的权重可以根据字符在文本中出现的频率来确定。

3.2 构造哈夫曼树接下来,我们通过以下步骤来构造哈夫曼树:- 将所有的叶子结点按照权重从小到大进行排序。

- 选取权重最小的两个叶子结点作为左右子结点,然后将它们合并为一个新的内部结点,其权重为两个子结点的权重之和。

- 将新得到的内部结点插入到已排序的叶子结点中,并重新排序。

- 重复以上步骤,直到所有的叶子结点都被合并为一个根结点。

3.3 输出叶子结点的哈夫曼编码一旦我们构造出了哈夫曼树,我们就可以通过以下步骤来输出叶子结点的哈夫曼编码:- 从根结点开始,沿着左子树走为0,沿着右子树走为1,直到达到叶子结点。

- 将叶子结点的路径上的0和1序列记录下来,即为该叶子结点的哈夫曼编码。

4. 示例为了更加直观地理解哈夫曼树的构造过程,我们来看一个简单的示例:假设我们有以下四个叶子结点:A(1),B(2),C(3),D(4)(括号内为权重)。

哈夫曼树构造方法

哈夫曼树构造方法

哈夫曼树构造方法哈夫曼树(Huffman Tree)是一种广泛应用于数据压缩和编码的二叉树结构。

它是一种最优二叉树,即带权路径长度最短的二叉树。

哈夫曼树的构造方法主要有两种:贪心算法和分治算法。

1. 贪心算法:哈夫曼树的贪心算法是一种自底向上(从叶子节点到根节点)的构造方法。

首先,根据给定的权值列表,将每个权值看作一个独立的节点,并按照权值从小到大的顺序构建一个森林。

然后,从森林中选择权值最小的两个节点(可以使用最小堆来实现),将它们合并为一个新的节点,并将新节点的权值设为两个被合并节点的权值之和。

将新节点插入到森林中,并移除原来的两个节点。

重复上述步骤,直到森林中只有一个节点为止,该节点就是哈夫曼树的根节点。

贪心算法构造哈夫曼树的时间复杂度为O(nlogn),n为节点数量。

2. 分治算法:哈夫曼树的分治算法是一种自顶向下(从根节点到叶子节点)的构造方法。

首先,将给定的权值列表按照权值从小到大的顺序排序。

然后,将权值最小的两个节点合并为一个新的节点,并将新节点的权值设为两个被合并节点的权值之和。

将新节点插入到排序后的列表中,并移除原来的两个节点。

重复上述步骤,直到列表中只有一个节点为止,该节点就是哈夫曼树的根节点。

分治算法构造哈夫曼树的时间复杂度为O(n^2),n为节点数量。

无论是贪心算法还是分治算法,构造出的哈夫曼树都具有最优性质,即带权路径长度最短。

由于贪心算法的时间复杂度较低,因此在实际应用中更为常用。

另外,构造哈夫曼树的方法除了贪心算法和分治算法外,还可以使用动态规划等其他方法。

对于哈夫曼树的应用,最常见的是数据压缩和编码。

哈夫曼树可以根据字符出现的频率构建对应的编码表,将频率高的字符用较短的编码表示,将频率低的字符用较长的编码表示,从而实现对数据的压缩。

在压缩的过程中,利用哈夫曼树可以实现对数据的高效编码和解码。

此外,哈夫曼树还有其他应用,比如在路由表的构建和图像压缩等领域也有广泛应用。

贪心算法

贪心算法

max vi xi
i 1
n
于是,背包问题归结为寻找一个满足约束条 件式,并使目标函数式达到最大的解向量X=(x1, x2, …, xn)。
至少有三种看似合理的贪心策略: (1)选择价值最大的物品,因为这可以尽可能快 地增加背包的总价值。但是,虽然每一步选择获得 了背包价值的极大增长,但背包容量却可能消耗得 太快,使得装入背包的物品个数减少,从而不能保 证目标函数达到最大。 (2)选择重量最轻的物品,因为这可以装入尽可 能多的物品,从而增加背包的总价值。但是,虽然 每一步选择使背包的容量消耗得慢了,但背包的价 值却没能保证迅速增长,从而不能保证目标函数达 到最大。 (3)选择单位重量价值最大的物品,在背包价值 增长和背包容量消耗两者之间寻找平衡。
算法
main( ) { int i,j,n,GZ,A; int B[8]={0,100,50,20,10,5,2,1},S[8]; input(n); for(i=1;i<=n;i++) { input(GZ); for(j=1,j<=7;j++) { A=GZ/B[j]; S[j]=S[j]+A; GZ=GZ-A*B[j];} } for(i=1;i<=7;i++) print(B[i], “----”, S[i]); }
∞ b 4 0 a 8 h ∞ 4 b 4 0 a 8 h 8 11 7 11 7
8 ∞ i 6 1 2
∞ c
7
∞ d 14 9 e ∞ 10
4 g ∞
2
f ∞
(a)
8 ∞ i 6 1 g ∞ 2 4 f ∞ ∞ c 7 ∞ d 14 9 e ∞ 10 2
贪心法求解活动安排问题的关键是如何选择贪心策略,使 得按照一定的顺序选择相容活动,并能安排尽量多的活动。至 少有两种看似合理的贪心策略: (1)最早开始时间:这样可以增大资源的利用率。 (2)最早结束时间:这样可以使下一个活动尽早开始。

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

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

0023算法笔记——【贪心算法】哈夫曼编码问题D译码过程需要方便的取出编码的前缀,因此需要表示前缀码的合适的数据结构。

为此,可以用二叉树作为前缀码的数据结构:树叶表示给定字符;从树根到树叶的路径当作该字符的前缀码;代码中每一位的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;71.}72.73.template<class T>74.BinaryTree<T>::~BinaryTree()75.{76.77.}78.79.template<class T>80.void BinaryTree<T>::Pre_Order()81.{82. PreOrder(root);83.}84.85.template<class T>86.void BinaryTree<T>::In_Order()87.{88. InOrder(root);89.}90.91.template<class T>92.void BinaryTree<T>::Post_Order()93.{94. PostOrder(root);95.}96.97.template<class T>98.int BinaryTree<T>::TreeHeight()const99.{100.return Height(root);101.}102.103.template<class T>104.int BinaryTree<T>::TreeNodeCount()const 105.{106.return NodeCount(root);107.}108.109.template<class T>110.void BinaryTree<T>::DestroyTree()111.{112. Destroy(root);113.}114.115.template<class T>116.void BinaryTree<T>::PreOrder(BTNode<T> *r) 117.{118.if(r!=NULL)119. {120. cout<<r->data<<' ';121. PreOrder(r->lChild);122. PreOrder(r->rChild);123. }124.}125.126.template<class T>127.void BinaryTree<T>::InOrder(BTNode<T> *r) 128.{129.if(r!=NULL)130. {131. InOrder(r->lChild);132. cout<<r->data<<' ';133. InOrder(r->rChild);134. }135.}136.137.template<class T>138.void BinaryTree<T>::PostOrder(BTNode<T> *r) 139.{140.if(r!=NULL)141. {142. PostOrder(r->lChild);143. PostOrder(r->rChild);144. cout<<r->data<<' ';145. }146.}147.148.template<class T>149.int BinaryTree<T>::NodeCount(const BTNode<T> *r)const150.{151.if(r==NULL)152.return 0;153.else154.return 1+NodeCount(r->lChild)+NodeCount(r->rChild);155.}156.157.template<class T>158.int BinaryTree<T>::Height(const BTNode<T> *r)const159.{160.if(r==NULL)161.return 0;162.else163. {164.int lh,rh;165. lh=Height(r->lChild);166. rh=Height(r->rChild);167.return 1+(lh>rh?lh:rh);168. }169.}170.171.template<class T>172.void BinaryTree<T>::Destroy(BTNode<T> *&r)173.{174.if(r!=NULL)175. {176. Destroy(r->lChild);177. Destroy(r->rChild);178.delete r;179. r=NULL;180. }181.}182.183.template<class T>184.void BinaryTree<T>::Change(BTNode<T> *r)//将二叉树bt所有结点的左右子树交换185.{186. BTNode<T> *p;187.if(r){188. p=r->lChild;189. r->lChild=r->rChild;190. r->rChild=p; //左右子女交换191. Change(r->lChild); //交换左子树上所有结点的左右子树192. Change(r->rChild); //交换右子树上所有结点的左右子树193. }194.}195.196.template<class T>197.void BinaryTree<T>::MakeTree(T pData,BinaryTree<T> leftTree,BinaryTree<T> r ightTree)198.{199. root = new BTNode<T>();200. root->data = pData;201. root->lChild = leftTree.root;202. root->rChild = rightTree.root;203.}(3)MinHeap.h 最小堆实现[cpp] view plain copy1.#include <iostream>ing namespace std;3.template<class T>4.class MinHeap5.{6.private:7. T *heap; //元素数组,0号位置也储存元素8.int CurrentSize; //目前元素个数9.int MaxSize; //可容纳的最多元素个数10.11.void FilterDown(const int start,const int end); //自上往下调整,使关键字小的节点在上12.void FilterUp(int start); //自下往上调整13.14.public:15. MinHeap(int n=1000);16. ~MinHeap();17.bool Insert(const T &x); //插入元素18.19. T RemoveMin(); //删除最小元素20. T GetMin(); //取最小元素21.22.bool IsEmpty() const;23.bool IsFull() const;24.void Clear();25.};26.27.template<class T>28.MinHeap<T>::MinHeap(int n)29.{30. MaxSize=n;31. heap=new T[MaxSize];32. CurrentSize=0;33.}34.35.template<class T>36.MinHeap<T>::~MinHeap()37.{38.delete []heap;39.}40.41.template<class T>42.void MinHeap<T>::FilterUp(int start) //自下往上调整43.{44.int j=start,i=(j-1)/2; //i指向j的双亲节点45. T temp=heap[j];46.47.while(j>0)48. {49.if(heap[i]<=temp)50.break;51.else52. {53. heap[j]=heap[i];54. j=i;55. i=(i-1)/2;56. }57. }58. heap[j]=temp;59.}60.61.template<class T>62.void MinHeap<T>::FilterDown(const int start,const int end) //自上往下调整,使关键字小的节点在上63.{64.int i=start,j=2*i+1;65. T temp=heap[i];66.while(j<=end)67. {68.if( (j<end) && (heap[j]>heap[j+1]) )69. j++;70.if(temp<=heap[j])71.break;72.else73. {74. heap[i]=heap[j];75. i=j;76. j=2*j+1;77. }78. }79. heap[i]=temp;80.}81.82.template<class T>83.bool MinHeap<T>::Insert(const T &x)84.{85.if(CurrentSize==MaxSize)86.return false;87.88. heap[CurrentSize]=x;89. FilterUp(CurrentSize);90.91. CurrentSize++;92.return true;93.}94.95.template<class T>96.T MinHeap<T>::RemoveMin( )97.{98. T x=heap[0];99. heap[0]=heap[CurrentSize-1];100.101. CurrentSize--;102. FilterDown(0,CurrentSize-1); //调整新的根节点103.104.return x;105.}106.107.template<class T>108.T MinHeap<T>::GetMin()109.{110.return heap[0];111.}112.113.template<class T>114.bool MinHeap<T>::IsEmpty() const115.{116.return CurrentSize==0;117.}118.119.template<class T>120.bool MinHeap<T>::IsFull() const121.{122.return CurrentSize==MaxSize;123.}124.125.template<class T>126.void MinHeap<T>::Clear()127.{128. CurrentSize=0;129.}3、贪心选择性质二叉树T表示字符集C的一个最优前缀码,证明可以对T作适当修改后得到一棵新的二叉树T”,在T”中x和y是最深叶子且为兄弟,同时T”表示的前缀码也是C的最优前缀码。

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

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

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

构造哈夫曼树代码

构造哈夫曼树代码

构造哈夫曼树:理论与实践哈夫曼树是一种重要的数据结构,它广泛应用于编码压缩和加密算法中。

本文将介绍哈夫曼树的原理和构造方法,并给出代码实现。

一、哈夫曼树的种类哈夫曼树分为静态哈夫曼树和动态哈夫曼树两种,其中静态哈夫曼树适用于数据量较小且变化不大的情况,而动态哈夫曼树则更适用于数据量较大且动态变化的情况。

二、哈夫曼树的构造方法哈夫曼树的构造方法一般有两种:贪心算法和分治算法。

其中贪心算法是最常用的构造方法,它的基本思想是每次选择出现频率最小的两个节点作为左右子树,然后合并成一个节点,直到所有节点都合并为一个根节点。

三、哈夫曼树的代码实现以下是基于贪心算法的哈夫曼树代码实现:```include <iostream>include <queue>include <vector>include <algorithm>using namespace std;struct Node {int val, freq;Node *left, *right;Node(int v, int f) : val(v), freq(f), left(nullptr), right(nullptr) {};};struct cmp {bool operator()(Node* a, Node* b) {return a->freq > b->freq;}};void buildHuffmanTree(vector<int>& arr) {priority_queue<Node*, vector<Node*>, cmp> pq;for (int i = 0; i < arr.size(); i++) {pq.push(new Node(arr[i], arr[i]));}while (pq.size() > 1) {Node* left = pq.top();pq.pop();Node* right = pq.top();pq.pop();Node* parent = new Node(-1, left->freq + right->freq);parent->left = left;parent->right = right;pq.push(parent);}}int main() {vector<int> arr {1, 2, 3, 4, 5};buildHuffmanTree(arr);return 0;}```四、总结本文介绍了哈夫曼树的原理和构造方法,并给出了基于贪心算法的代码实现。

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

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

相关文档
最新文档