哈夫曼编码原理解析及算法构造过程

哈夫曼编码原理解析及算法构造过程

一.哈夫曼编码

哈夫曼编码(Huffman Coding),又称霍夫曼编码,是一种编码方式,哈夫曼编码是可变字长编码(VLC)的一种。Huffman于1952年提出一种编码方法,该方法完全依据字符出现概率来构造异字头的平均长度最短的码字,有时称之为最佳编码,一般就叫做Huffman编码(有时也称为霍夫曼编码)

二.构造原理

要构造二叉树(哈夫曼树),二叉树的叶子节点才是用来存储数据的节点,取出的具体数据由给出的编码确定

每一组数据对应一个哈夫曼树不同文件的哈夫曼树一般是不一样的

三.构造方法

构造哈夫曼树

每一次取出出现概率(权值)最小的两个节点作为新节点的子节点(小左大右),新节点的权值即左右子节点权值之和,然后将新的节点放入原数据集合中,递归

译码

从建立好的哈夫曼树的头节点开始查找,找到叶子节点即返回一个数据,然后重新开始查找

不能用其他的树的编码来完成译码,容易出现内存访问错误

四.具体算法实现(C++)

首先确定数据符号有哪些以及每一种符号权值是多少(此处不做解释)

本算法以如下数据构件哈夫曼树

char c[8]={'a','b','c','d','e','f','g','h'};
//数据集合
int w[8]={5,29,7,8,14,23,3,11};
//对应权值
void sort(){
    //对应排序(此处冒泡)
	for(int i=0;i<7;i++){
		for(int j=0;j<7;j++){
			if(w[j]>w[j+1]){
				int t1=w[j];
				char t2=c[j];
				c[j]=c[j+1];
				w[j]=w[j+1];
				c[j+1]=t2;
				w[j+1]=t1;
			}
		}
	}
}

由于用到二叉树,基础节点定义如下:

struct ht{
	char data;
	int weight;
	ht *lc;//左孩子
	ht *rc;//右孩子
};

由于重复对数据进行排序,时间复杂度较高,故采用数组先排序,然后建立对应有序链表,每一次建立一个根节点就插入即可

struct l{
	ht *data;
	l *last;
	l *next;
}; //双链表结构

ht *h;//全局变量
void insert(ht *p,l *e){
    //插入算法
    //以待插入数据节点和链表头指针作为参数
	l *t=e;
	while(t->next!=NULL){
		if(p->weight<t->data->weight){
			l *n=(l *)malloc(sizeof(l));
			n->data=p;
			n->last=t->last;
			t->last->next=n;
			n->next=t;
			t->last=n;
			return;
		}
		t=t->next;
	}
	if(p->weight<t->data->weight){
		l *n=(l *)malloc(sizeof(l));
		n->data=p;
		n->last=t->last;
		t->last->next=n;
		n->next=t;
		t->last=n;
		return;
	}
	else{
		l *n=(l *)malloc(sizeof(l));
		n->data=p;
		n->last=t;
		n->next=NULL;
		t->next=n;
		return;
	}
    //各种条件使数据插入该插入的位置(此处不再解释)
}
void creat(l *&head){
    //创建树
    //以链表头指针作为参数
	if(head->next==NULL)return ;
    //终止构建条件(链表仅有一个节点)
	ht *t=(ht *)malloc(sizeof(ht));
	t->data='#';
    //此步骤可省略
	t->weight=head->data->weight+head->next->data->weight;
    //新节点权值
	t->lc=head->data;
	t->rc=head->next->data;
	insert(t,head);
	head=head->next->next;
    //取完数据指针后移两个
	creat(head);
}

设置读取方式

void find(ht *t,char *s){
	if(t->lc==NULL&&t->rc==NULL){
        //是叶子节点,得到数据,从头再来
		cout<<t->data;
		find(h,s);
	}
	else{
		if(*s=='0'){
			//0表示左,1表示右
			find(t->lc,++s);
		}
		else if(*s=='1'){
			find(t->rc,++s);
		}//其他情况终止查找
	}
}

检验

//测试数据如下:000100011011101110
int main(){
	sort();
	l *head=NULL;
	head=(l *)malloc(sizeof(l));
	l *temp=head;
	ht *p=(ht *)malloc(sizeof(ht));
	p->data=c[0];
	p->weight=w[0];
	p->lc=NULL;
	p->rc=NULL;
	temp->data=p;
	temp->last=NULL;
	temp->next=NULL;
	temp=temp->next;
	l *r=head;
	for(int i=1;i<8;i++){
		p=(ht *)malloc(sizeof(ht));
		temp=(l *)malloc(sizeof(l));
		temp->last=r;
		r->next=temp;
		temp->next=NULL;
		p->data=c[i];
		p->weight=w[i];
		p->lc=NULL;
		p->rc=NULL;
		temp->data=p;
		temp=temp->next;
		r=r->next;
	}
	creat(head);
	p=head->data;
	h=p;
	char s[10000];
	cin>>s;
	find(h,s);
	return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_44322945/article/details/89787963
今日推荐