贪心算法 之 哈夫曼编码

  • Post author:
  • Post category:其他


贪心算法 之 哈夫曼编码

1.最优二叉树:(哈夫曼树)

1>结点的权:赋予叶子结点以个有意义的值;

2>结点的路径长度:从根结点到当前结点的的长度

结点的带权路径长度:W*L (W:权 L:路径长度)

3>二叉树的带权路径长度:一颗二叉树的所有叶子结点的带权路径长度之和

4>最优二叉树:一颗二叉树的带权路径最小,就是最优二叉树

2.哈夫曼树的特点:

1>没有单分支;

2>当叶子为n时,双亲为n-1;

构造一颗哈夫曼树:

(1)根据给定的n个权值{w1, w2,…,wn},构成n棵二叉树的集合F= {T1, T2,…,Tn},其中每棵二叉树Ti中只有一个带权为wi的根节点,其左右子树均空。

在F中选取两棵权值最小的二叉树作为左、右子树构造一棵新的二叉树,置新 构造二叉树的根节点的权值为其左、右子树根节点的权值之和。

从F中删除这两棵树,同时将新得到的二叉树加入到F中。

重复(2)、(3),直到F中只含一棵树时为止。这棵树便是最优二叉树(哈夫曼树)。 从以上叙述可知,哈夫曼树中权值最小的两个节点互为兄弟节点。

例子:

设有一英文文章,共有A,B,C,D四种字符,它们的出现频率依次为F{9,3,6,4},

算法一:链式哈夫曼树

思想:

1>从森林{9,3,6,4}中选取最小和次小的两颗二叉树构成一颗新的二叉树,并放回森林。(左子女最小,右子女次小)

2>重复第一步,直到森林中只剩一颗二叉树;

算法描述:实现链式哈夫曼树:

数据结构:结点:

typedef struct node{

char word;

int weight;

struct node *left,*right;

}HufNode;

森林:

有N个叶子结点的初始森林(数组)

1>定义森林:

HufNode **F;
F=(HufNode**)malloc(n*sizeof(HufNode*));

2>初始化森林:

HufNode *p;
char a[]={A,B,C,D};
int b[]={9,3,6,4};
for(int i=0;i<n;i++){
p=(HufNode*)malloc(sizeof(HufNode));
p->word=a[i];
p->right=p->left=^;
F[i]=p;
}

实现最优二叉树:

for(int loop=1;loop<n;loop++){                  //找n-1次最小次小
复习:找最小次小;
min=0;
minx=1;
for(int i=1;i<n;i++){
if(a[i]<a[min]){
minx=min;
min=i;
}
else if(a[i]<a[minx])
minx=i;
}
}

实践 1 //第一种 找最小次小,解决重复值,含^的情况

for(int i=1;i<n;i++){
int min,minx,t;             //最小,次小
for(min=0;min<n&&(!F[min]);min++);                   //找到第一个非空元素
for(minx=min+1;minx<n&&(!F[min]);minx++);            //找到第二个非空元素
for(t=minx;t<n;t++){                          //找最小次小
if(F[t]){
if(F[t]->weight<F[min]->weight){
minx=min;
min=t;
}
else if(F[t]->weight<F[minx]->weight){
minx=t;
}
}
}         

生成二叉树;

p=(HufNode*)malloc(sizeof(HufNode));           //生成双亲结点
p->word=‘x’;
p->weight=F[min]->weight+F[minx]->weight;
p->left=F[min];           //最小
p->right=F[minx];            //次小
F[min]=p;                         //放回数组
F[minx]=^;
}

哈夫曼树的输出:

算法二:以数组实现哈夫曼树

1.存储:

typedef struct{
char word;
int weight;
int left,right,parent;
int *cale                    //编码
}Huff;

实现结构体数组:

6=2*4-1

Huff *F;
F=(Huff*)malloc((2*n-1)sizeof(Huff));
//初始化
for(int i=4;i<n;i++){
printf("请输入:");
scanf("%c",&ch);
F[i].word=ch;
scanf("%d",&we);
F[i].weight=we;
}
//建立关系:
int      i,min,minx;
for(int loop=0;loop<n-1;loop++){
for(min=0;min<n+loop&&F[min].parint!=-1;min++);
for(minx=0;i<minx+loop&&F[minx].parint!=-1;minx++);
for(i=0;i<n+loop;i++){

}
}

编码:

例:给B编码,找到B的双亲结点,判断B是双亲的左子女还是右子女,来编码一位,再查找双亲的双亲,循环执行,直到找到根结点为止。

存储结构:

用一个n-1的int数组来存放编码,下标为0的空间存放编码长度(岗哨),从下标1开始正式存放编码。

C语言描述:

//哈夫曼编码  数组实现
#include  <stdio.h>
#include  <stdlib.h>

typedef struct node{
	char word;   //字符 
	int weight;   //字符的权 
	int left;       //左子女的下标 
	int right;      //右子女的下标 
	int parint;     //双亲结点的下标 
	int *code;     //编码数组的首地址 
}Huffnode;


void CreatHuffmanTree(Huffnode *F,int n);      //函数声明 
void CreatHuffmancode(Huffnode *F,int n); 
void PrintHuffmancode(Huffnode *F,int n);

int main (void){
	Huffnode *F;
	int n,w[4]={9,3,6,4};
	char ch[4]={'A','B','C','D'};
	printf("请输入:");
	scanf("%d",&n); 
	//创建森林
	F=(Huffnode*)malloc((2*n-1)*sizeof(Huffnode));
	for(int i=0;i<n;i++){
		F[i].word=ch[i];
		F[i].weight=w[i];
		F[i].left=F[i].right=F[i].parint=-1;
	}
	//创建哈夫曼树
	CreatHuffmanTree(F,n);
	//创建 哈夫曼编码

	CreatHuffmancode(F,n); 
	//输出
	PrintHuffmancode(F,n); 
	free(F);
}
void CreatHuffmanTree(Huffnode *F,int n){	//创建哈夫曼树
	int loop=0;
	int k1,k2;//最小次小 
	for(loop=0;loop<n-1;loop++){
		for(k1=0;k1<n+loop&&F[k1].parint!=-1;k1++);   //找到第一个元素 
		for(k2=k1+1;k2<n+loop&&F[k2].parint!=-1;k2++);          //找到第二个元素 
		for(int i=k2;i<loop+n;i++){            //找到最小,次小 
			if(F[i].parint==-1){
				if(F[i].weight<F[k1].weight){
					k2=k1;
					k1=i;
				}
				else if(F[i].weight<F[k2].weight){
					k2=i;
				}	
			}
		}
		F[n+loop].weight=F[k1].weight+F[k2].weight;
		F[n+loop].left=k1;    //最小在左子女
		F[n+loop].right=k2;         //次小在右子女
		F[n+loop].parint=-1;
		F[n+loop].word='X';
		F[k1].parint=F[k2].parint=n+loop;
	}
}


void CreatHuffmancode(Huffnode *F,int n){            //创建哈夫曼编码 
	int c,pa;
	int *p;
	for(int i=0;i<n;i++){
		F[i].code=p=(int*)malloc(n*sizeof(int));
		p[0]=0;                    //p[0]用来充当岗哨 
		c=i;
		while(F[c].parint!=-1){             //当找到根结点时终止循环 
			pa=F[c].parint;
			if(c==F[pa].left){
				p[++p[0]]=0;
			}
			else{
				p[++p[0]]=1; 
			}
			c=pa;               //再找双亲的双亲 
		}
	}
}

void PrintHuffmancode(Huffnode *F,int n){
	for(int j=0;j<n;j++){
		printf("%c的编码是:",F[j].word);
		for(int i=F[j].code[0];i>0;i--){                       //由子女找双亲,编的码,故倒着输出 
			printf("%d",F[j].code[i]);
		}
		printf("\n");
	}
} 



版权声明:本文为zuo_h_dr原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。