数据结构慕课PTA 05-树9 Huffman Codes

题目内容

In 1953, David A. Huffman published his paper "A Method for the Construction of Minimum-Redundancy Codes", and hence printed his name in the history of computer science. As a professor who gives the final exam problem on Huffman codes, I am encountering a big problem: the Huffman codes are NOT unique. For example, given a string "aaaxuaxz", we can observe that the frequencies of the characters 'a', 'x', 'u' and 'z' are 4, 2, 1 and 1, respectively. We may either encode the symbols as {'a'=0, 'x'=10, 'u'=110, 'z'=111}, or in another way as {'a'=1, 'x'=01, 'u'=001, 'z'=000}, both compress the string into 14 bits. Another set of code can be given as {'a'=0, 'x'=11, 'u'=100, 'z'=101}, but {'a'=0, 'x'=01, 'u'=011, 'z'=001} is NOT correct since "aaaxuaxz" and "aazuaxax" can both be decoded from the code 00001011001001. The students are submitting all kinds of codes, and I need a computer program to help me determine which ones are correct and which ones are not.

输入格式

Each input file contains one test case. For each case, the first line gives an integer N (2≤N≤63), then followed by a line that contains all the N distinct characters and their frequencies in the following format:

c[1] f[1] c[2] f[2] ... c[N] f[N]
where c[i] is a character chosen from {'0' - '9', 'a' - 'z', 'A' - 'Z', '_'}, and f[i] is the frequency of c[i] and is an integer no more than 1000. The next line gives a positive integer M (≤1000), then followed by M student submissions. Each student submission consists of N lines, each in the format:
c[i] code[i]
where c[i] is the i-th character and code[i] is an non-empty string of no more than 63 '0's and '1's.

输出格式

For each test case, print in each line either "Yes" if the student's submission is correct, or "No" if not.
Note: The optimal solution is not necessarily generated by Huffman algorithm. Any prefix code with code length being optimal is considered correct.

输入样例(对应图1):

7
A 1 B 1 C 1 D 3 E 3 F 6 G 6
4
A 00000
B 00001
C 0001
D 001
E 01
F 10
G 11
A 01010
B 01011
C 0100
D 011
E 10
F 11
G 00
A 000
B 001
C 010
D 011
E 100
F 101
G 110
A 00000
B 00001
C 0001
D 001
E 00
F 10
G 11

输出样例

Yes
Yes
No
No

单词

redundancy

英 /rɪ'dʌnd(ə)nsɪ/ 美 /rɪ'dʌndənsi/
n. [计][数] 冗余(等于redundance);裁员;人浮于事

hence

英 /hens/ 美 /hɛns/
adv. 因此;今后

professor

英 /prə'fesə/ 美 /prə'fɛsɚ/
n. 教授;教师;公开表示信仰的人

respectively

英 /rɪ'spektɪvlɪ/ 美 /rɪ'spɛktɪvli/
adv. 分别地;各自地,独自地

compress

英 /kəm'pres/ 美 /kəm'prɛs/
vt. 压缩,压紧;精简
vi. 受压缩小

correct

英 /kə'rekt/ 美 /kə'rɛkt/
adj. (政治或思想)正确的;恰当的;端正的
v. 改正;批改(学生作业);校正;指出错误;抵消;校准(仪器);修正、调整(数据)

submit

英 /səb'mɪt/ 美 /səb'mɪt/
vi. 服从,顺从
vt. 使服从;主张;呈递;提交

distinct

英 /dɪ'stɪŋ(k)t/ 美 /dɪ'stɪŋkt/
adj. 明显的;独特的;清楚的;有区别的

submission

英 /səb'mɪʃ(ə)n/ 美 /səb'mɪʃən/
n. 投降;提交(物);服从;(向法官提出的)意见;谦恭

optimal

英 /'ɒptɪm(ə)l/ 美 /'ɑptəml/
adj. 最佳的;最理想的

prefix

英 /'priːfɪks/ 美 /'prifɪks/
n. 前缀
vt. 加前缀;将某事物加在前面

题目分析

本题算是树类题中最难的一道了,考察了二叉堆、哈夫曼树等问题的运用,其中必须要认识到编码最优解并不一定要由哈夫曼树构造得来,这要WPL的值为最优即可。
根据题目的提示,我们发现最优的字符编码方式需要满足:
1.首先WPL的值应该为最小值(与构造的哈夫曼树一致即可保证)。
2.要保证不会出现一个字符的编码是另一个字符的前缀(即保证要计算的结点都出现在叶子结点)。
要满足第一点,我们可以构造一个哈夫曼树,然后计算其WPL值(注意:虽然WPL的最小值不一定要考哈夫曼树构造,但是哈夫曼树构造的一定是WPL最小的情况),构造哈夫曼树,我们需要用到二叉堆,具体实现见下面的代码。
要满足第二点,我选择的方法是根据用户所给的编码自己构造一个树,如果编码为0且上一个结点没有左子树就malloc一个新节点(新节点一律把frequency设为0),如果有那么把左子树置为上一个结点。如果为1就看右子树,如此循环,结束时在最后一个结点把frequency赋给它(因为编码顺序是和输入时一致的,所以我构造了一个全局数组fre[],来依次存放frequency)。
当在编码路径上遇到frequency不为0的结点说明其遇到了叶子结点,这说明本次的编码一定出现了不满足第二点的情况。或者是一次编码路径结束后,最后一个结点的左右子树不全为空,这也说明了本次编码一定出现了前缀冲突的情况。

代码实现

#include
#include

typedef struct HFMTreeNode *Node;
typedef Node Root;
struct HFMTreeNode
{
	int frequency;
	Node lchild;
	Node rchild;
};//哈夫曼树结构

Node *MinHeap;
int HeapLength;
//最小堆

int M, N;
int *fre;//这个变量用来按顺序保存C[i]的frequency。

void create_heap(int maxsize);
void insert_heap(int pos, Node n);//因为要用DeleteMin来构造哈夫曼树,所以插入时要保存结点的左右子树。
Node DeleteMin_heap(void);
//最小堆操作

Root create_hfmtree(void);
int count_wpl(Root,int);
//哈夫曼树操作

bool is_prefix_true(Root);
//判断前缀是否正确

void create_heap(int maxsize)
{
	fre = (int*)malloc((maxsize - 1) * sizeof(int));
	MinHeap = (Node*)malloc(maxsize * sizeof(Node));
	HeapLength = 0;
	MinHeap[0] = (Node)malloc(sizeof(struct HFMTreeNode));//将首结点置为0,也可以置为一个负数,总之要保证其比所有的可能频率值小即可
	MinHeap[0]->frequency = 0;
	MinHeap[0]->lchild = NULL;
	MinHeap[0]->rchild = NULL;
	for (int i = 1; i < maxsize; i++)
	{
		char word;
		int frequency;
		getchar();//因为scanf接收空格,所以这里用一个getchar将其读取掉。
		scanf("%c %d", &word, &frequency);
		fre[i - 1] = frequency;
		Node n= (Node)malloc(sizeof(struct HFMTreeNode));
		n->frequency = frequency;
		n->lchild = NULL;
		n->rchild = NULL;
		insert_heap(i, n);
	}	
}

void insert_heap(int pos, Node n)
{
	while (n->frequency < MinHeap[pos / 2]->frequency)
	{
		MinHeap[pos] = MinHeap[pos / 2];
		pos /= 2;
	}
	MinHeap[pos] = n;
	HeapLength++;
}

Node DeleteMin_heap(void)
{
	Node temp=MinHeap[1];
	MinHeap[1] = MinHeap[HeapLength];
	HeapLength--;
	int parents = 1;
	int child = 2 * parents;
	while (child <= HeapLength)
	{
		if (child != HeapLength)
		{
			if (MinHeap[child]->frequency > MinHeap[child+1]->frequency)
				child++;
		}
		if (MinHeap[parents]->frequency > MinHeap[child]->frequency)
		{
			Node temp = MinHeap[parents];
			MinHeap[parents] = MinHeap[child];
			MinHeap[child] = temp;
		}
		else
			break;
		parents = child;
		child = 2 * parents;
	}
	return temp;
}
//最小堆操作

Root create_hfmtree(void)
{
	Node Root = NULL;
	scanf("%d", &N);
	create_heap(N + 1);
	while (1)
	{
		Node left = DeleteMin_heap();
		Node right = DeleteMin_heap();
		Node newnode = (Node)malloc(sizeof(struct HFMTreeNode));
		newnode->frequency = left->frequency + right->frequency;
		newnode->lchild = left;
		newnode->rchild = right;
		if (HeapLength != 0)
			insert_heap(HeapLength + 1, newnode);
		else
		{
			Root = newnode;
			break;
		}
	}
	return Root;
}

int count_wpl(Root t,int depth)
{
	if (t == NULL)
		return 0;
	else if (t->lchild == NULL && t->rchild == NULL)
		return t->frequency*depth;
	else
		return count_wpl(t->lchild, depth + 1) + count_wpl(t->rchild, depth + 1);
}
//哈夫曼树操作

bool is_prefix_true(Root t)
{
	int flag = 0;
	char word;
	char string[64];
	for (int i = 0; i < N; i++)
	{
		getchar();
		scanf("%c %s", &word, string);
		char *p = string;
		Node Last = t;
		while (*p != '\0')
		{
			if (*p == '0')
			{
				if (Last->lchild == NULL)
				{
					Node Nnode = (Node)malloc(sizeof(struct HFMTreeNode));
					Nnode->frequency = 0;
					Nnode->lchild = NULL;
					Nnode->rchild = NULL;
					Last->lchild = Nnode;
					Last = Nnode;
				}
				else
				{
					if (Last->lchild->frequency != 0)
						flag = 1;	
					Last = Last->lchild;
				}
			}
			else if(*p == '1')
			{
				if (Last->rchild == NULL)
				{
					Node Nnode = (Node)malloc(sizeof(struct HFMTreeNode));
					Nnode->frequency = 0;
					Nnode->lchild = NULL;
					Nnode->rchild = NULL;
					Last->rchild = Nnode;
					Last = Nnode;
				}
				else
				{
					if (Last->rchild->frequency != 0)
						flag = 1;
					Last = Last->rchild;
				}
			}
			p++;
		}
		if (Last->lchild != NULL || Last->rchild != NULL)
			flag = 1;
		else
			Last->frequency = fre[i];
	}
	if (flag == 1)
		return false;
	else
		return true;
}
//判断前缀是否正确

bool is_wpl_true(Root t1, Root t2)
{
	if (count_wpl(t1, 0) == count_wpl(t2, 0))
		return true;
	else
		return false;
}
//判断WPL是否一致

int main(void)
{
	Root t = create_hfmtree();
	scanf("%d", &M);
	for (int i = 0; i < M; i++)
	{
		Root check_t = (Node)malloc(sizeof(struct HFMTreeNode));
		check_t->frequency = 0;
		check_t->lchild = NULL;
		check_t->rchild = NULL;
		if (is_prefix_true(check_t))
		{
			if (is_wpl_true(t, check_t))
				printf("Yes\n");
			else
				printf("No\n");
		}
		else
			printf("No\n");
	}
}
posted @ 2019-08-19 11:12  smile_zyk  阅读(306)  评论(0编辑  收藏  举报