恢复哈夫曼树
[题目链接](https://www.nowcoder.com/practice/9f97544aa62e40288a6267ad83262047)
思路
本题给定一棵哈夫曼树所有叶子节点的哈夫曼编码(0 表示左、1 表示右),以及叶子节点的权值集合(顺序随机),要求恢复出完整的哈夫曼树。
关键观察
哈夫曼树有三条排序约束:
- 权值越小的叶子越深——较小的权值应被分配到编码更长(层数更深)的位置。
- 同层节点权值从左到右递增——同一深度的叶子,左边的权值不大于右边的。
- 左子节点权值
右子节点权值——这一条在叶子正确分配后,内部节点由子节点权值之和确定,自动满足。
做法
分三步完成:
第一步:建树骨架。 根据每个叶子的哈夫曼编码,从根节点出发,遇 0 走左、遇 1 走右,逐步创建节点,最终到达叶子位置。所有编码处理完后,树的形状就确定了。
第二步:分配权值。 将叶子按 (深度降序, 同深度按编码字典序升序) 排列,将权值升序排列,一一对应赋值。这样最小的权值分配给最深且最靠左的叶子,满足上述约束。
第三步:填充内部节点。 后序遍历整棵树,每个内部节点的值等于其左右子节点值之和。
样例演示
输入 leaf = ["0","10","11"],value = [2,1,2]。
| 编码 | 深度 | 排序优先级 |
|---|---|---|
| "10" | 2 | 最高(最深) |
| "11" | 2 | 次高(同深度,字典序更大) |
| "0" | 1 | 最低(最浅) |
权值升序排列:[1, 2, 2]。
分配结果:"10" → 1,"11" → 2,"0" → 2。
填充内部节点后,右子节点值 = 1 + 2 = 3,根节点值 = 2 + 3 = 5。
输出:{5, 2, 3, #, #, 1, 2}。
复杂度
- 时间复杂度:
,其中
为叶子节点数,
为所有编码长度之和。排序
,建树
。
- 空间复杂度:
,用于存储树节点。
代码
class Solution {
public:
TreeNode* recoverHuffman(vector<string>& leaf, vector<int>& value) {
int n = leaf.size();
TreeNode* root = new TreeNode(0);
vector<TreeNode*> leafNodes(n);
// 根据编码建树骨架
for (int i = 0; i < n; i++) {
TreeNode* cur = root;
for (char c : leaf[i]) {
if (c == '0') {
if (!cur->left) cur->left = new TreeNode(0);
cur = cur->left;
} else {
if (!cur->right) cur->right = new TreeNode(0);
cur = cur->right;
}
}
leafNodes[i] = cur;
}
// 按深度降序、同深度按编码字典序升序排列
vector<int> indices(n);
for (int i = 0; i < n; i++) indices[i] = i;
sort(indices.begin(), indices.end(), [&](int a, int b) {
if (leaf[a].size() != leaf[b].size())
return leaf[a].size() > leaf[b].size();
return leaf[a] < leaf[b];
});
// 权值升序排列后一一分配
sort(value.begin(), value.end());
for (int i = 0; i < n; i++) {
leafNodes[indices[i]]->val = value[i];
}
// 后序遍历填充内部节点
fillValues(root);
return root;
}
private:
int fillValues(TreeNode* node) {
if (!node->left && !node->right) return node->val;
int sum = 0;
if (node->left) sum += fillValues(node->left);
if (node->right) sum += fillValues(node->right);
node->val = sum;
return sum;
}
};
import java.util.*;
public class Solution {
public TreeNode recoverHuffman(String[] leaf, int[] value) {
TreeNode root = new TreeNode(0);
int n = leaf.length;
TreeNode[] leafNodes = new TreeNode[n];
// 根据编码建树骨架
for (int i = 0; i < n; i++) {
String code = leaf[i];
TreeNode cur = root;
for (int j = 0; j < code.length(); j++) {
if (code.charAt(j) == '0') {
if (cur.left == null) cur.left = new TreeNode(0);
cur = cur.left;
} else {
if (cur.right == null) cur.right = new TreeNode(0);
cur = cur.right;
}
}
leafNodes[i] = cur;
}
// 按深度降序、同深度按编码字典序升序排列
Integer[] indices = new Integer[n];
for (int i = 0; i < n; i++) indices[i] = i;
Arrays.sort(indices, (a, b) -> {
int da = leaf[a].length();
int db = leaf[b].length();
if (da != db) return db - da;
return leaf[a].compareTo(leaf[b]);
});
// 权值升序排列后一一分配
int[] sortedValues = Arrays.copyOf(value, n);
Arrays.sort(sortedValues);
for (int i = 0; i < n; i++) {
leafNodes[indices[i]].val = sortedValues[i];
}
// 后序遍历填充内部节点
fillValues(root);
return root;
}
private int fillValues(TreeNode node) {
if (node.left == null && node.right == null) return node.val;
int sum = 0;
if (node.left != null) sum += fillValues(node.left);
if (node.right != null) sum += fillValues(node.right);
node.val = sum;
return sum;
}
}

京公网安备 11010502036488号