【问题描述】
已知输入n个正整数,正整数之间用空格键分开,请以这n个正整数为权值建立一棵有n个叶子节点的哈夫曼树,求这棵哈夫曼树的带权路径长度。
【输入形式】
首先输入正整数的个数,然后接下来为接下来的正整数,正整数个数不超过10个,每个数字之间用空格隔开。
【输出形式】
输出相应的权值
【样例输入】
5 4 5 6 7 8
【样例输出】
69
#include <stdio.h>
#include <stdlib.h>
// 定义哈夫曼树节点结构体
typedef struct Node {
int weight;
struct Node* left;
struct Node* right;
} Node;
// 创建新节点
Node* createNode(int weight) {
Node* newNode = (Node*)malloc(sizeof(Node));
newNode->weight = weight;
newNode->left = NULL;
newNode->right = NULL;
return newNode;
}
// 构建哈夫曼树
Node* buildHuffmanTree(int weights[], int n) {
Node** nodes = (Node**)malloc(n * sizeof(Node*));
for (int i = 0; i < n; i++) {
nodes[i] = createNode(weights[i]);
}
while (n > 1) {
// 找出最小权值的两个节点
int minIndex1 = 0, minIndex2 = 1;
if (nodes[minIndex1]->weight > nodes[minIndex2]->weight) {
int temp = minIndex1;
minIndex1 = minIndex2;
minIndex2 = temp;
}
for (int i = 2; i < n; i++) {
if (nodes[i]->weight < nodes[minIndex1]->weight) {
minIndex2 = minIndex1;
minIndex1 = i;
} else if (nodes[i]->weight < nodes[minIndex2]->weight) {
minIndex2 = i;
}
}
// 创建新节点,权值为两个最小节点的和
Node* newNode = createNode(nodes[minIndex1]->weight + nodes[minIndex2]->weight);
newNode->left = nodes[minIndex1];
newNode->right = nodes[minIndex2];
// 删除已合并的节点
nodes[minIndex1] = newNode;
for (int i = minIndex2; i < n - 1; i++) {
nodes[i] = nodes[i + 1];
}
n--;
}
Node* root = nodes[0];
free(nodes);
return root;
}
// 计算哈夫曼树的带权路径长度
int calculateWPL(Node* root, int depth) {
if (root == NULL) {
return 0;
}
if (root->left == NULL && root->right == NULL) {
return root->weight * depth;
}
int leftWPL = calculateWPL(root->left, depth + 1);
int rightWPL = calculateWPL(root->right, depth + 1);
return leftWPL + rightWPL;
}
int main() {
int n;
printf("请输入正整数的个数:");
scanf("%d", &n);
int* weights = (int*)malloc(n * sizeof(int));
printf("请输入正整数:");
for (int i = 0; i < n; i++) {
scanf("%d", &weights[i]);
}
Node* root = buildHuffmanTree(weights, n);
int wpl = calculateWPL(root, 0);
printf("%d\n", wpl);
free(weights);
return 0;
}
在DEVC++编译、运行成功。
if (nodes[minIndex1]->weight > nodes[minIndex2]->weight) {
int temp = minIndex1;
minIndex1 = minIndex2;
minIndex2 = temp;
}//为什么要单独列出,而不放在for (int i = 2; i < n; i++)
为什么要单独列出,而不放在for (int i = 2; i < n; i++)?
这段代码是用来确保minIndex1对应的节点的权值小于等于minIndex2对应的节点的权值。如果放在for循环中,会导致每次迭代都要判断一次,而实际上在找出最小权值的两个节点时只需要进行一次比较和交换即可。因此,将这部分逻辑单独列出,可以提高程序执行效率,并且更清晰地表达了找出最小权值的两个节点的逻辑。
算法原理
构建哈夫曼树:Node* buildHuffmanTree(int weights[], int n) 其核心算法原理是贪心算法。
在贪心算法中,每一步都选择当前最优解,通过不断地选择最小权值的节点并合并,最终构建出带有最优前缀编码的哈夫曼树