本博文内容源于老师上课的数据结构,因为内容比较简单,所以在这里实现,本周的内容是哈夫曼编码。内容分为三步,看清题目、书写代码,进行测试
实验内容
实验9
写程序实现算法5.10、5.11
实验细节
5.10
写这段代码注意Select函数,一个是s1与s2,二是实现细节,代码如下
参考于leetcode找第一大与第二大,一般O(n)就可以完成.
void Select(HuffmanTree HT,int n,int &s1,int &s2) {
int m1 = 1e6,m2 = 1e6;
int i=1;
for(i=1;i<=n;i++){
if(HT[i].parent == 0){
//find the minest weight
if(HT[i].weight < m2){
//它是取一个小值
m1 = m2;
s1 = s2;
m2 = HT[i].weight;
s2 = i;
}else if(HT[i].weight < m1){
//它是取另一个小值
m1 = HT[i].weight;
s1 = i;
}
}
}
}
5.11
5.11的难点在于它的逻辑,它的逻辑是建立在你能创建一个已完成的霍夫曼树进行操作。
//有点类似于并查集,从1找父亲
typedef char **HuffmanCode; //dynamic malloc huffman code
void CreateHuffmanCode(HuffmanTree HT,HuffmanCode &HC,int n){
HC = new char*[n+1];
char *cd = new char[n];
int i;
cd[n-1]='\0';
for(i=1;i<=8;i++){
int start = n-1;
int c= i;
int f = HT[i].parent;
while(f!=0){
cout <<f << " " << HT[f].lchild << " " << c ;
--start;
if(HT[f].lchild == c)
cd[start] = '0';
else
cd[start] = '1';
cout << " " << cd[start] << endl;
c = f;
f = HT[f].parent;
}
HC[i] = new char[n-start];
strcpy(HC[i],&cd[start]);
}
delete cd;
}
实验效果测试
我想创建的树,与书上的类似。因为霍夫曼不唯一,比较一下书上的差不多。(潦草是我的)
完整代码
//dev c++ 6.3 running
#include<iostream>
#include<cstring>
#define Node 8
using namespace std;
typedef struct{
int weight; //node weight
int parent,lchild,rchild; //node parent,left child and right child
}HTNode,*HuffmanTree; // new huffman
void Select(HuffmanTree HT,int n,int &s1,int &s2) {
int m1 = 1e6,m2 = 1e6;
int i=1;
for(i=1;i<=n;i++){
if(HT[i].parent == 0){
//find the minest weight
if(HT[i].weight < m2){
m1 = m2;
s1 = s2;
m2 = HT[i].weight;
s2 = i;
}else if(HT[i].weight < m1){
m1 = HT[i].weight;
s1 = i;
}
}
}
}
void CreateHuffmanTree(HuffmanTree &HT,int n){
if(n<=1) return;
int m = 2*n-1,i;
HT = new HTNode[m+1]; //0 unit not use HT[m] need root
for(i=1;i<=m;++i){
HT[i].parent = 0;
HT[i].lchild = 0;
HT[i].rchild = 0;
}
for(i =1;i<=n;++i){
cin >> HT[i].weight;
}
for(i=n+1;i<=m;++i){
int s1=1,s2=1;
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;
}
}
typedef char **HuffmanCode; //dynamic malloc huffman code
void CreateHuffmanCode(HuffmanTree HT,HuffmanCode &HC,int n){
HC = new char*[n+1];
char *cd = new char[n];
int i;
cd[n-1]='\0';
for(i=1;i<=8;i++){
int start = n-1;
int c= i;
int f = HT[i].parent;
while(f!=0){
--start;
if(HT[f].lchild == c)
cd[start] = '0';
else
cd[start] = '1';
c = f;
f = HT[f].parent;
}
HC[i] = new char[n-start];
strcpy(HC[i],&cd[start]);
}
delete cd;
}
int main(){
HuffmanTree HT;
int n = 8,i;
CreateHuffmanTree(HT,n);
for(i=1;i<=15;i++){
cout << HT[i].weight << " " << HT[i].parent << " " <<HT[i].lchild << " "
<< HT[i].rchild << endl;
}
HuffmanCode HC;
CreateHuffmanCode(HT,HC,8);
for(i =1;i<=8;i++){
cout << HC[i] << endl;
}
return 0;
}
#include <iostream>
using namespace std;
#include <string>
#include <cstring>
//-----哈夫曼树的存储表示-----
typedef struct{
int weight;//结点的权值
int parent,lchild,rchild;//结点的双亲、左孩子、右孩子的下标
}HTNode,*HuffmanTree;//动态分配数组存储哈夫曼树
void Select(HuffmanTree HT,int n,int &s1,int &s2){
int a= 1e4,b= 1e4;//选两个很大的值进行比大小
for(int i=1;i<=n;i++){
if(HT[i].parent == 0){
//一开始双亲都为0
if(HT[i].weight < b){
a = b;
s1 = s2;
//如果找到比b还小的,则将a变为b,s2赋给s1,继承b(原来最小的值)
b = HT[i].weight;
s2 = i;//取出最小的结点编号
}else if(HT[i].weight < a){
/*第一个条件语句满足后,即便第二个条件语句也满足,也不会执行第二个括号。
所以这是用来在第一个条件语句不满足后,用来比较出第二小的*/
a = HT[i].weight;
s1 = i;//取出第二小的结点编号
}
}
}
}
//构造哈夫曼树
void CreateHuffmanTree(HuffmanTree &HT,int n){
//------------初始化哈夫曼树HT------------
if(n<=1) return;
int m=2*n-1;//n个结点构造出哈夫曼树后的结点总个数
HT=new HTNode[m+1]; //0号单元未用,所以需要动态分配m+1个单元,HT[m]表示根节点//HT是指针,指向结点
for(int i=1;i<=m;++i){
//将1~m号单元中的双亲、左孩子,右孩子的下标都初始化为0
HT[i].parent=0;
HT[i].lchild=0;
HT[i].rchild=0;
}
cout << n << endl;
for(int i=1;i<=n;i++){
cin>>HT[i].weight;//输入前n个单元各结点的权值
}
//------------创建哈夫曼树------------
for(int i=n+1;i<=m;i++){
//n+1是新合并出来的第一个结点的编号i
int s1,s2;//因为select函数传的是变量,所以要先声明,如果是定值就不需要先声明
Select(HT,i-1,s1,s2);//在HT[k](1<=k<=i-1)中选择两个其双亲域为0且权值最小的结点,并返回它们在HT中的序号s1和s2
//得到新结点i,从森林中删除s1和s2,将s1和s2的双亲域由0改为i
HT[s1].parent=i;
HT[s2].parent=i;
//s1,s2分别作为i的左右孩子
HT[i].lchild=s1; HT[i].rchild=s2;
//i的权值为左右孩子权值之和
HT[i].weight=HT[s1].weight+HT[s2].weight;
}
}
//输出哈夫曼树
void printHuffmanTree(HuffmanTree &HT,int m){
cout << "Huffman is:" << endl;
for(int i=1;i<=m;i++){
//注意i不要从0开始,因为0号位没有权值
cout << HT[i].weight << " " << HT[i].parent << " " <<HT[i].lchild << " "<< HT[i].rchild << endl;
}
}
//哈夫曼编码
typedef char **HuffmanCode;
void CreateHuffmanCode(HuffmanTree HT,HuffmanCode &HC,int n){
//从叶子到根逆向求每个字符的哈夫曼编码,存储在编码表HC中
HC=new char*[n+1];//分配存储n个字符编码的编码表空间,指针HC指向他们
char *cd=new char[n]; //分配临时存放每个字符编码的动态数组空间
cd[n-1]='\0';//开辟了n个空间,cd[n-1]是最后一格,放'\0'
int i;
for(i=1;i<=8;i++){
int start=n-1;//start开始时指向最后,即便编码结束符位置
int c=i;
int f=HT[i].parent;//f指向结点c的双亲结点
while(f!=0){
--start;//回溯一次start向前指一个位置
if(HT[f].lchild==c)
cd[start]='0';//结点c是f的左孩子,则生成代码0
else
cd[start]='1';//结点c是f的右孩子,则生成代码1
c=f;
f=HT[f].parent;//继续向上回溯
}//求出第i个字符的编码
HC[i]=new char[n-start];//为第i个字符编码分配空间
strcpy(HC[i],&cd[start]);//将求得的编码临时从cd复制到HC的当前行中
}
delete cd;//释放临时空间
}
int main(){
HuffmanTree HT;
int n=8;
CreateHuffmanTree(HT,n);
printHuffmanTree(HT,2*n-1);
HuffmanCode HC;
CreateHuffmanCode(HT,HC,8);
for(int i=1;i<=8;i++){
cout<< HC[i] <<endl;
}
system("pause");
}