05-树9 Huffman Codes

题目地址:05-树9 Huffman Codes

求解过程:

    判断组编码是否是Huffman编码需要满足两个条件:

        1. 没有共同前缀(这里需要说明一下,在信息论中前缀码表示的没有共同前缀的编码,因此这里我用没有共同前缀来表述)。

        2.  长度与Huffman编码长度相同

    我在这里用了一个最小堆来存放编码的长度,然后通过循环依次取出最小堆顶部的元素来构建Huffman树,通过Huffman树来计算WPL(最短带权路径长度)。然后通过对编码建树来判断是否有共同前缀和计算输入编码的WPL共同判定是否为Huffman编码。

奇怪的地方:

    不知道为什么我用(TNode*)malloc(sizeof(TNode))和new(TNode)会得到完全不同的结果,前者无法通过,会出现段错误,希望有大佬指教。

程序:

#include <iostream>
#include <string>
#include <cstdlib>
using namespace std;
#define MaxNum 64

typedef struct TNode *Huffman;
struct TNode
{
	int weight;
	TNode *left = NULL, *right = NULL;
	int flag = 0;	// 用于判定是否有共同前缀	
};

typedef struct HeapNode *MinHeap;	// 最小堆
struct HeapNode
{
	TNode Data[MaxNum];
	int size;
};

MinHeap CreateHeap(int MaxCapacity)		// 初始化最小堆
{
	MinHeap H = (MinHeap)malloc(sizeof(HeapNode));
	H->size = 0;
	H->Data[0].weight = -1;		// 第0个元素做哨兵,存最小元素
	return H;
}

void Insert(MinHeap H, TNode *item)		// 插入元素
{
	int i = ++(H->size);
	for (; H->Data[i/2].weight > item->weight; i /= 2)
		H->Data[i]= H->Data[i/2];
	H->Data[i] = *item;
}

TNode* DeleteMin(MinHeap H)
{
	TNode *item = (TNode*)malloc(sizeof(TNode));
	*item = H->Data[1];		// 取出第一个结点(0号元素是哨兵)
	TNode temp = H->Data[(H->size)--];		// 用最后一个元素来填充空白
	int child, parent;
	for (parent = 1; parent*2 <= H->size; parent = child)
	{
		child = parent * 2;
		if ((child != H->size) && (H->Data[child].weight > H->Data[child+1].weight))
			child++;
		if (temp.weight <= H->Data[child].weight)		// 如果找到合适的位置插入
			break;
		else
			H->Data[parent] = H->Data[child];
	} 
	H->Data[parent] = temp;
	return item;
}

MinHeap ReadNode(int N, MinHeap H, int NodeWeight[])		// 读取各个结点的权重并插入
{	/* NodeWeight 用于暂时存放结果*/
	char ch;
	int weight;
	for (int i = 0; i < N; i++)
	{
		cin >> ch >> weight;
		NodeWeight[i] = weight;
		// TNode* T = (Huffman)malloc(sizeof(TNode));		// 这两个有差别?
		TNode *T = new(TNode);
		T->weight = weight;
		Insert(H, T);
	}
	return H;
}


TNode* BuildHuffman(MinHeap H)		// 构造Huffman树
{	/* 注意,这里一定不能在for循环里面写H->size-1,因为
	   这样写的话,每次DeleteMin操作都会修改H->size的值*/
	int num = H->size-1;	
	TNode* T;
	for (int i = 0; i < num; i++)	// 只要合并size-1次即可
	{	
		T = (TNode*)malloc(sizeof(TNode));
		T->left = DeleteMin(H);
		T->right = DeleteMin(H);
		T->weight = T->left->weight + T->right->weight;
		Insert(H, T);
	}
	T = DeleteMin(H);
	return T;
}

int WPL(Huffman T, int depth)		// 计算带权路径长度
{
	if ((!T->left) && (!T->right))		// 如果是根结点
	{
		return (T->weight) * depth;
	}
	else	// 如果不是根结点
	{
		return (WPL(T->left, depth+1) + WPL(T->right, depth+1));
	}
}

bool Judge(string code, Huffman H)	// 判断是否有共同前缀
{
	int len = code.length(), i;
	for (i = 0; i < len; i++)
	{		
		if (code[i] == '0')		// 如果为0就往左子树生长
		{
			if (!H->left)		// 如果左子树为空
			{
				TNode *Node = new(TNode);
				H->left = Node;
			}
			else	// 如果左子树存在且已有结点,沿着该路径下去一定会变成前缀码
				if (H->left->flag == 1)	
					return false;
			H = H->left;
		}
		else
		{
			if (!H->right)		// 如果右子树为空
			{
				TNode *Node = new(TNode);
				H->right = Node;
			}
			else	// 如果右子树存在且已有结点,沿着该路径下去一定会变成前缀码
				if (H->right->flag == 1)
					return false;
			H = H->right;
		}			
	}
	H->flag = 1;	// 结束循环后,将该点flag置为1表示有结点
	if (!H->left && !H->right)	// 如果这个是根结点,说明是正确的,否则是前缀码
		return true;
	else
		return false;
}

int main()
{
	int N, M, HuffLen, testWPL;
	cin >> N;
	int NodeWeight[N];	// 保存结点权重
	MinHeap H = CreateHeap(N);	// 初始化最小堆
	H = ReadNode(N, H, NodeWeight);	// 建立最小堆
	Huffman T = BuildHuffman(H);	// 建立Huffman树
	HuffLen = WPL(T, 0);	// 计算带权路径
	cin >> M;
	char letter;	// 保存字母
	string code;	// 保存编码
	for (int i = 0; i < M; i++)
	{	/* 通过构造Huffman树来确定是否有共同前缀 */
		int Flag = 0; 	// 判断是否合法,0表示合法
		/* 想不明白下面两种写法有什么区别, 用上面的通不过 */
		// TNode *judge = (TNode*)malloc(sizeof(TNode));	
		TNode *judge = new(TNode);	
		testWPL = 0;		// 输入编码的WPL
		for (int j = 0; j < N; j++)
		{
			cin >> letter >> code;
			testWPL += code.length() * NodeWeight[j];
			if (!Flag)	
			{
				if (!Judge(code, judge))	// 如果是有共同前缀
					Flag = 1;
			}
		}
		/* 如果既没有共同前缀并且编码长度和Huffman编码长度相同 */
		if (!Flag && (testWPL == HuffLen))	
			cout << "Yes" << endl;
		else
			cout << "No" << endl;	
	}
	return 0;
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值