NYOJ 801-Haffman编码(丧心病狂的输入格式)

题目描述:

哈弗曼编码大家一定很熟悉吧(不熟悉也没关系,自己查去。。。)。现在给你一串字符以及它们所对应的权值,让你构造哈弗曼树,从而确定每个字符的哈弗曼编码。当然,这里有一些小规定:

1.规定哈弗曼树的左子树编码为0,右子树编码为1;

2.若两个字符权值相同,则ASCII码值小的字符为左孩子,大的为右孩子;

3.创建的新节点所代表的字符与它的左孩子的字符相同;

4.所有字符为ASCII码表上32-96之间的字符(即“ ”到“`”之间的字符)。

输入描述:

输入包含多组数据(不超过100组)
每组数据第一行一个整数n,表示字符个数。接下来n行,每行有一个字符ch和一个整数weight,表示字符ch所对应的权值,中间用空格隔开。
输入数据保证每组测试数据的字符不会重复。

输出描述:

对于每组测试数据,按照输入顺序输出相应的字符以及它们的哈弗曼编码结果,具体格式见样例。

样例输入:

复制

3
a 10
b 5
c 8
4
a 1
b 1
c 1
d 1

样例输出:

a:0
b:10
c:11
a:00
b:01
c:10
d:11

想法:

哈夫曼编码(哈夫曼树的一个应用):

它应用广泛,JPEG中就应用了哈夫曼编码。

哈夫曼树又称最优二叉树,是一种带权路径长度最短的二叉树。

有N个叶结点,从而构成一棵二叉树,结点的权值为Wi(i=1,2,...n),相应的叶结点的路径长度(根节点到叶节点有多远)为Li(i=1,2,...n),树的带权路径长度记为WPL= (W1*L1+W2*L2+W3*L3+...+Wn*Ln),这棵树使WPL最小

编码过程:

          虽然没有官方规定左子树权重小,右子树权重大,但一般都是左小右大。 

  1. 给定的n棵二叉树(相当于n个初始结点)的初始集合F= {T1,T2,T3,...,Ti,...,Tn},对应n个权值{W1,W2,W3,...,Wi,...,Wn},其中每棵二叉树Ti中只有一个权值为Wi的根结点(就相当于还是单独的一个节点还没组成树),它的左右子树均为空。(为方便实现算法,还要求F以权值Wi 升序排列,实际编程中可以不用
  2. 在F中选取两棵根结点权值最小的树作为新构造的二叉树的左右子树,新二叉树的根结点的权值为其所有结点的权值之和
  3. 从F中删除这两棵树,并把这棵新的二叉树同样以升序排列(这样做时间复杂度高,可以不排列只取出权值第二小)加入到集合F中。
  4. 重复2和3,直到集合F中只有一棵二叉树为止(或者有n-1个非叶子结点为止)。

这个麻烦就麻烦在输入上,c++对付输入中含有空格的还是挺麻烦的所以还是用c的scanf比较好

代码实现:

/*
	最坑的地方在于输入有空格,导致c++那套输入流不好用,用的是c语言的scanf
	开头处加入忽略安全警告的声明 _CRT_SECURE_NO_WARNINGS
	此代码没有使用优先级队列,只是普通huffman编码的一点改动
		n个结点的满二叉树(全部塞满)叶子结点数为(n+1)/2,所以n个叶子节点的总结点数为2*n-1
		所以给哈夫曼树开空间就可以依照这个数(完全二叉树不满足这个性质)
*/
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<string>
#include<cstdio>
using namespace std;

#define N 200

struct Node{
	char ch;
	string huffman_str;//存储这个字符在树中的Huffman编码
	int weight;
	int left_child;
	int right_child;
	int parent;
};

void selectNode(int label, Node huff[200], int &node1, int &node2)	//选出两个权值最小的结点node1,node2
{
	int i = 0, min = -1;
	for (i = 0; i < label; i++)	//首先结点需要是没有父节点的,作为min的初始大小
	{
		if (huff[i].parent == -1)
		{
			min = i;
			break;
		}
	}
	for (i = 0; i < label; i++)	//找到第一小的结点
	{
		if (huff[i].parent == -1)
		{
			if (huff[i].weight < huff[min].weight)
				min = i;								//权值相等时,ASCII码值小的字符优先
			else if (huff[i].weight == huff[min].weight && huff[i].ch < huff[min].ch)
				min = i;
		}
	}
	node1 = min;	//现在min和node1记录的都是第一小的结点

	for (i = 0; i < label; i++)	//第二个min的初始大小
	{
		if (huff[i].parent == -1 && i != node1)
		{
			min = i;
			break;
		}
	}
	for (i = 0; i < label; i++)	//找到第二小的结点
	{							//有可能第二小的跟第一小的权值一样大,所以是小于等于	
		if (huff[i].parent == -1 && i != node1)
		{
			if (huff[i].weight < huff[min].weight)
				min = i;								//权值相等时,ASCII码值小的字符优先
			else if (huff[i].weight == huff[min].weight &&  huff[i].ch < huff[min].ch)
				min = i;
		}
	}
	node2 = min;
}

void createHuffmanTree(int n, Node huff[N])
{
	int node1 = -1, node2 = -1;
/*	if (n == 1)	//单独处理只有一个结点的情况
	{
		huff[1].left_child = 0;
		huff[0].parent = 1;
		huff[1].weight = huff[0].weight;
	}*/
	for (int i = n; i < 2 * n - 1; i++)	//最后只剩一棵树的时候停止,有n-1个非叶子结点,以此为结束条件
	{
		selectNode(i, huff, node1, node2);
		huff[i].ch = huff[node1].ch;//按照题目要求新树的字符等于左孩子的字符
		huff[i].left_child = node1;	//左子树的权值小,右子树权值大
		huff[i].right_child = node2;
		huff[i].weight = huff[node1].weight + huff[node2].weight;
		huff[node1].parent = i;	//合并成为一棵树,同一个父结点
		huff[node2].parent = i;
	}
}

void searchHuffmanCode(int n, Node huff[N])	//寻找哈夫曼编码
{
	int p, temp;
	for (int i = 0; i < n; i++)	//下标小于n的才是叶子节点
	{
		temp = i;	//用来判断是左儿子还是右儿子
		p = huff[i].parent;
		while (p != -1)
		{
			if (huff[p].left_child == temp)	//由于是从叶子出发到根,所以要使用插入函数
				huff[i].huffman_str.insert(0, "0");
			else
				huff[i].huffman_str.insert(0, "1");
			temp = p;
			p = huff[p].parent;
		}
		cout << huff[i].ch << ":" << huff[i].huffman_str << endl;	//在这输出
	}
}

int main()
{
	int n;	//最大为100组
	while (scanf("%d",&n) != EOF)//因为输入中含有空格,cin无法自动处理,真坑
	{
		getchar();	//作用是吞掉回车
		if (n == 0)	//n为0则跳出此次循环
			continue;
		Node huff[N];	//先假设最大是200个结点(题上没给)
		for (int i = 0; i < 2 * n; i++)	//可能用到的下标都初始化一下
		{
			huff[i].huffman_str = "";
			huff[i].parent = -1;
			huff[i].left_child = -1;
			huff[i].right_child = -1;
			huff[i].weight = 0;
		}
		for (int i = 0; i < n; i++)
		{
			scanf("%c%d", &huff[i].ch, &huff[i].weight);
			getchar();	//吞掉回车
		}
		createHuffmanTree(n, huff);
		/*for (int i = 0; i < 2 * n - 1; i++)	//输出测试
			cout << huff[i].parent << huff[i].left_child << huff[i].right_child << endl;
			*/
		searchHuffmanCode(n, huff);
	}
	return 0;
}

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
孪生素数是指两个素数之间的差值为2的素数对。通过筛选法可以找出给定素数范围内的所有孪生素数的数。 在引用的代码中,使用了递归筛选法来解决孪生素数问题。该程序首先使用循环将素数的倍数标记为非素数,然后再遍历素数数,找出相邻素数之间差值为2的素数对,并统计总数。 具体实现过程如下: 1. 定义一个a[N,用来标记数字是否为素数,其中N为素数范围的上限。 2. 初始化数a,将0和1标记为非素数。 3. 输入要查询的孪生素数的个数n。 4. 循环n次,每次读入一个要查询的素数范围num。 5. 使用两层循环,外层循环从2遍历到num/2,内层循环从i的平方开始,将素数的倍数标记为非素数。 6. 再次循环遍历素数数,找出相邻素数之间差值为2的素数对,并统计总数。 7. 输出总数。 至此,我们可以使用这个筛选法的程序来解决孪生素数问题。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [python用递归筛选法求N以内的孪生质数(孪生素数)](https://blog.csdn.net/weixin_39734646/article/details/110990629)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT0_1"}}] [.reference_item style="max-width: 50%"] - *2* *3* [NYOJ-26 孪生素数问题](https://blog.csdn.net/memoryofyck/article/details/52059096)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT0_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值