贪心算法;最小堆+哈夫曼树;打印出每个字母对应编码;调试了几次,应该无错;

#include <iostream> using namespace std; typedef struct HuffmanNode//哈弗曼树的结点 { char letter;//存储的字符,叶节点为字母,非叶节点为# struct HuffmanNode *parent;//父亲结点 int code;//如果为父结点的左孩子,则为0,右孩子为1 }HuffmanNode; typedef struct HeapNode//堆结点定义 { int rate;//出现频率 HuffmanNode *node;//对应于哈弗曼树中的结点 }HeapNode; //需要在初始化堆得时候完成所有叶结点的插入 class Huffman { private: int heapSize;//堆大小 int num;//记录字符数量 HeapNode *heap;//堆数组 char *letter;//字符数组 int *rate;//字符出现频度 HuffmanNode **arr;//记录叶结点的数组,打印编码的时候可以从叶结点回溯向上 public: //构造函数 Huffman(int numOfLetters)//传入字符个数 { this->heapSize=numOfLetters;//堆大小初始化为字母数 num=numOfLetters;//记录字符数量 heap=new HeapNode[numOfLetters+1];//分配堆空间,最多只需要字符的个数,因为合并的过程删除2个插入1个 letter=new char[numOfLetters+1];//用于存储字符 rate=new int[numOfLetters+1];//用于存储字符出现频度 arr=new HuffmanNode* [numOfLetters+1]; } //输入数组 void input() { int i=1; while(i<=heapSize) { cout<<“输入第”<<i<<“个字符”<<endl; cin>>letter[i]; cout<<“输入第”<<i<<“个字符的频度”<<endl; cin>>rate[i]; //向堆中插入各个结点 heap[i].rate=rate[i]; heap[i].node=new HuffmanNode; arr[i]=heap[i].node; heap[i].node->parent=NULL; heap[i].node->letter=letter[i]; i++; } } //求父结点 int parent(int i) { return i/2; } //求左孩子 int left(int i) { return 2*i; } //求右孩子 int right(int i) { return 2*i+1; } //交换函数 void swap(int i,int j) { //交换结构体数组,需要交换结构体内数据 int rate;//用于交换频度 HuffmanNode *p;//用于交换哈弗曼树的结点指针 rate=heap[i].rate; p=heap[i].node; heap[i].rate=heap[j].rate; heap[i].node=heap[j].node; heap[j].rate=rate; heap[j].node=p; } //维持堆性质函数,使用前提为左右子书均为最小堆 void heapIfy(int i,int localHeapSize) { int l=left(i); int r=right(i); int least=i; //找出i,left(i),right(i)的最小值 if(l<=localHeapSize&&heap[least].rate>heap[l].rate) { least=l; } if(r<=localHeapSize&&heap[least].rate>heap[r].rate) { least=r; } //交换heap[i]与heap[least] if(least!=i) { swap(i,least); heapIfy(least,localHeapSize); } } //初始化堆(建立堆) void buildHeap() { for(int i=heapSize/2;i>=1;i–) { heapIfy(i,heapSize); } } //去掉并返回堆中最小的元素(前提:堆已初始化) HeapNode* extractMin() { if(heapSize>=1) { HeapNode *min; swap(1,heapSize); min=&heap[heapSize]; –heapSize; heapIfy(1,heapSize); return min; } else { cout<<“堆中没有元素”<<endl; return NULL; } } //向堆中插入数据(前提:堆已经初始化) void heapInsert(int rate,HuffmanNode *p) { ++heapSize; int i=heapSize; while(i>1&&heap[parent(i)].rate>rate) { heap[i].rate=heap[parent(i)].rate; heap[i].node=heap[parent(i)].node; i=parent(i); } heap[i].rate=rate; heap[i].node=p; } //构造哈弗曼树 HuffmanNode* buildTree() { buildHeap();//初始化堆 HeapNode *p;//用于临时存储最小堆结点 HeapNode *q;//用于临时存储次小堆结点 int count=heapSize; for(int i=1;i<=count-1;i++) { HuffmanNode *tree=new HuffmanNode;// 生成内结点 tree->letter=’#’;//内结点的字符记作#,没有实际意义 p=extractMin();//取堆最小结点 q=extractMin();//取堆次小结点 p->node->parent=tree; p->node->code=0; q->node->parent=tree; q->node->code=1; heapInsert(p->rate+q->rate,tree);//插入堆中 } return extractMin()->node; } //递归打印编码 void display() { HuffmanNode *p=buildTree();//哈弗曼树的根节点地址 int i=1; while(i<=num) { cout<<arr[i]->letter<<“:”; backPrint(arr[i],p); cout<<endl; i++; } } //从叶结点回溯打印code编码 void backPrint(HuffmanNode *p,HuffmanNode *root) { if(p!=root) { backPrint(p->parent,root); cout<<p->code; } } }; void main() { Huffman test(5); test.input(); test.display(); }

 

 

关键在于最小堆与哈夫曼树的结构体定义与联系。

 

最小堆数组的结构体中存放频率与对应的哈夫曼树结点。

哈夫曼树的叶子结点存储字符,内结点以#存储,并且设置父指针与一个标志左或右孩子的标志,为了方便从叶结点回溯打印结果。

 

构造哈夫曼树之前,首先将叶结点放入堆中,初始化堆。 然后利用最小堆,每次取得两个频率最小的哈夫曼树结点,并设最小的为父亲的左孩子,次小的为父亲的右孩子,然后生成一个新的结点,其频率为两者之和,并且修改两个结点的父亲为该结点,加入到堆中,并将两个结点删除。 然后再重复寻找2个最小结点,重复这样的工作,一直运行n-1次即可全部合并完毕,这时取出堆中的最后一个结点即为哈夫曼树的根节点。

 

为了能够回溯打印每个叶结点的Code,我们在构造哈夫曼树之前就应该将这些哈夫曼树叶结点存放于一个数组中,在构造结束后,我们从数组中依次根据每个叶结点的parent指针与标志域就可以回溯到根节点,打印出code码。 如果我们从根节点向下递归打印code是无法打印完成的,思考一下就可以明白了。

 

 

    原文作者:贪心算法
    原文地址: https://blog.csdn.net/qq120848369/article/details/5443129
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞