Huffman编码与解码


哈夫曼树又称为最优二叉树  哈夫曼编码是指一种可变长前缀码 即字符编码位数不统一 也无重复前缀  甚至有些情况下 哈夫曼编码被称为最优编码 于是着手简单的看了理论之后立即去实现  因为还要上课 总的时间大约花了我一天多时间结束 调完bug的一瞬间也是感觉世界真美好的 0-0


---C语言编程

---辅助栈手动实现

---重要函数及体现过程:

------------------------------ 统计字符概率(StatCodedChar )

------------------------------ 按照概率排序(BubbleSort(冒泡))

------------------------------ 创建哈夫曼树(CreateHuffmanTree)

------------------------------ 遍历树并将编码元素放入编码表(LoopFOrderTree(非递归前序))

------------------------------ 编码(CreateHuffmancode)

------------------------------ 解码(HuffmanDecoder)

------------------------------ 释放哈夫曼树(FreeHuffmanTree)

------------------------------ 释放编码表(FreeCoderTable)

---编码输出到文件

---编码表输出到文件


---可改进:在解码时从文件中读取编码表到程序 从而达到编码与解码的完全分离使用(To Do)


编码表输出文件结果:

编码输出文件结果:



#include <stdio.h>
#include <stdlib.h>
#include <windows.h>

#define TRUE 1
#define FALSE 0
#define BOOL int
#define LEFT_CODE '0'
#define RIGHT_CODE '1'

typedef struct huffmannode
{
	BOOL IsSrcChar;
	char CodingNum;
	int Weight;
	char CodedChar;
	struct huffmannode *pLeftChlid;
	struct huffmannode *pRightChlid;
	struct huffmannode *pFather;
	struct huffmannode *pNext;
}HuffmanNode;

typedef struct codertablenode
{
	char CodedChar;
	char* CoderNum;
	int CoderPlacesNum;
}CoderTableNode;

typedef struct codelist
{
	char CodedNum;
	struct codelist *pNext;
}CodeList;
typedef struct stack
{
	HuffmanNode *tree;
	struct stack *pNext;
}Stack;

typedef struct MyStack
{
	Stack *stack;
	int count;
}Stack_Top;


CoderTableNode *pCoderTable = NULL;//编码表
int CoderTableLen = -1;

//当前字符是否在之前出现过 return 出现过返回1 没有出现过:0
int IsAppearBefore(char src_string[],char src_char,int char_appear_index)
{
	while(char_appear_index > 0)
	{
		if(src_char == src_string[--char_appear_index]) return 1;
	}
	return 0;
}

//统计字符概率
HuffmanNode* StatCodedChar(char src_string[],int len_src)
{
	int count;
	HuffmanNode *pHead = NULL;
	HuffmanNode *pTemp = NULL;
	//进行统计
	for(count = 0;count<len_src;count++)
	{
		if( !IsAppearBefore(src_string,src_string[count],count) )
		{
			pTemp = (HuffmanNode *)malloc(sizeof(HuffmanNode));
			
			pTemp->IsSrcChar = TRUE;
			pTemp->CodingNum = LEFT_CODE;
			pTemp->Weight = 1;
			pTemp->CodedChar = src_string[count];
			pTemp->pRightChlid = NULL;
			pTemp->pLeftChlid = NULL;
			pTemp->pFather = NULL;

			//用pNext指针来贯穿链表 头添加
			if(pHead != NULL)
			{
				pTemp->pNext = pHead;
				pHead->pFather = pTemp;
			}
			else
			{
				pTemp->pNext = NULL;
				pTemp->pFather = NULL;
			}
			pHead = pTemp;
		}
		else
		{
			pTemp = pHead;
			while(pTemp)
			{
				if(pTemp->CodedChar == src_string[count])
				{
					pTemp->Weight++;
					break;
				}
				pTemp = pTemp->pNext;
			}
		}
	}
	return pHead;
}

//冒泡排序:小->大
void BubbleSort(HuffmanNode **pHead)
{
	char temp_char;
	int temp_weigtht;
	HuffmanNode *pTemp = NULL;
	HuffmanNode *pLoopPoint = NULL;
	HuffmanNode *pLoopPoint_in = NULL;
	if(pHead == NULL)return ;

	pTemp = pLoopPoint = *pHead;
	while(pLoopPoint->pNext)
	{
		pLoopPoint_in = pLoopPoint;
		while(pLoopPoint_in->pNext)
		{
			if(pLoopPoint_in->Weight > pLoopPoint_in->pNext->Weight)
			{
				temp_char = pLoopPoint_in->CodedChar;
				temp_weigtht = pLoopPoint_in->Weight;
				pLoopPoint_in->Weight = pLoopPoint_in->pNext->Weight;
				pLoopPoint_in->CodedChar = pLoopPoint_in->pNext->CodedChar;
				pLoopPoint_in->pNext->Weight = temp_weigtht;
				pLoopPoint_in->pNext->CodedChar = temp_char;
			}
			pLoopPoint_in = pLoopPoint_in->pNext;
		}

		pLoopPoint = pLoopPoint->pNext;
	}
}

//对已排序的数据建造哈夫曼树
HuffmanNode* CreateHuffmanTree(HuffmanNode **pHead)
{
	int count;
	HuffmanNode *pTemp = NULL;
	HuffmanNode *HeadLastPoint = NULL;
	if(pHead == NULL)return NULL;

	while(*pHead != NULL)
	{
		pTemp =(HuffmanNode *)malloc(sizeof(HuffmanNode));
		pTemp->pLeftChlid = *pHead;
		pTemp->pLeftChlid->CodingNum = LEFT_CODE;//左孩子编码值为0
		pTemp->pLeftChlid->pFather = pTemp;//更新父亲
		pTemp->pRightChlid = (*pHead)->pNext;
		pTemp->pRightChlid->CodingNum = RIGHT_CODE;//右孩子编码值为1
		pTemp->pRightChlid->pFather = pTemp;//更新父亲
		pTemp->IsSrcChar = FALSE;
		pTemp->CodingNum = LEFT_CODE;
		pTemp->Weight = (*pHead)->Weight + (*pHead)->pNext->Weight;
		pTemp->CodedChar = -1;
		pTemp->pFather = NULL;
		pTemp->pNext = NULL;

		for(count = 0;count<2;count++)//断开原有结构 头指针后移
		{
			HeadLastPoint = (*pHead);
			(*pHead) = (*pHead)->pNext;
			if(HeadLastPoint->IsSrcChar == TRUE)
			{
				HeadLastPoint->pNext = NULL;
			}
		}

		if(*pHead == NULL)
			break;

		HeadLastPoint = *pHead;
		while(pTemp->Weight > HeadLastPoint->Weight && HeadLastPoint->pNext != NULL)
		{
			HeadLastPoint = HeadLastPoint->pNext;
		}

		if(HeadLastPoint == *pHead && HeadLastPoint->Weight >= pTemp->Weight)
		{
			pTemp->pNext = *pHead;
			(*pHead)->pFather = pTemp;
			*pHead = pTemp;
		}
		else
		{
			if(HeadLastPoint->pNext == NULL && HeadLastPoint->Weight < pTemp->Weight)
			{
				HeadLastPoint->pNext = pTemp;
				pTemp->pFather = HeadLastPoint;
				pTemp->pNext = NULL;
			}
			else
			{
				HeadLastPoint->pFather->pNext = pTemp;
				pTemp->pNext = HeadLastPoint;
				pTemp->pFather = HeadLastPoint->pFather;
				HeadLastPoint->pFather = pTemp;
			}
		}
	}
	return pTemp;
}

int GetCharCount(HuffmanNode *pHead)
{
	int CharCount = 0;
	HuffmanNode *ptemp = NULL;
	if(pHead == NULL)return -1;
	ptemp = pHead;
	while(ptemp)
	{
		CharCount++;
		ptemp = ptemp->pNext;
	}
	return CharCount;
}

//-------------------------------辅助栈-------------------------------------
void InitStack(Stack_Top** stacktop)
{
	*stacktop = (Stack_Top *)malloc(sizeof(Stack_Top));
	(*stacktop)->stack = NULL;
	(*stacktop)->count = 0;
}

void StackPush(Stack_Top *stacktop,HuffmanNode* val)
{
	Stack *pTemp = NULL;

	pTemp = (Stack *)malloc(sizeof(Stack));
	pTemp->tree = val;
	pTemp->pNext = NULL;
	if(stacktop->stack != NULL)
	{
		pTemp->pNext = stacktop->stack;
	}
	stacktop->stack = pTemp;
	stacktop->count++;
	return ;
}
HuffmanNode* StackPop(Stack_Top *stacktop)
{
	HuffmanNode *val = NULL;
	if(stacktop->stack == NULL)
		return NULL;
	else
	{
		Stack *pTemp = stacktop->stack;
		val = (stacktop->stack)->tree;
		stacktop->stack = stacktop->stack->pNext;
		free(pTemp);
		stacktop->count--;
		return val;
	}
}
//-----------------------------辅助栈---------------------------------------

//编码并加入表中
void CallBackCode(HuffmanNode *Node,CoderTableNode *CoderTable)
{
	int count_Places;
	int count;
	int count_Add;
	HuffmanNode *pTemp = NULL;
	CodeList *pHead = NULL;
	CodeList *pDel = NULL;
	CodeList *pTemp_CodeList = NULL;
	if(Node == NULL || CoderTable == NULL)return ;

	count_Places = 0;
	pTemp = Node;
	while(pTemp->pFather)
	{
		pTemp_CodeList = (CodeList*)malloc(sizeof(CodeList));
		pTemp_CodeList->CodedNum = pTemp->CodingNum;

		//头添加
		if(pHead == NULL)
		{
			pTemp_CodeList->pNext = NULL;
		}
		else
		{
			pTemp_CodeList->pNext = pHead;
		}
		pHead = pTemp_CodeList;
		count_Places++;
		pTemp = pTemp->pFather;
	}

	//放入编码表
	for(count = 0;count<CoderTableLen;count++)
	{
		if(pCoderTable[count].CodedChar == -1)
			break;
	}
	pCoderTable[count].CodedChar = Node->CodedChar;
	pCoderTable[count].CoderPlacesNum = count_Places;
	pCoderTable[count].CoderNum = (char *)malloc(sizeof(char)*count_Places);
	
	for(count_Add = 0;count_Add<count_Places;count_Add++)
	{
		pCoderTable[count].CoderNum[count_Add] = pHead->CodedNum;
		pDel = pHead;
		pHead = pHead->pNext;
		free(pDel);
		pDel = NULL;
	}
}

//非递归前序遍历
void LoopFOrderTree(HuffmanNode *tree)
{
	Stack_Top *stack = NULL;
	if(tree == NULL) return ;

	InitStack(&stack);
	while(1)
	{
		while(tree != NULL)
		{
			StackPush(stack,tree);
			tree = tree->pLeftChlid;
		}
		tree = StackPop(stack);

		if(tree == NULL) return ;

		if(tree->IsSrcChar == TRUE)
		{
			CallBackCode(tree,pCoderTable);//编码并加入表中
		}
		tree = tree->pRightChlid;
	}
}

//编码
void CreateHuffmancode(char src_string[],int len_src)
{
	int count;
	int count_in;
	int count_print;
	CoderTableNode *pTemp = NULL;
	FILE *pFile = NULL;
	if(src_string == NULL || len_src <= 0) return ;
	pTemp = pCoderTable;

	if(fopen_s(&pFile,"./Huffmancode.txt","w") != 0)return ;
	printf(">>>编码结果输出文件在此程序源文件同目录下 名为:Huffmancode.txt\n\n");//编码结果输出文件
	for(count = 0;count<len_src;count++)
	{	
		for(count_in = 0;count_in<CoderTableLen;count_in++)
		{	
			if(pTemp[count_in].CodedChar == src_string[count])
			{
				for(count_print = 0;count_print<pTemp[count_in].CoderPlacesNum;count_print++)
				{
					//printf("%c",pTemp[count_in].CoderNum[count_print]);
					fputc(pTemp[count_in].CoderNum[count_print],pFile);
				}
				break;
			}
		}
	}
	printf(">>>编码完成\n\n");
	fclose(pFile);
}

//输出编码表到文件
void PrintToFileOfCoderTable()
{
	int count;
	int count_in;
	FILE *pFile = NULL;
	if(pCoderTable == NULL || CoderTableLen <= 0)return ;

	if(fopen_s(&pFile,"./CoderTable.txt","w") != 0)return ;
	printf(">>>编码表结果输出文件在此程序源文件同目录下 名为:CoderTable.txt\n\n");//编码表结果输出文件
	for(count = 0;count<CoderTableLen;count++)
	{
		fputc(pCoderTable[count].CodedChar,pFile);
		fputc(':',pFile);
		for(count_in = 0;count_in<pCoderTable[count].CoderPlacesNum;count_in++)
		{
			fputc(pCoderTable[count].CoderNum[count_in],pFile);
		}
		fseek(pFile,4,SEEK_CUR);
	}
}

//递归释放哈夫曼树
void FreeHuffmanTree(HuffmanNode *pHunffmanTree)
{
	if(pHunffmanTree == NULL)return ;
	FreeHuffmanTree(pHunffmanTree->pLeftChlid);
	FreeHuffmanTree(pHunffmanTree->pRightChlid);
	free(pHunffmanTree);
	pHunffmanTree = NULL;
}

//释放编码表
void FreeCoderTable()
{
	int count;
	if(pCoderTable == NULL)return ;
	for(count = 0;count<CoderTableLen;count++)
	{
		free(pCoderTable[count].CoderNum);
		pCoderTable[count].CoderNum = NULL;
	}
	free(pCoderTable);
	pCoderTable = NULL;
}

//前期准备过程   编码
void HuffmanEncoder(char src_string[],int len_src)
{
	int CharCount;
	HuffmanNode *pHead = NULL;
	HuffmanNode *pHunffmanTree = NULL;

	if(src_string == NULL || len_src <= 0) return ;

	//准备过程
	pHead = StatCodedChar(src_string,len_src);//统计字符概率
	BubbleSort(&pHead);//按照概率排序
	CharCount = GetCharCount(pHead);//统计编码元素个数
	pHunffmanTree = CreateHuffmanTree(&pHead);//创建哈夫曼树

	pCoderTable = (CoderTableNode *)malloc(sizeof(CoderTableNode)*CharCount);//申请编码表
	CoderTableLen = CharCount;
	memset(pCoderTable,-1,sizeof(CoderTableNode)*CharCount);
	LoopFOrderTree(pHunffmanTree);//遍历树并放入编码表
	PrintToFileOfCoderTable();//编码表结果输出文件
	CreateHuffmancode(src_string,len_src);//编码过程
	FreeHuffmanTree(pHunffmanTree);//回收空间
}

//按照编码表匹配并打印 返回编码占位数
int SearchIsRightAndPrint(char huffmancodechar[],int LoopCount)
{
	int count;
	int count_in;
	int LoopCountTemp;
	if(huffmancodechar == NULL || LoopCount < 0)return -1;

	//查找匹配
	for(count = 0;count<CoderTableLen;count++)
	{
		LoopCountTemp = LoopCount;
		for(count_in = 0;count_in<pCoderTable[count].CoderPlacesNum;count_in++)
		{
			if(pCoderTable[count].CoderNum[count_in] == huffmancodechar[LoopCountTemp])
			{
				LoopCountTemp++;
			}
			else
			{
				break;
			}
		}
		//匹配成功 打印
		if(count_in == pCoderTable[count].CoderPlacesNum)
		{
			printf("%c",pCoderTable[count].CodedChar);
			return pCoderTable[count].CoderPlacesNum;
		}
	}
	return -1;
}

//解码
void HuffmanDecoder(char *filename)
{
	int filesize;
	int SearchResult;
	int LoopCount;
	FILE *pFile = NULL;
	char *huffmancodechar = NULL;
	if(fopen_s(&pFile,filename,"r") != 0)return ;

	//得到编码
	fseek(pFile,0,SEEK_END);//偏移至文件尾
	filesize = ftell(pFile);//得到文件大小
	rewind(pFile);//重新指向文件开头
	huffmancodechar = (char *)malloc(sizeof(char)*filesize);
	memset(huffmancodechar,0,sizeof(char)*filesize);

	if( fgets(huffmancodechar,filesize+1,pFile) == NULL)
	{
		return ;
		//printf("%s",huffmancodechar);
	}
	fclose(pFile);

	//解码
	LoopCount = 0;
	while(LoopCount <= filesize)
	{
		SearchResult = SearchIsRightAndPrint(huffmancodechar,LoopCount);//按照编码表匹配并打印
		if(SearchResult == -1)
			break ;
		else
			LoopCount+=SearchResult;
	}
	printf("\n");
	FreeCoderTable();//释放编码表
}
int main()
{
	char arr[] = "fdg;d;gfherwrewrwqersddfshghdsfgghjdsfgfhasfashhsdtgsdghhhfghdhdfyuuyuoipasopuyiuhhfyuareryewgrelgeryuthreunrblerbytbyltjbysbhbewtbhetbhbtwebrltberwhtberlktbertbhewrlbtlhewrbtlethgsdfhgkdsfhglghsdfhgjldsfhgjhdsfglhdsflghdfhgjldsfhgjfdhgldshgdsflghdsfhgdsghjdhgdfhglkdffsgdsgsdgfgsgfhfdhdf";
	HuffmanEncoder(arr,strlen(arr));
	HuffmanDecoder("./Huffmancode.txt");
	system("pause");
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值