实验二:第二题、哈夫曼树锯木头

目录

目录

实验目的及内容

解题思路

哈夫曼树的构造:

实验代码及注释

输入输出说明及结果截图

心得体会

实验目的及内容

农夫将木头锯成N块,要求总花费最少。请编写程序,输出锯木头的具体步骤以及总花费值。(要求构建哈夫曼树)

 第一行输入正整数N(<=104),表示要将木头锯成N块。 第二行给出N个正整数(<=50),表示每段木块的长度

解题思路

根据题目的描述,可以发现这样的规律:要想省钱,越小的长度,越应该放到后面锯。

进一步抽象一下的话,就得到了解决方案:根据所有的目标木块长度来构建一棵哈夫曼树,把每段木块长度存在叶子中,根据法则生成根结点。最后,在三叉静态链表中顺序访问每一个根结点,即可输出锯木头的具体步骤总花费即为所有根结点权值之和。

哈夫曼树的构造:

由于哈夫曼树中没有度为1 的结点,则一棵有n个叶子结点的哈夫曼树共有 2n-1个结点,可以存储在一个大小为2n-1的数组(静态三叉链表中。
为了实现方便,数组的0号单元不使用,从1号单元开始使用,所以数组的大小为2n,。将叶子结点集中存储在前面部分 1~n个位置。构造的时候,按顺序从数组的(n+1)号位置开始生成相应的根结点
在这里插入图片描述

实验代码及注释

本段代码使用常规方式构建哈夫曼树,其时间复杂度为O(n^2): Crt函数的for循环里调用了select函数,而select函数时间复杂度为O(n)

#include <stdio.h>
#include <iostream>
using namespace std;
#define N 104		/* 叶子结点的最大值*/
#define M 2 * N - 1 /* 所有结点的最大值*/
/*用静态三叉链表定义哈夫曼树的节点*/
typedef struct
{
	int weight;				  /* 结点的权*/
	int parent;				  /* 双亲的下*/
	int LChild;				  /* 左孩子结点的下标*/
	int RChild;				  /* 右孩子结点的下标*/
} HTNode, HuffmanTree[M + 1]; /* HuffmanTree 是一个结构体数组类型,0 号单元不用*/

// 选择两个双亲域为0且权值最小的结点,并将其在HT中的序号s1,s2通过引用的方式传递给实参
void select(HuffmanTree ht, int n, int &x1, int &x2)
{
	int min; // 暂存序号
	// 先找第一个序号s1
	for (int i = 1; i < n; ++i) // 首先要找到第一个parent==0的节点,下标暂存min
		{
			if (ht[i].parent == 0)
				{
					min = i;
					break;
				}
		}
	for (int i = min + 1; i < n; ++i)
		{
			if (ht[i].parent == 0)
				{
					if (ht[i].weight < ht[min].weight)
						{
							min = i;
						}
				}
		}
	x1 = min;

	// 接着找第二个s2
	for (int i = 1; i <= n; ++i) // 首先要找到第一个parent==0的节点,下标暂存min
		{
			if (ht[i].parent == 0&&i!=x1 )
				{
					min = i;
					break;
				}
		}
	for (int i =  1; i <= n; ++i)
		{
			if (ht[i].parent == 0&&i!=x1)
				{
					if (ht[i].weight < ht[min].weight)
						{
							min = i;
						}
				}
		}
	x2 = min;


}

void CrtHuffmanTree(HuffmanTree ht, int w[], int n)
{
	/*构造哈夫曼树,ht w[ ]存放着 n 个权值*/
	for (int i = 1; i <= n; i++)
		ht[i] = {w[i], 0, 0, 0}; /* 1 ~ n 号单元存放叶子结点,初始*/
	int m = 2 * n ;
	for (int i = n + 1; i <= m; i++)
		ht[i] = {0, 0, 0, 0}; /* n+1 ~ m 号单元存放非叶结点,初始*/

	/*—————————初始化完毕!下面开始构造造根结点————————*/
	for (int i = n + 1; i <= m; i++)
		{
			int s1, s2;
			select(ht, i - 1, s1, s2); /* 在ht[1] ~ ht[i-1] 的范围内选择两个 parent 为 0、且weight 最小的结点,其序号分别赋值给 s1、s2  */
			ht[i].weight = ht[s1].weight + ht[s2].weight;
			ht[s1].parent = i;
			ht[s2].parent = i;
			ht[i].LChild = s1;
			ht[i].RChild = s2;
		} /*哈夫曼树建立完毕*/
}

// 输出,从每个根结点开始输出,其左右子树便是这次锯出的结果
void Print(HuffmanTree ht, int n, int m)
{
	int ALL =0;
	cout<<"锯木头的步骤如下:"<<endl;
	for (int i = m, j=1; i >= n + 1; j++,i--)
		{


			int l = ht[i].LChild;
			int r = ht[i].RChild;
			ALL+=ht[i].weight;
			cout<<"第"<<j<<"次:"<<endl;

			cout << ht[i].weight << "->" << ht[l].weight << "," << ht[r].weight << endl;
			cout<<endl;

		}
	cout<<"最小总花费为:"<<ALL<<endl;
}

int main()
{
	HuffmanTree ht;
	int n;
	cout << "请输入,要把木头锯成多少块:" << endl;
	cin>>n;
	int m = 2 * n - 1;
	int weight[n];
	cout<<"请输入每段木块的长度:"<<endl;
	for (int i = 1; i <= n; ++i)
		{

			cin >> weight[i];
		}
	CrtHuffmanTree(ht, weight, n);

	Print(ht, n, m);

	return 0;
}

****************************

输入输出说明及结果截图

输入:

第一行输入正整数N<=104),表示要将木头锯成N块。

第二行给出N个正整数(<=50),表示每 段木块的长度

输出:

锯木头的具体步骤以及总花费值

心得体会

通过本次实验,我加深了对哈夫曼树的理解,巩固了课堂学习知识,查漏补缺!

  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值