这一节讲述二叉树的运用用于通讯运用中的编码与解码,依然是参照<零基础学算法>的学习心得及自己的理解来阐述,如果对二叉树基本性质不了解的可以查看基本部分:二叉树基本了解
赫夫曼中的二叉树和我们普通的二叉树有点不同,不同点在于赫夫曼二叉树中没有普通二叉树的左右子节点,没有指针,但是取而代之是两个指示左右孩子的两个整型值left,right当然还增加了一个指示父节点的parent以便查找,另外一个全局的用于保存编码的指针,请看结构体中的定义:
//定义赫夫曼二叉树结构体
typedef struct
{
int weight;
int parent;
int left;
int right;
}HuffmanTree;
//定义赫夫曼编码指针
typedef char* HuffmanCode;
这个结构体当中保存了节点的权值,父节点,子节点.在看创建赫夫曼二叉树之前先来看张创建图:如下:
以上叶子节点中保存的是权值,如我们所了解的创建赫夫曼树的过程可以说是自下而上的过程,每次都找出对于的无父节点的权值最小的两个节点组成一个新的节点,其中的0,1是下面将会看到的运用到编码中的二进制值,左为0右为1
下面看看是如何创建赫夫曼树的吧:
void CreateHuffmanTree(HuffmanTree *T,int n,int *w) { //定义总的节点数 int i, m; int bt1,bt2; m=2*n-1; for(i=1;i<=m;i++) { if(i<=n) T[i].weight=w[i-1]; else T[i].weight=0; T[i].parent=0; T[i].left=0; T[i].right=0; } for(i=n+1 ; i<=m ; ++i) { SelectNode(T,i-1,&bt1,&bt2); T[bt1].parent=i; T[bt2].parent=i; T[i].left=bt1; T[i].right=bt2; T[i].weight=T[bt1].weight+T[bt2].weight; } }
函数参数中是一个赫夫曼树的指针,也就说用来保存赫夫曼树的是一维的数组ht,将叶节点的数据保存到数组的低下标处,见上述程序的第一个循环处,先初始化叶子节点的权值到数组的低下标处,其他都初始化为零,所有节点都无父节点这是为后面生成赫夫曼树做准备的。在这也可看到有一个SelectNode()函数这个函数的目的是每次都从无父节点的节点中选择权值最小的两个节点来生成另一个节点,具体代码示例如下:
以上代码比较简单就是简单的查找然后,再返回下标值,也就是二叉树中的节点在数组中的下标值,如图:void SelectNode(HuffmanTree *ht,int n,int *bt1,int *bt2) { int i; HuffmanTree *ht1,*ht2,*t; ht1=ht2=NULL; for(i=1;i<=n;i++) { if(ht1==NULL) { ht1=ht+i; continue; } if(ht2==NULL) { ht2=ht+i; if(ht1->weight>ht2->weight) { t=ht1; ht1=ht2; ht2=t; } continue; } if(ht1 && ht2) { if(ht[i].weight<=ht1->weight) { ht2=ht1; ht1=ht+i; } else if(ht[i].weight<ht2->weight) ht2=ht+i; } if(ht1>ht2) { *bt2=ht1-ht; *bt1=ht2-ht; } else { *bt1=ht1-ht; *bt2=ht2-ht; } } }
接下来看看是如何编码的吧,这部分知识刚开始看起来还真的会头有点晕啊:
其中cur为指向当前节点的值,parent为指向父节点的值,start是用来指向保存编码最后一个下标的值,程序中hc是一个指向赫夫曼编码表的指针,可以看做是一个二维指针,每次生成的编码值都存放在编码表hc中,且每次生成时都是自底向上的,从叶子节点开始依次去查找父节点,看当前父节点是否为零,即查找到了根节点则停止查找,完成了一个叶节点中数据的编码.这部分知识应该说是后面知识的主体,欢迎大家相互讨论,这样会在交流中加深理解,有不当之处希望指正,谢谢啦!//赫夫曼编码,n代表需要生产的赫夫曼编码数,即叶子节点数 void HuffmanCoding(HuffmanTree *ht,int n,HuffmanCode *hc) { char *cd; //start从尾部逐次递减 int start,i; int cur,parent; cd=(char*)malloc(sizeof(char)*n); cd[n-1]='\0'; for(i=1;i<=n;i++) { start=n-1; cur=i; parent=ht[cur].parent; while(parent) { if(cur==ht[parent].left) cd[--start]='0'; else cd[--start]='1'; cur=parent; parent=ht[parent].parent; } hc[i-1]=(char*)malloc(sizeof(char)*(n-start)); strcpy(hc[i-1],&cd[start]); } free(cd); }
下面附上编码与解码代码,这里就不做具体介绍了,如果明白了上述代码应该可以理解:
二叉树的知识就总结到这了,接下来我们该学习图了,哎哎,又是一次纠结的过程加油啦!//将字符串转成赫夫曼编码 void Encode(HuffmanCode *hc,char* alphabet,char* str,char* code) { int len=0,i=0,j; code[0]='\0'; while(str[i]) { j=0; while(alphabet[j]!=str[i]) j++; strcpy(code+len,hc[j]); len=len+strlen(hc[j]); i++; } code[len]='\0'; } //赫夫曼的解码 void Decode(HuffmanTree* ht,int m,char* code,char *alphabet,char* decode) { int pos=0,i,j=0; m=2*m-1; while(code[pos]) { for(i=m;ht[i].left && ht[i].right; pos++) { if(code[pos]=='0') i=ht[i].left; else i=ht[i].right; } decode[j]=alphabet[i-1]; j++; } decode[j]='\0'; }