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块的最小花费
题目分析
哈夫曼编码通常用于数据压缩,也可以用来解决类似的问题,尤其是涉及合并成本最小化时。对于这个问题我们可以使用哈夫曼树的思想来解决。
算法过程:
- 初始化:将所有的木头放入一个数组中
- 合并:每次从堆中取出两个最小的木头,创建一个新的结点,其值为两个木头长度之和,并将结点放回数组中。
- 重复:重复上述步骤,直到只剩下一个结点。
- 计算:在每次合并时累加合并成本,得到总最小花费。
哈夫曼算法中取两个最小的数算法思想是:从数组的起始位置开始,首先找到两个无父节点的结点(没有构成树),然后和后续无父节点的节点依次比较。有两种情况需要考虑:
- 如果该节点比两个节点都小,保留这个节点,删除原来较大的节点;
- 如果该节点介于两个节点大小之间,则可以替换掉那个最大的结点。
基于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;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· 从HTTP原因短语缺失研究HTTP/2和HTTP/3的设计差异
· 三行代码完成国际化适配,妙~啊~