哈夫曼樹原理簡介:
是一種編碼方式,哈夫曼編碼是可變字長編碼(VLC)的一種。該方法完全依據字符出現概率來構造異字頭的平均長度最短的碼字,有時稱之爲最佳編碼,一般就叫做Huffman編碼(有時也稱爲霍夫曼編碼)。
如:字符串 “jdlasjdlajhfhioe”
在此字符串中每個字符的權重爲: (出現的次數)
編碼方式爲:
選取權重最小的倆個字符 進行合併
新生成節點的權重爲2 將e ,f 節點從原數據中刪除,將新節點插入到原數據,並繼續選取權重最小的節點進行組合,直到剩一個節點時,此節點便是哈夫曼樹的頭結點;
生成的樹形左枝編碼爲 0,右枝編碼爲 1;得到每個字符的編碼;
<pre name="code" class="cpp">/*
1. 將提供的字符串(自定義字符串)進行排序,獲取各個字符的權重;
2. 將字符及對應的權重放入樹節點(node)中,用鏈表將各個節點有序的(按權重升序)鏈接;
3. 實現鏈表的增、刪功能;
4. 遍歷鏈表,將鏈表的前兩個節點中權重相加,生成新節點,然後將新節點插入到有序鏈表中;
5. 直到鏈表中只剩一個節點時,將此節點賦給哈夫曼樹頭;
6. 利用創建的哈夫曼樹得到編碼; 用遞歸得到葉子節點,由葉子節點追溯到根節點,得到編碼後反轉順序;
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAXL 20 //自定義字符串的最大長度;
typedef struct _node
{
char val;
int weight;
struct _node *p;
struct _node *l;
struct _node *r;
}node; //樹節點
typedef struct l_node
{
node *ln;
struct l_node *prev;
struct l_node *next;
}lnode;//鏈表節點
typedef struct
{
node *head;
}huf_tree;//樹頭結點
typedef struct
{
lnode *head;
}list;//鏈表頭
char arr[ MAXL ];//有效字符串數組;排重之後的數組,和權重數組一一對應;
int w_a[ MAXL ];//權重數組;
void get_weight(char *ch,int c_long)//從排完序的字符串中得到有效字符和對應的權重;
{
char a_s=ch[0];
int i=0,j=0;
while(i<c_long)
{
if(ch[i]==a_s)
{
w_a[j]++;
i++;
} else{
arr[j]=a_s;
j++;
a_s=ch[i];
}
}
arr[j]=a_s;
}
int quick_part(char *s,int low,int hight)//對字符串進行快排,
{
char f_s=s[low];
int i,j;
i=low; j=hight;
while(i<j){
while(i<j&&s[j]>=f_s) j--;
if(i<j) s[i++]=s[j];
while(i<j && s[i]<=f_s) i++;
if(i<j) s[j]=s[i];
}
s[i]=f_s;
if((i-1)>low)
quick_part(s,low,i-1);
if((i+1)<hight)
quick_part(s,i+1,hight);
}
void r_arr(char *p) //反轉字符串
{
int sz=strlen(p);
int i=0;
for(i;i<sz-i;i++)
{
char t;
t=p[i];
p[i]=p[sz-i-1];
p[sz-i-1]=t;
}
}
void print_tree( node *n)//遍歷哈夫曼樹
{
node *nd=n;
if(nd!=NULL){
print_tree(nd->r);
print_tree(nd->l);
node *sn=nd;
if(nd->val != '\0'){
printf("%c ",nd->val);
char p[10]={};
int i=0;
while(sn->p != NULL)
{
if(sn->p->l==sn){
p[i]='0';
i++;
}
if(sn->p->r==sn){
p[i]='1';
i++;
}
sn=sn->p;
}
r_arr(p);
printf("%s\n",p);
}
}
}
void del_list(list *lst,lnode *p)//刪除鏈表裏面的指定節點;
{
lnode *lnd=lst->head->next;
while(lnd!=NULL){
if(lnd == p)
{
if(lnd->next == NULL){
lst->head->next=NULL;
free(p);
}else{
lnd->prev->next=lnd->next;
lnd->next->prev=lnd->prev;
node *s=p->ln;
free (p);
}
break;
}
lnd=lnd->next;
}
}
void add_list(list *lst,node *nd)//向鏈表中添加節點;
{
lnode *sn=lst->head;
lnode *new_node=(lnode*)malloc(sizeof(lnode));
new_node->prev=NULL;
new_node->next=NULL;
new_node->ln=nd;
if(lst->head->next==NULL)
{
lst->head->next=new_node;
new_node->prev=lst->head;
return ;
}
while(sn->next!=NULL)
{
if(sn->next->ln->weight > nd->weight)
{
new_node->prev = sn->next->prev;
new_node->next = sn->next;
sn->next=new_node;
new_node->next->prev=new_node;
sn=sn->next;
break;
}else {
sn=sn->next;
}
}
if(sn->next==NULL){
sn->next=new_node;
new_node->prev=sn;
}
}
void print_list(list *lst)//打印鏈表;
{
lnode *lnd=lst->head->next;
while(lnd!=NULL)
{
printf("val= %c ,weight= %d \n",lnd->ln->val,lnd->ln->weight);
lnd=lnd->next;
}
}
void add_tree(huf_tree *hr, list *lst)//創建哈夫曼樹
{
node *hnd=hr->head;
lnode *lnd=lst->head->next;
//循環:每次將權重最小的兩個節點合併成一個新節點,刪除權重最小的這兩個節點,並將新節點插入鏈表
while(lnd!=NULL && lnd->next!=NULL)
{
node *n_node=(node*)malloc(sizeof(node));
n_node->val='\0';
n_node->weight = lnd->ln->weight + lnd->next->ln->weight;
n_node->l=lnd->ln;
n_node->r=lnd->next->ln;
n_node->p=NULL;
lnd->ln->p=n_node;
lnd->next->ln->p=n_node;
del_list(lst,lnd);
del_list(lst,lnd->next);
add_list(lst,n_node);
//printf("=====================\n");
//print_list(lst);
lnd=lst->head->next;
}
if(lst->head->next != NULL)//鏈表中只剩下一個節點時,將此節點的數據賦給哈夫曼樹的頭結點;
hr->head=lst->head->next->ln;
}
void de_tree(huf_tree *ht,char *dp)
{
int sz=strlen(dp);
int i=0;
node *hn=ht->head;
for(i=0;i<sz&&hn!=NULL;i++)
{
if(dp[i]=='0')hn=hn->l;
if(dp[i]=='1')hn=hn->r;
if(hn->val!='\0'){
printf("%c",hn->val);
hn=ht->head;
}
}
printf("\n");
}
int main(int argv,char **argc)
{
huf_tree ht;
list lst;
int i=0;
ht.head=(node*)malloc(sizeof(node));
ht.head->p=NULL;
ht.head->l=NULL;
ht.head->r=NULL;
lst.head=(lnode*)malloc(sizeof(lnode));
lst.head->next=NULL;
lst.head->prev=NULL;
strcpy(arr,"jdlasjdlajhfhioe"); //自定義字符串數組
quick_part(arr,0,15);//快排
get_weight(arr,strlen(arr));//獲取各個字符的權重;
for(i=0;w_a[i]!=0;i++)//創建有序鏈表(以權重排序)
{
node *nd=(node*)malloc(sizeof(node));
nd->val=arr[i];
nd->weight=w_a[i];
nd->p=NULL;
nd->l=NULL;
nd->r=NULL;
add_list( &lst,nd);
}
print_list(&lst);//打印鏈表
add_tree(&ht,&lst);//創建哈夫曼樹;
print_tree(ht.head);//打印樹,包括字符的編碼,編碼未保存,直接打印出來了;
if(argv==2)
de_tree(&ht,argc[1]);//解碼
}
運行結果1 :只編碼 不解碼
運行結果2 編碼後 繼續解碼
用哪棵樹編碼 就用哪棵樹解碼;