哈夫曼编码的实现

// Question.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//
#include "stdio.h"
#include "stdlib.h"
#define StringSize 30//输入最大字符串的大小

struct BiTreeNode//构造二叉树结构体
{
	char sign;//二叉树结点符号
	int power;//二叉树结点的权值
	int path;//哈弗曼编码的路径值
	struct BiTreeNode* L;//左指针
	struct BiTreeNode* R;//右指针
};

struct CodeStack//顺序栈用来存哈弗曼编码
{
	int path[StringSize];
	int top;//栈顶指针;
};

void Push(CodeStack *&code, int path)//入栈,存入哈夫曼代码,path为哈弗曼编码值
{
	code->top++;
	code->path[code->top] = path;
}

void HuffmanCode(BiTreeNode *p, CodeStack *code)//p为每一个节点,code是一个栈,用来存哈弗曼编码
{
	if (p != NULL)
	{
		if (p->path == 0 || p->path == 1)//如果为0或1就存入code
		{
			Push(code, p->path);//存储当前位置的path值,即哈夫曼编码元素
			p->path = code->top;//用path储存当前层数,用来之后再次访问调用(弹栈)
		}
		if (p->sign != '#')//读到了叶子结点
		{
			printf("字母:%c  权重:%02d 编码为:", p->sign, p->power);
			for (int x = 0; x <= code->top; x++)//从栈底开始输出哈弗曼编码
			{
				printf("%d", code->path[x]);
			}
			putchar('\n');//换行准备输出下一组哈弗曼编码
		}
		HuffmanCode(p->L, code);//左子树深度搜索
		code->top = p->path;//这里为访问头结点的位置,当做再次访问此结点。
		                    //将top重置为当前层数(path储存的当前层数)
		HuffmanCode(p->R, code);//右子树递归
	}
}

void RenewArray(char a[], int address)//删除相同字符,重新生成字符串
{
	while (a[address + 1] != '\0')//address之后的元素位置全部向前移动一位
		                          //即删除address位置的元素
	{
		a[address] = a[address + 1];
		address++;
	}
	a[address] = '\0';//最后一项为空,使得Size减小1
}

void SortArray(BiTreeNode *data[], int k)//哈夫曼数组根据权值大小排序
{
	int i, j;
	BiTreeNode *x;
	for (i = 0; i < k; i++)
		for (j = 0; j < k; j++)
			if (data[i]->power < data[j]->power)
			{
				x = data[i];
				data[i] = data[j];
				data[j] = x;
			}
}

void DeleteArray(BiTreeNode *data[], int* boundary)//删除数组中最小的两个元素
{
	int i = 0;
	while (data[i]->power != 0)//保证数据元素处理干净,多进行两次
	{
		data[i] = data[i + 2];
		i++;
	}
	*boundary = i - 2;//实际上多了一个,用来存放新构成的树
}

void InitiateStack(CodeStack *&code)//初始化栈
{
	code = new CodeStack;
	code->top = -1;
}

void CountPower(char store[], BiTreeNode *data[], int* boundary)//统计字符串中各字符的权值
                                                                //data为最终要获取的原始数组
{
	int i = 0, j = 0, k = 0;//i用来遍历字符串,j用来统计权值, k用来录入哈夫曼数组
	char flag;//flag存储要遍历寻找的对象
	while (store[0] != '\0')
	{
		flag = store[0];
		for (i = 0; store[i] != '\0';)
		{
			if (store[i] == flag)
			{
				RenewArray(store, i);//如果找到相同的字符
				                     //数组从该位置开始全部前移一位并继续从该位置遍历
				j++;
			}
			else//如果不相等便搜索下一个位置
				i++;
		}
		data[k]->sign = flag;
		data[k]->power = j;
		k++;//指向哈夫曼数组的下一个单元
		j = 0;//权值归零用来统计下一个字符的权值
	}
	*boundary = k;
}

void  InitiateStruct(BiTreeNode *data[])//初始化哈夫曼原始数组
{
	for (int i = 0; i < StringSize; i++)
	{
		data[i] = new BiTreeNode;
		data[i]->power = 0;
		data[i]->L = NULL;
		data[i]->R = NULL;
	}
}

void InitiateTree(BiTreeNode *data[], BiTreeNode **s, int* boundary)
{
	SortArray(data, *boundary);//数组按权值由小到大排序
	while (*boundary > 0)//每合成一个数就会删除两个小树
						 //并在数组中添加新的一棵树
						 //如此重复直到数组中只有一个元素
	{
		*s = new BiTreeNode;//生成新的头结点
		if (data[0]->power <= data[1]->power)//数组元素权值小的在左边并初始化哈弗曼编码
											 //如果有相等的则未合并的优先
		{
			data[0]->path = 0;
			data[1]->path = 1;
			(*s)->L = data[0];
			(*s)->R = data[1];
		}
		else
		{
			data[0]->path = 1;
			data[1]->path = 0;
			(*s)->L = data[1];
			(*s)->R = data[0];
		}
		(*s)->power = data[0]->power + data[1]->power;//头结点的权值是两个子树权值之和
		(*s)->sign = '#';//头结点的符号为“#”
		(*s)->path = -1;//根结点的路径默认为-1
		DeleteArray(data, boundary);//删除数组中最小的两个元素
		data[*boundary] = (*s);//数组末尾存放新生成的树
		if (*boundary < 0)//如果只剩下一棵树就不用排序
			break;
		else
			SortArray(data, *boundary + 1);//哈夫曼数组根据权值大小排序
	}
}

void OperateMode(char store[], BiTreeNode *data[], int *boundary)
{
	int flag;
	int i=0,j;
	char c;
	printf("请选择输入模式:1 输入字符串;2 输入字符及其权值:");
	scanf("%d",&flag);
	system("cls");
	switch (flag)
	{
		case 1:
		{
			printf("请输入字符串");
			scanf("%s", store);//输入字符串
			CountPower(store, data, boundary);//统计字符串中各字符的权值存入data中
			system("cls");
			break;
		}
		case 2: 
		{
			printf("当输入的符号为“#”时退出输入\n");
			while (1)
			{
				printf("请输入符号:");
				getchar();
				scanf("%c",&c);
				if (c == '#')
					break;
				else
					data[i]->sign = c;
				printf("请输入权重(正整数,等比例缩放):");
				scanf("%d",&j);
				data[i]->power = j;
				i++;
			}
			*boundary = i;
			system("cls");
			break;
		}
	}
}

void main()
{
	int boundary = 0;//boundary记录原始数据的个数
	char store[StringSize];//用来存入原始字符串
	BiTreeNode *data[StringSize];//用来存储哈夫曼树的原始数据
	BiTreeNode *head;//head用来作树的根
	CodeStack *code;//记录哈弗曼编码
	InitiateStack(code);//初始化哈弗曼编码
	InitiateStruct(data);//初始化最小二叉树
	OperateMode(store, data, &boundary);//模式选择
	InitiateTree(data, &head, &boundary);//构建哈夫曼树
	HuffmanCode(head, code);//输出哈弗曼编码
	getchar();
	getchar();
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值