数据结构-赫夫曼树

引言:

赫夫曼编码是属于一种变长编码形式。它在压缩领域有重要的运用。可以利用二叉树来构建赫夫曼树来对信号进行编码。该树的构造思想是:对于有n个叶子节点(既是需要编码的字符数)的赫夫曼树,应该有2*n-1个结点(整个赫夫曼树的总结点数)。在树进行构造的时候输入的参数是长度为2*n-1的静态链表(类型为赫夫曼树的数据类型)首指针,该静态链表的前n个位置存放叶子结点,后面的n-1个结点存放的是分支节点。

在初始化赫夫曼树时,把前面n个叶子结点的权值域与数据域用指定的值(字符数组与权值数组)进行填充,n-1个分支结点的权值域用0填充,其它域用-1进行填充。然后,在结点中寻找两个权值最小的结点node1与node2,最后用这两个权值最小的结点构造一个新的二叉树。对于已经构造了二叉树的结点,就不能在此参与构造新的二叉树了,但是这棵二叉树的根节点可以参加构造新二叉树。在初始化时,结点的双亲标志位(parent)被设置为-1,在构造二叉树时,参与构造二叉树的两个结点node1与node2的双亲标志位被设置为它们的根结点,这样它们的双亲标志位(域)就不是-1了,也就与未参与构造二叉树的结点区分开了。接下来上代码...

实现代码

赫夫曼树存储结构:

typedef char datatype;		//Huffman数据类型别名

typedef struct Node2 
{
	datatype data;			//数据域
	unsigned int weight;	//权值域
	int parent;				//父结点域
	int left;				//左孩子
	int right;				//右孩子
}HufNode, *PHufNode;<span style="font-family:Microsoft YaHei;">
</span>


赫夫曼树的构建:

/************************************************************************/
/*
初始化并且建立Huffman树
参数:weight			权值数组			n个
参数:data			数据数组			n个
参数:num				权值与数据的个数	n个
参数:hufnode			huffman节点数组	n-1个
返回值:成功true 失败false
*/
/************************************************************************/
bool CHuffman::InitHufTree(const int* weight, const datatype* data, const int num, PHufNode hfnode)
{
	//首先检查数据域是否为空 若为空返回
	if ((nullptr==weight) || (nullptr==data) || (nullptr==hfnode))
	{
		std::cout << "\nthere could be a empty input pointer,please check!\n";
		return false;
	}
	//接下来应该有weight与data数组元素个数是否相等之类的检查

	//初始化huffman树
	for (int i=0; i<(2*num-1); i++)
	{
		if (i < num)	//初始化num个叶子节点
		{
			hfnode[i].data = data[i];	//赋值数据域
			hfnode[i].weight = weight[i];	//赋值权值域
		}
		else	//初始化num-1个分支结点
		{
			hfnode[i].weight = 0;	//对于不是huffman叶子结点的结点的权值设置为0
		}
		hfnode[i].parent = -1;
		hfnode[i].left = -1;
		hfnode[i].right = -1;
	}

	unsigned int mw1(100), mw2(100);	//权值计算中间变量
	int node1(-1), node2(-1);	//结点连接中间变量
	for (int i=num; i<(2*num-1); i++)
	{
		mw1 = mw2 = 100;
		node1 = node2 = -1;
		for (int j=0; j<i; j++)	//在节点(父节点域=-1)中寻找两个权值最小的节点node1和node2
		{							//最后node2的权值大于node1的权值 因为node2在node1的右边
			if (-1 == hfnode[j].parent)
			{
				if (mw1 > hfnode[j].weight)
				{
					mw2 = mw1;
					node2 = node1;
					mw1 = hfnode[j].weight;
					node1 = j;
				}
				else if(mw2 > hfnode[j].weight)
				{
					mw2 = hfnode[j].weight;
					node2 = j;
				}				
			}			
		}
		//用node1和node2构造一颗新的二叉树,二叉树根的权值为node1和node2权值的和
		hfnode[i].weight = hfnode[node1].weight + hfnode[node2].weight;
		hfnode[node1].parent = i;
		hfnode[node2].parent = i;
		//填充选出来的根节点的左右孩子域
		hfnode[i].left = node1;
		hfnode[i].right = node2;
	}

	return true;
}


获取相应单个字符的编码:

/************************************************************************/
/* 
获取单个元素的Huffman编码
参数:hfnode				霍夫曼树
参数:data_pos			需要编码的霍夫曼字符	0~(N-1)
参数:N					编码的总个数
返回值:成功true 失败false
*/
/************************************************************************/
bool CHuffman::GetItemHufCode(PHufNode hfnode, const int data_pos, const int N)
{
	if (nullptr == hfnode)
	{
		std::cout << "\nempty input error!\n";
		return false;
	}
	if (data_pos > (N-1))
	{
		std::cout << "\nthe input pos is out of range!\n";
		return false;
	}

	unsigned int parent = hfnode[data_pos].parent;
	unsigned int left = data_pos;
	std::cout << "\n" <<  hfnode[data_pos].data << ":";
	while (-1 != parent)	//没有达到根节点
	{
		if (left == hfnode[parent].left)
			std::cout << "0";
		else
			std::cout << "1";
		left = parent;
		parent = hfnode[parent].parent;
	}

	return true;
}

赫夫曼树的层序遍历:

不是叶子结点的结点输出‘-’

void CHuffman::TravelByLevel(PHufNode hfnode, const int num)
{
	std::cout << "\n";
	std::vector<HufNode> vec;
	vec.push_back(hfnode[2*num-2]);
	unsigned int cur(0), end(1);
	while (cur < vec.size())
	{
		end = vec.size();
		while (cur < end)
		{
			if (NULL != vec[cur].data)
				std::cout << vec[cur].data << " ";
			else
				std::cout << "-" << " ";

			if (-1 != vec[cur].left)	//若左孩子不为空将其放入向量中
			{
				vec.push_back(hfnode[vec[cur].left]);	//压入数据
			}
			if (-1 != vec[cur].right)	//若右孩子不为空将其放入向量中
			{
				vec.push_back(hfnode[vec[cur].right]);	//压入数据
			}
			cur++;	
		}
		std::cout << "\n";
	}
}


上述如若有错欢迎指正^_^


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值