C语言-哈夫曼树、哈夫曼编码

  1. 哈夫曼树中的查找算法(Select)
  2. 哈夫曼树的构建(HuffmanTree)
  3. 哈夫曼编码的构建(HuffmanCoding)
  4. 打印哈夫曼树表(Print)
  5. 打印权值及其编码(Inputcode)

什么是哈夫曼树?

  1. 当有 n 个结点(都做叶子结点且都有各自的权值)构建一棵树时,如果构建的这棵树的带权路径长度(WPL)最小,称这棵树为“最优二叉树”,有时也叫“赫夫曼树”或者“哈夫曼树”(HuffmanTree)。
  2. 权值可以理解为访问频率,权值越大访问频率越高,访问次数越多;而带权路径长度=权值 x 该节点到树根的路径长度——WPL。
  • 总而言之,哈夫曼树所构建出来的树,会将最频繁被访问的结点放在最前面,不常访问的结点依次往后排,这样的话每次都能最快速的查找到最常被访问的结点。

如何构建哈夫曼树?

  1. 选取并合并:选取取值最小的两个结点合并成一棵树(根节点为权值之和)
  2. 删除并加入:从序列中删除上述选取的两个两个最小结点,加入新合并的树
  3. 重复:重复上述操作即可得到哈夫曼树
  • 不难发现在中有两个权值都为5的结点,此时选取任意一个都是可行的,所以哈夫曼树的构造是不唯一的,但是带权路径长度WPL是唯一的

代码实现哈夫曼树的构造

  • 首先我们要知道,我们的哈夫曼树使用的是结构体数组,每一个结点有4个域。
  • 拥有n个叶子结点的哈夫曼树,共有2n-1个结点
typedef struct
{
	int weight;//权值
	int parent,rchild,lchild;//存储地址(下标)
}HTnode;//每一个域存储的都是地址(数组下标)
  1. 我们需要建立起哈夫曼树组HuffTree[2n-1] (如下图,哈夫曼数组用HT[ ]代替)
    在这里插入图片描述
  2. 数组初始化:建立好有2n-1个单元格的哈夫曼数组后要对每个单元初始化。
    初始化内容包括:
    ① 把每个结点的地址域(parent,lchild,rchild)赋值为-1
    ② 给每个节点填上(输入)对应的权值。

  3. 完成前两步的初始化才开始构建哈夫曼树: ① 选出权值最小的两个结点(select函数)②找父母 ③找孩子 (见下列代码第30-40行
HTnode *HuffmanTree(HTnode *HuffTree,int n)
{
	int i;//i处理a[0]-a[n] 
	int k;//k处理a[n+1]-a[2n-1] 
	
//1.建立HuffTree数组( HuffTree=&HuffTree[0]).	
	if(n<=1)
	{
		return;
	}
	
	HuffTree=(HTnode *)malloc((2*n-1)*sizeof(HTnode));//建立哈夫曼树组,有2n-1个结点,每个节点有4个域 
	
//2.给HuffTree数组的地址域初始化为-1.
	for(i=0;i<2*n-1;i++)  
	{//对2n-1个结点进行初始化 
		HuffTree[i].parent=-1;
		HuffTree[i].lchild=-1;
		HuffTree[i].rchild=-1; 
	} 
	
//3.将权值写入HuffTree数组中.
	printf("Please Enter Weights in Turn:");
	for(i=0;i<n;i++) 
	{
		scanf("%d",&HuffTree[i].weight);
	} 

//4.构建哈夫曼树.
	for(k=n;k<2*n-1;k++)
	{
	    int s1,s2;//第一小,第二小的地址下标
		select(HuffTree,k,&s1,&s2);//1.选择出权值第一小s1,和第二小s2(s1,s2是下标、地址,不是数值!)  
		HuffTree[k].weight=HuffTree[s1].weight+HuffTree[s2].weight;//2.权值加和 
		HuffTree[s1].parent=k;//3.s1,s2找父母(默认左孩子是第一小,右孩子是第二小) 
		HuffTree[s2].parent=k;
		HuffTree[k].lchild=s1; //4.父母K找孩子 
		HuffTree[k].rchild=s2;
	} 
	
	return HuffTree; 
}

如何构造select函数,选出权值最小的两个结点?

  1. 可行的方法 排序:在权值数组w[ ]中,我们可以先对数组元素排序,然后取走第一小和第二小。 升级版选择排序:我写了一个哈夫曼树中的查找算法(Select),是选择排序的升级版。
  2. 需要注意的问题:① 只对parent=-1的结点进行查找 ② 我们要查找的是第一小、第二小在哈夫曼树组中的下标,而不是数值!
void select(HTnode *HuffTree,int k,int *s1,int *s2)
{
	int min1=9999,min2=9999;
	int i; 
	int m1,m2;//用于储存坐标
	
	for(i=0;i<k;i++)
	{
		if(HuffTree[i].parent==-1)//判断父母为-1的结点参与比较 
		{
			   if(HuffTree[i].weight<min2)//小于第二小 
		   {
			     if(HuffTree[i].weight<min1)//不但小于第二小,还小于第一小 
			   {
				  min2=min1;
				  min1=HuffTree[i].weight;
			   	  m1=i;//储存第一小的坐标 (地址) 
			   }
			     else //小于第二小,但大于第一小 
			   {
			 	  min2=HuffTree[i].weight; 
				  m2=i;//储存第一小的坐标(地址) 
			   } 
		   }
		}
	
	}
	*s1=m1;//将第一小的坐标带入指针中传出 
	*s2=m2;//将第二小的坐标带入指针中传出
}

哈夫曼编码

  1. 什么是哈夫曼编码?
  • 哈夫曼编码是不等长编码。
  • 哈夫曼编码就是在哈夫曼树的基础上构建的,这种编码方式最大的优点就是用最少的字符包含最多的信息内容
  1. 哈夫曼编码的优越性在哪?
  • 哈夫曼编码是前缀编码(前缀永不重叠),且是最优前缀编码!因为哈夫曼编码是前缀永不重叠的前缀编码,保证了编码的可读取性可解码性,与此同时结合自身是不等长编码的特性,实现了与等长编码一样的编码解码功能,但编码更短,数据量更少。
    在这里插入图片描述
  • 看完上图,你就能明白什么叫用最少的字符包含最多的信息。

如何手动构建哈夫曼编码?

  • 手动构建哈夫曼编码非常简单,只需要构建出哈夫曼树,然后按"左0右1"(左1右0也是可以的)给每个左右孩子标记上,最后从根节点走向每一个叶子结点,就可以得到每个叶子节点的哈夫曼编码。

如何用程序构建哈夫曼编码?

  • 用程序构建哈夫曼编码比我们手动构建哈夫曼编码复杂得多。
char **HuffmanCoding(HTnode *HuffTree,char **Huffcode,int n)//哈弗曼编码 
{
	char *temp;//临时存储,用于存放生成的编码
	int start;//用于记录temp数组的下标
	int i,pos,parent;//用于记录HuffTree数组的下标 
	
	Huffcode=(char **)malloc(n*sizeof(char *));//建立哈夫曼编码数组 
	temp=(char *)malloc(n*sizeof(char));//n个叶子结点最长生成编码数为n-1位,故申请n个存储空间的数组(char temp[n])
	temp[n-1]='\0';//初始化数组最后一位为‘\0 ’
	
	for(i=0;i<n;i++)//n个叶子结点,循环n次,每一次对一个叶子结点进行编码 
	{
		start=n-1;//temp数组将从后向前读入每一位编码
		pos=i; 
		parent=HuffTree[i].parent;
		
		while(parent!=-1)
		{
			if(HuffTree[parent].lchild==pos)//左0
			{
				temp[--start]='0'; 
			}
			else//右1 
			{
				temp[--start]='1';
			}
			
			pos=parent;//将pos移位 
			parent=HuffTree[parent].parent;//将parent移位 
		}//while
		Huffcode[i]=(char *)malloc((n-start)*sizeof(char));
		strcpy(Huffcode[i],&temp[start]);//将temp中由start开始的字符串拷贝进哈夫曼树组中 
		
	}//for
	free(temp);//释放零时存储 
	return Huffcode;//也可以定义成无返回值函数,则Huffcode要以char ***Huffcode进入函数 
}
  1. 临时数组temp:
  • 为什么需要一个临时存储数组temp?
    因为程序在检索哈夫曼树的时候,是先由根节点往下检索到叶子结点,然后在判断当前叶子结点是左孩子还是右孩子,生成0或1,再逐步往上走(走向根节点)。也就是说程序的执行过程是:根节点—>叶子结点,叶子结点—>根节点,由根节点开始先找到叶子结点,再返回去生成哈夫曼编码。那么这样生成的哈夫曼编码和我们手动生成的编码顺序颠倒了过来,生成的是逆序编码,所以我们需要用一个临时数组temp先逆序存储生成的逆序编码(逆序编码被逆序存储故变为正序),在正序输出即可。
  • 临时数组temp该定义多长呢?
    该定义成有n个单元格的数组,因为哈夫曼树共有n个叶子结点,可能生成最长的编码是n-1,算上最后一个'\0',一共需要n个单元格。
  • 'Huffcode[i]=(char *)malloc((n-start)*sizeof(char));'上代码第31行),我们知道哈夫曼编码是逆序在temp数组中生成的,每生成一位start就减1,那么这个(n-start)是什么意思呢?
    在这里插入图片描述
    所以我们知道了生成的编码长度就可以用malloc申请到对应的存储空间。
  1. 哈夫曼编码数组:
  • Huffcode[i]数组的下标与哈夫曼数组HuffTree[i](0≤ i ≤n)的前n个元素一一对应,也就是n个叶子结点。也就是说哈夫曼数组HuffTree[ ]中第i个元素的哈夫曼编码储存在Huffcode[i]中(0≤ i ≤n)。
  • 哈弗曼编码并不是直接存储在Huffcode[ ]数组的每一个单元格当中,生成的哈夫曼编码一串字符串,而Huffcode[ ]数组就是指向这些字符串的指针,所以哈夫曼编码数组Huffcode[ ]中的每一个元素都是指针

完整哈夫曼树、哈夫曼编码源代码

(内涵打印函数,可直接展示结果)

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
 
typedef struct
{
	int weight;
	int parent,rchild,lchild;
}HTnode;

void select(HTnode *HuffTree,int k,int *s1,int *s2)
{
	int min1=9999,min2=9999;
	int i; 
	int m1,m2;
	
	for(i=0;i<k;i++)
	{
		if(HuffTree[i].parent==-1)//判断父母为-1的结点参与比较 
		{
			   if(HuffTree[i].weight<min2)//小于第二小 
		   {
			     if(HuffTree[i].weight<min1)//不但小于第二小,还小于第一小 
			   {
				  min2=min1;
				  min1=HuffTree[i].weight;
			   	  m1=i;//储存第一小的坐标 (地址) 
			   }
			     else //小于第二小,但大于第一小 
			   {
			 	  min2=HuffTree[i].weight; 
				  m2=i;//储存第一小的坐标(地址) 
			   } 
		   }
		}
	
	}
	
	*s1=m1;//将第一小的坐标带入指针中传出 
	*s2=m2;//将第二小的坐标带入指针中传出
}

HTnode *HuffmanTree(HTnode *HuffTree,int n)
{
	int i;//i处理a[0]-a[n] 
	int k;//k处理a[n+1]-a[2n-1] 
	
//1.建立HuffTree数组( HuffTree=&HuffTree[0]).	
	if(n<=1)
	{
		return;
	}
	
	HuffTree=(HTnode *)malloc((2*n-1)*sizeof(HTnode));//建立哈夫曼树组,有2n-1个结点,每个节点有4个域 
	
//2.给HuffTree数组初始化为-1.
	for(i=0;i<2*n-1;i++)  
	{//对2n-1个结点进行初始化 
		HuffTree[i].parent=-1;
		HuffTree[i].lchild=-1;
		HuffTree[i].rchild=-1; 
	} 
	
//3.将权值写入HuffTree数组中.
	printf("Please Enter Weights in Turn:");
	for(i=0;i<n;i++) 
	{
		scanf("%d",&HuffTree[i].weight);
	} 

//4.构建哈夫曼树.
	for(k=n;k<2*n-1;k++)
	{
	    int s1,s2;
		select(HuffTree,k,&s1,&s2);//1.选择出权值第一小s1,和第二小s2(s1,s2是下标、地址,不是数值!)  
		HuffTree[k].weight=HuffTree[s1].weight+HuffTree[s2].weight;//2.权值加和 
		HuffTree[s1].parent=k;//3.s1,s2找父母(默认左孩子是第一小,右孩子是第二小) 
		HuffTree[s2].parent=k;
		HuffTree[k].lchild=s1; //4.父母K找孩子 
		HuffTree[k].rchild=s2;
	} 
	
	return HuffTree;//将建立好的哈夫曼数组地址带回主函数
	                也可以定义成无返回值函数,则HuffTree要以char **Huffcode进入函数  
}

char **HuffmanCoding(HTnode *HuffTree,char **Huffcode,int n)//哈弗曼编码 
{
	char *temp;//临时存储,用于存放生成的编码
	int start;//用于记录temp数组的下标
	int i,pos,parent;//用于记录HuffTree数组的下标 
	
	Huffcode=(char **)malloc(n*sizeof(char *));//建立哈夫曼编码数组 
	temp=(char *)malloc(n*sizeof(char));//n个叶子结点最长生成编码数为n-1位,故申请n个存储空间的数组(char temp[n])
	temp[n-1]='\0';//初始化数组最后一位为‘\0 ’
	
	for(i=0;i<n;i++)//n个叶子结点,循环n次,每一次对一个叶子结点进行编码 
	{
		start=n-1;//temp数组将从后向前读入每一位编码
		pos=i; 
		parent=HuffTree[i].parent;
		
		while(parent!=-1)
		{
			if(HuffTree[parent].lchild==pos)//左0
			{
				temp[--start]='0'; 
			}
			else//右1 
			{
				temp[--start]='1';
			}
			
			pos=parent;//将pos移位 
			parent=HuffTree[parent].parent;//将parent移位 
		}//while
		Huffcode[i]=(char *)malloc((n-start)*sizeof(char));
		strcpy(Huffcode[i],&temp[start]);//将temp中由start开始的字符串拷贝进哈夫曼树组中 
		
	}//for
	free(temp);//释放零时存储 
	return Huffcode;//也可以定义成无返回值函数,则Huffcode要以char ***Huffcode进入函数 
}

void print(HTnode *HuffTree,int m)//打印哈夫曼树表 
{
	int i;
	
	printf("\n");
	printf(" --------------------------------------------- \n");
	printf("  Huffman Tree:\n\n");
	printf("	Loc	weight	parent	lchild	rchild\n");
	for(i=0;i<m;i++)
	{
	    printf("	HT[%d]	%d	%d	%d	%d\n",i,HuffTree[i].weight,HuffTree[i].parent,HuffTree[i].lchild,HuffTree[i].rchild);
	}
	printf("\n --------------------------------------------- \n");
}

void Inputcode(char **Huffcode,HTnode *HuffTree,int n)//打印权值对应其哈弗曼编码 
{
	int i;
	printf(" Huffman code:\n\n");
	for(i=0;i<n;i++)
	{   
		printf("	Weight:%d	code:%s\n",HuffTree[i].weight,Huffcode[i]);
	}
}

int main() 
{
	HTnode *HuffTree;
	char **Huffcode;//Huffcode是指向字符指针类型数组的指针 
	
	int n;//n个叶子结点 
	int i; 

	printf("Please Enter the Number of Elements:");
	scanf("%d",&n);
	printf("\n");
	
	HuffTree=HuffmanTree(HuffTree,n);
	print(HuffTree,2*n-1);
	
    Huffcode=HuffmanCoding(HuffTree,Huffcode,n);
    Inputcode(Huffcode,HuffTree,n);
    
	return 0;
}

执行结果


其他问题

  1. 在哈夫曼树组HuffTree[i]与哈夫曼编码数组Huffcode[i]中,均采用的是用指针指向用malloc所申请的连续的数组存储区域,也就是说HuffTree和Huffcode是指针,在此处均采用以指针做数组名的方式对数组元素进行访问。(了解“指针如何做数组名
  2. HuffTree、Huffcode变量是指针,故在主函数中应当定义成相应的指针类型,HuffTree是 HTnode *HuffTree;,而Huffcode指向的又是一个指针数组,所以Huffcode要定义成指向指针的指针char **Huffcode;二重指针!
  3. HuffmanTree和HuffmanCoding函数涉及到传参的问题,两个函数走的流程都是:传入一个变量->对变量进行操作修改->将变量传出。那我们如何能将操作完后的变量传出带回主函数呢?
    ① return,把两个函数写成带返回值的函数,最后将地址传出即可(上文所使用的方法)
    ② 传入函数的时候用指针做形参(写法如下)
//HT为地址传递的存储哈夫曼树的数组,w为存储结点权重值的数组,n为结点个数
void HuffmanTree(HTnode **HT, int *w, int n)
{
    if(n<=1) return; // 如果只有一个编码就相当于0
    int m = 2*n-1; // 哈夫曼树总节点数,n就是叶子结点
    *HT = (HTnode *) malloc((m+1) * sizeof(HTnode)); // 0号位置不用
    HTnode *p = *HT;
    // 初始化哈夫曼树中的所有结点
    for(int i = 1; i <= n; i++)
    {
        (p+i)->weight = *(w+i-1);
        (p+i)->parent = 0;
        (p+i)->left = 0;
        (p+i)->right = 0;
    }
    //从树组的下标 n+1 开始初始化哈夫曼树中除叶子结点外的结点
    for(int i = n+1; i <= m; i++)
    {
        (p+i)->weight = 0;
        (p+i)->parent = 0;
        (p+i)->left = 0;
        (p+i)->right = 0;
    }
    //构建哈夫曼树
    for(int i = n+1; i <= m; i++)
    {
        int s1, s2;
        Select(*HT, i-1, &s1, &s2);
        (*HT)[s1].parent = (*HT)[s2].parent = i;
        (*HT)[i].left = s1;
        (*HT)[i].right = s2;
        (*HT)[i].weight = (*HT)[s1].weight + (*HT)[s2].weight;
    }
}
//HT为哈夫曼树,HC为存储结点哈夫曼编码的二维动态数组,n为结点的个数
void HuffmanCoding(HTnode **HT, char ***HC,int n){
    *HC = (HTnode *) malloc((n+1) * sizeof(char *));
    char *cd = (char *)malloc(n*sizeof(char)); //存放结点哈夫曼编码的字符串数组
    cd[n-1] = '\0';//字符串结束符
   
    for(int i=1; i<=n; i++){
        //从叶子结点出发,得到的哈夫曼编码是逆序的,需要在字符串数组中逆序存放
        int start = n-1;
        //当前结点在数组中的位置
        int c = i;
        //当前结点的父结点在数组中的位置
        int j = HT[i].parent;
        // 一直寻找到根结点
        while(j != 0){
            // 如果该结点是父结点的左孩子则对应路径编码为0,否则为右孩子编码为1
            if(HT[j].left == c)
                cd[--start] = '0';
            else
                cd[--start] = '1';
            //以父结点为孩子结点,继续朝树根的方向遍历
            c = j;
            j = HT[j].parent;
        }
        //跳出循环后,cd数组中从下标 start 开始,存放的就是该结点的哈夫曼编码
        (*HC)[i] = (char *)malloc((n-start)*sizeof(char));
        strcpy((*HC)[i], &cd[start]);
    }
    //使用malloc申请的cd动态数组需要手动释放
    free(cd);
}

注:以上两段代码引用参考1


参考:

1.部分文字及源代码参考 哈夫曼树(最优数)—解学武
2. 部分图片来自于漫画:“哈夫曼编码” 是什么鬼?
3. 数据结构(C语言第二版,严蔚敏)

#include #include #include #include using namespace std; # define MaxN 100//初始设定的最大结点数 # define MaxC 1000//最大编码长度 # define ImpossibleWeight 10000//结点不可能达到的权值 # define n 26//字符集的个数 //-----------哈夫曼树的结点结构类型定义----------- typedef struct //定义哈夫曼树各结点 { int weight;//权值 int parent;//双亲结点下标 int lchild;//左孩子结点下标 int rchild;//右孩子结点下标 }HTNode,*HuffmanTree;//动态分配数组存储哈夫曼树 typedef char**HuffmanCode;//动态分配数组存储哈夫曼编码表 //-------全局变量-------- HuffmanTree HT; HuffmanCode HC; int *w;//权值数组 //const int n=26;//字符集的个数 char *info;//字符值数组 int flag=0;//初始化标记 //********************************************************************** //初始化函数 //函数功能: 从终端读入字符集大小n , 以及n个字符和n个权值,建立哈夫曼树,并将它存于文件hfmTree中 //函数参数: //向量HT的前n个分量表示叶子结点,最后一个分量表示根结点,各字符的编码长度不等,所以按实际长度动态分配空间 void Select(HuffmanTree t,int i,int &s1,int &s2) { //s1为最小的两个值中序号最小的那个 int j; int k=ImpossibleWeight;//k的初值为不可能达到的最大权值 for(j=1;j<=i;j++) { if(t[j].weight<k&&t[j].parent==0) {k=t[j].weight; s1=j;} } t[s1].parent=1; k=ImpossibleWeight; for(j=1;j<=i;j++) { if(t[j].weight0),构造哈夫曼树HT,并求出n个字符的哈弗曼编码HC { int i,m,c,s1,s2,start,f; HuffmanTree p; char* cd; if(num<=1) return; m=2*num-1;//m为结点数,一棵有n个叶子结点的哈夫曼树共有2n-1个结点,可以存储在一个大小为2n-1的一维数组中 HT=(HuffmanTree)malloc((m+1)*sizeof(HTNode));//0号单元未用 //--------初始化哈弗曼树------- for(p=HT+1,i=1;iweight=*w; p->parent=0; p->lchild=0; p->rchild=0; } for(i=num+1;iweight=0; p->parent=0; p->lchild=0; p->rchild=0; } //--------建哈夫曼树------------- for(i=num+1;i<=m;i++) { Select(HT,i-1,s1,s2);//在HT[1...i-1]选择parent为0且weight最小的两个结点,其序号分别为s1和s2 HT[s1].parent=i; HT[s2].parent=i; HT[i].lchild=s1; HT[i].rchild=s2;//左孩子权值小,右孩子权值大 HT[i].weight=HT[s1].weight+HT[s2].weight; } //-------从叶子到根逆向求每个字符的哈弗曼编码-------- HC=(HuffmanCode)malloc((num+1)*sizeof(char *));//指针数组:分配n个字符编码的头指针向量 cd=(char*)malloc(n*sizeof(char*));//分配求编码的工作空间 cd[n-1]='\0';//编码结束符 for(i=1;i<=n;i++)//逐个字符求哈弗曼编码 { start=n-1;//编码结束符位置 for(c=i,f=HT[i].parent;f!=0;c=f,f=HT[f].parent)//从叶子到跟逆向求哈弗曼编码 if(HT[f].lchild==c) cd[--start]='0';//判断是左孩子还是右孩子(左为0右为1) else cd[--start]='1'; HC[i]=(char*)malloc((num-start)*sizeof(char*));//按所需长度分配空间 int j,h; strcpy(HC[i],&cd[start]); } free(cd); } //****************初始化函数****************** void Initialization() { flag=1;//标记为已初始化 int i; w=(int*)malloc(n*sizeof(int));//为26个字符权值分配空间 info=(char*)malloc(n*sizeof(char));//为26个字符分配空间 ifstream infile("ABC.txt",ios::in); if(!infile) { cerr<<"打开失败"<<endl; exit(1); } for(i=0;i>info[i]; infile>>w[i]; } infile.close(); cout<<"读入字符成功!"<<endl; HuffmanCoding(HT,HC,w,n); //------------打印编码----------- cout<<"依次显示各个字符的值,权值或频度,编码如下"<<endl; cout<<"字符"<<setw(6)<<"权值"<<setw(11)<<"编码"<<endl; for(i=0;i<n;i++) { cout<<setw(3)<<info[i]; cout<<setw(6)<<w[i]<<setw(12)<<HC[i+1]<<endl; } //---------将建好的哈夫曼树写入文件------------ cout<<"下面将哈夫曼树写入文件"<<endl; ofstream outfile("hfmTree.txt",ios::out); if(!outfile) { cerr<<"打开失败"<<endl; exit(1); } for(i=0;i<n;i++,w++) { outfile<<info[i]<<" "; outfile<<w[i]<<" "; outfile<<HC[i+1]<<" "; } outfile.close(); cout<<"已经将字符与对应的权值,编码写入根目录下文件hfmTree.txt"<<endl; } //*****************输入待编码字符函数************************* void Input() { char string[100]; ofstream outfile("ToBeTran.txt",ios::out); if(!outfile) { cerr<<"打开失败"<<endl; exit(1); } cout<<"请输入你想要编码的字符串(字符个数应小于100),以#结束"<>string; for(int i=0;string[i]!='\0';i++) { if(string[i]=='\0') break; outfile<<string[i]; } cout<<"获取报文成功"<<endl; outfile.close(); cout<<"------"<<"已经将报文存入根目录下的ToBeTran.txt文件"<<endl; } //******************编码函数**************** void Encoding() { int i,j; char*string; string=(char*)malloc(MaxN*sizeof(char)); cout<<"下面对根目录下的ToBeTran.txt文件中的字符进行编码"<<endl; ifstream infile("ToBeTran.txt",ios::in); if(!infile) { cerr<<"打开失败"<<endl; exit(1); } for(i=0;i>string[i]; } for(i=0;i<100;i++) if(string[i]!='#') cout<<string[i]; else break; infile.close(); ofstream outfile("CodeFile.txt",ios::out); if(!outfile) { cerr<<"打开失败"<<endl; exit(1); } for(i=0;string[i]!='#';i++) { for(j=0;j<n;j++) { if(string[i]==info[j]) outfile<<HC[j+1]; } } outfile<<'#'; outfile.close(); free(string); cout<<"编码完成------"; cout<<"编码已写入根目录下的文件CodeFile.txt中"<<endl; } //******************译码函数**************** void Decoding() { int j=0,i; char *code; code=(char*)malloc(MaxC*sizeof(char)); char*string; string=(char*)malloc(MaxN*sizeof(char)); cout<<"下面对根目录下的CodeFile.txt文件中的代码进行译码"<<endl; ifstream infile("CodeFile.txt",ios::in); if(!infile) { cerr<<"打开失败"<<endl; exit(1); } for( i=0;i>code[i]; if(code[i]!='#') { cout<<code[i]; } else break; } infile.close(); int m=2*n-1; for(i=0;code[i-1]!='#';i++) { if(HT[m].lchild==0) { string[j]=info[m-1]; j++; m=2*n-1; i--; } else if(code[i]=='1') m=HT[m].rchild; else if(code[i]=='0') m=HT[m].lchild; } string[j]='#'; ofstream outfile("TextFile.txt",ios::out); if(!outfile) { cerr<<"打开失败"<<endl; exit(1); } cout<<"的译码为------"<<endl; for( i=0;string[i]!='#';i++) { outfile<<string[i]; cout<<string[i]; } outfile<<'#'; outfile.close(); cout<<"------译码完成------"<<endl; cout<<"译码结果已写入根目录下的文件TextFile.txt中"<<endl; free(code); free(string); } //*************打印编码函数**************** void Code_printing() { int i; char *code; code=(char*)malloc(MaxC*sizeof(char)); cout<<"下面打印根目录下文件CodeFile.txt中的编码"<<endl; ifstream infile("CodeFile.txt",ios::in); if(!infile) { cerr<<"打开失败"<<endl; exit(1); } for( i=0;i>code[i]; if(code[i]!='#') cout<<code[i]; else break; } infile.close(); cout<<endl; ofstream outfile("CodePrin.txt",ios::out); if(!outfile) { cerr<<"打开失败"<<endl; exit(1); } for(i=0;code[i]!='#';i++) { outfile<<code[i]; } outfile.close(); free(code); cout<<"------打印结束------"<<endl; cout<<"该字符形式的编码文件已写入文件CodePrin.txt中"<<endl; } //*************打印哈夫曼树函数**************** int numb=0; void coprint(HuffmanTree start,HuffmanTree HT) //start=ht+26这是一个递归算法 { if(start!=HT) { ofstream outfile("TreePrint.txt",ios::out); if(!outfile) { cerr<<"打开失败"<rchild,HT); //递归先序遍历 cout<<setw(5*numb)<weight<rchild==0) cout<<info[start-HT-1]<<endl; outfile<weight; coprint(HT+start->lchild,HT); numb--; outfile.close(); } } void Tree_printing(HuffmanTree HT,int num) { HuffmanTree p; p=HT+2*num-1; //p=HT+26 cout<<"下面打印赫夫曼树"<<endl; coprint(p,HT); //p=HT+26 cout<<"打印工作结束"<<endl; } //*************主函数************************** int main() { char choice; do{ cout<<"************哈弗曼编/译码器系统***************"<<endl; cout<<"请选择您所需功能:"<<endl; cout<<":初始化哈弗曼树"<<endl; cout<<":输入待编码字符串"<<endl; cout<<":利用已建好的哈夫曼树进行编码"<<endl; cout<<":利用已建好的哈夫曼树进行译码"<<endl; cout<<":打印代码文件"<<endl; cout<<":打印哈夫曼树"<<endl; cout<<":退出"<<endl; if(flag==0) { cout<<"请先初始化哈夫曼树,输入I"<<endl; cout<<""<>choice; switch(choice) { case 'I':Initialization();break; case 'W':Input();break; case 'E':Encoding();break; case 'D':Decoding();break; case 'P':Code_printing();break; case 'T':Tree_printing(HT,n);break; case 'Q':;break; default:cout<<"输入的命令出错,请重新输入!"<<endl; } }while(choice!='Q'); free(w); free(info); free(HT); free(HC); system("pause"); return 0; }
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Attract1206

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值