Task A2 哈夫曼树的应用

【题目描述】 PTA(数据结构与算法题目集 7-29)
农夫要修理牧场的一段栅栏,他测量了栅栏,发现需要 N 块木头,每块木头长度为整数 Li个长度单位,于是他购买了一条很长的、能锯成 N 块的木头,即该木头的长度是 Li 的总和。但是农夫自己没有锯子,请人锯木的酬金跟这段木头的长度成正比。为简单起见,不妨就设酬金等于所锯木头的长度。例如,要将长度为 20 的木头锯成长度为 8、 7 和 5 的三段,第一次锯木头花费20,将木头锯成 12 和 8;第二次锯木头花费 12,将长度为 12 的木头锯成 7 和 5,总花费为 32。如果第一次将木头锯成 15 和 5,则第二次锯木头花费 15,总花费为 35(大于 32)。请编写程序帮助农夫计算将木头锯成 N 块的最少花费。

输入格式
第一行为正整数N≤104,表示将木头锯成N块,第二行给出N个正整数,表示每段木块的长度

输出格式
一个正整数,即将木头锯成N块的最小花费

题目分析
哈夫曼编码通常用于数据压缩,也可以用来解决类似的问题,尤其是涉及合并成本最小化时。对于这个问题我们可以使用哈夫曼树的思想来解决。
算法过程:

  1. 初始化:将所有的木头放入一个数组中
  2. 合并:每次从堆中取出两个最小的木头,创建一个新的结点,其值为两个木头长度之和,并将结点放回数组中。
  3. 重复:重复上述步骤,直到只剩下一个结点。
  4. 计算:在每次合并时累加合并成本,得到总最小花费。

哈夫曼算法中取两个最小的数算法思想是:从数组的起始位置开始,首先找到两个无父节点的结点(没有构成树),然后和后续无父节点的节点依次比较。有两种情况需要考虑:

  1. 如果该节点比两个节点都小,保留这个节点,删除原来较大的节点;
  2. 如果该节点介于两个节点大小之间,则可以替换掉那个最大的结点。
    基于c++的算法如下:
void Select(int n, int &i1, int &i2)
{
    int i = 0;
    for( ; i < n; i++)
        if(huffTree[i].parent == -1) {i1 = i; break;}
    for(i = i + 1; i < n; i++)
        if(huffTree[i].parent == -1) {i2 = i; break;}
    //首先找到没有两个父节点的两个节点i1 和 i2,在这里的parent等于-1表示没有构成树
    
    if(huffTree[i1].weight > huffTree[i2].weight)
    {
        int temp = i1;
        i1 = i2;
        i2 = temp //交换代码
    }
    //在这里规定i1和i2的大小,i1为小,i2为大

    //循环遍历数组
    for(i = i + 1; i < n; i++)
    {
        if(huffTree[i].parent == -1)  //找到后续没有父节点的结点
        {
            if(huffTree[i].weight < huffTree[i1].weight)//如果比i1和i2都小
            {
                i2 = i1; i1 = i;  
            }
            else if(huffTree[i].weight < huffTree[i2].weight) //如果介于两者之间
            {
                i2 = i;
            }
        } 
    }
}

这样我们就确定了两个权值最小的节点。

完整代码如下

#include<iostream>
using namespace std;
const int MAXSIZE = 104;
//定义树的结点结构,包括节点权值,左右孩子指针,父节点指针
struct Elemtype {
	int weight;
	int parent, lchild, rchild;
}; 

//构造哈夫曼树
class HuffmanTree {
public:
	HuffmanTree(int w[], int n); //构造哈夫曼树,参数w为权值数组,n为权值数组的个数
	int GetSumWeight();//返回哈夫曼树的权值和
private:
	Elemtype* hufftree;//存储哈夫曼树的结点
	int num;//节点个数
	int Sumweght = 0;
	void Select(int n, int& i1, int& i2); //选择权值最小的两个节点
};

HuffmanTree::HuffmanTree(int w[], int n)
{
	int i, k, i1, i2;
	hufftree = new Elemtype[2 * n + 1];//开辟存储结点的空间,个数为2n+1
	num = n;
	for (i = 0; i < 2 * num + 1; i++)
	{
		hufftree[i].parent = -1;
		hufftree[i].lchild = -1;
        hufftree[i].rchild = -1;
	}//初始化结点,没有孩子,没有父节点
	for(i = 0; i < num; i++)
		hufftree[i].weight = w[i];
	//存储叶子结点的权值
	for (k = num; k < 2 * num - 1; k++)
	{
		Select(k, i1, i2); //选择权值最小的两个节点
		hufftree[k].weight = hufftree[i1].weight + hufftree[i2].weight;
		Sumweght += hufftree[k].weight;
		hufftree[i1].parent = k;
		hufftree[i2].parent = k;
		hufftree[k].lchild = i1;
		hufftree[k].rchild = i2;
	}
}

void HuffmanTree::Select(int n, int& i1, int& i2)
{
	int i = 0, temp;
	for (; i < n; i++)
		if (hufftree[i].parent == -1) { i1 = i; break; }
	for (i = i + 1; i < n; i++)
		if (hufftree[i].parent == -1) { i2 = i; break; }
	if (hufftree[i1].weight > hufftree[i2].weight)
	{
        temp = i1;
        i1 = i2;
        i2 = temp;
	}
    for (i = i + 1; i < n; i++)
		if (hufftree[i].parent == -1)
		{
			if (hufftree[i].weight < hufftree[i1].weight)
			{
				i2 = i1, i1 = i;
			}
			else if (hufftree[i].weight < hufftree[i2].weight)
			{
				i2 = i;
			}
		}
}

int HuffmanTree::GetSumWeight()
{
	return Sumweght;
}

int main() {
	cout << "请输入你要将木头切割的块数正整数N:" << endl;
	int N;
	cin >> N;
	cout << "请输入每段木块的长度:"<<endl;
    int w[MAXSIZE];
	for (int i = 0; i < N; i++)
	{
		cin >> w[i];
	}
	HuffmanTree ht(w, N);
	cout << "哈夫曼树的权值和为:" << ht.GetSumWeight() << endl;
	return 0;
}



posted @   YunC  阅读(26)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· 从HTTP原因短语缺失研究HTTP/2和HTTP/3的设计差异
· 三行代码完成国际化适配,妙~啊~
点击右上角即可分享
微信分享提示