哈夫曼樹編碼及解碼,鏈表實現

哈夫曼樹原理簡介:

是一種編碼方式,哈夫曼編碼是可變字長編碼(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   編碼後  繼續解碼



用哪棵樹編碼 就用哪棵樹解碼;

發佈了32 篇原創文章 · 獲贊 23 · 訪問量 14萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章