容器镜像Top-K大小统计(C++/Py/Java/Js/Go)题解
华为笔试真题 6月24号 非AI方向第三题 300分题型
题目内容
在容器镜像管理系统中, 容器镜像通常采用堆叠方式管理和挂载, 为了减少镜像管理系统中重复的镜像层数量, 假定容器镜像层采用二叉树管理。镜像层二叉树节点描述镜像层大小, 节点的镜像完整大小为镜像层大小及其所有父节点镜像层大小之和。由于业务需要, 现在需要对系统中所有的客户镜像大小统计分析, 从小到大输出最大的 K K K 个镜像大小; 输入为容器镜像二叉树前序遍历数组和中序遍历数组, 输出为最大的 K K K 个镜像大小, 并按从小到大排序输出。
说明: 当镜像完整大小小于等于 0 0 0 时, 则表示该镜像节点异常, 异常镜像节点需要剪枝; 例如: 节点 A A A 有子节点 B B B 和子节点 C C C, 如果节点 A A A 完整镜像大小为 0 0 0, 则节点 A A A 需要剪枝, 即节点 A A A、节点 B B B、节点 C C C均需要从镜像二叉树中删除。
输入描述
第一行: 容器镜像树前序遍历结果。
第二行: 容器镜像树中序遍历结果。
第三行: 需要统计的最大容器镜像个数, 取值范围为 1 1 1, 1000 1000 1000。
说明:
- 容器镜像树前序遍历、中序遍历结果中的数字表示当前镜像层大小, 取值范围为 − 10000 -10000 −10000, 10000 10000 10000。
- 镜像层大小为负数时表示该层基于父镜像裁剪文件, 镜像层为正数时表示该层基于父镜像新增文件, 0 0 0 则表示镜像层未做任何更改或者裁剪文件大小与新增文件大小相等;
- 节点规模数量 ≤ 1000 \le 1000 ≤1000。
- 为了能够通过前序遍历和中序遍历还原唯一的镜像二叉树, 输入的各节点镜像层大小都不相同。
输出描述
从小到大输出最大的镜像大小。
说明:
- 如果二叉树节点总数小于需要统计 T O P TOP TOP 数量, 则从小到大输出所有镜像大小。
- 如果二叉树剪枝后为空, 则输出 n u l l null null。
样例1
输入
8 2 -3 9 5
2 8 9 -3 5
3
输出
10 10 14
说明

根据 8 2 − 3 9 5 8\ 2\ -3\ 9\ 5 8 2 −3 9 5 前序遍历, 2 8 9 − 3 5 2\ 8\ 9\ -3\ 5 2 8 9 −3 5 中序遍历, 得到左图所示二叉树, 每个节点数字为对应镜像层大小; 每个节点镜像大小为自身节点镜像层大小加上所有父节点镜像层大小, 如右图所示为计算之后的各节点镜像实际大小。 一共有 5 5 5 个镜像, 镜像大小分别是 8 8 8, 10 10 10, 5 5 5, 14 14 14, 10 10 10, 所以 T O P 3 TOP3 TOP3 从小到大排序为 10 10 14 10\ 10\ 14 10 10 14。
样例2
输入
1 2 3 -5 5 6
2 1 3 5 -5 6
1
输出
4
说明

左图为输入的二叉树, 由于 − 5 -5 −5 节点计算出镜像完整大小为 − 1 -1 −1, 小于等于 0 0 0, 因此属于非法节点, 需要剪枝处理, 剪枝后得到最右边的二叉树, 一共三个镜像, T O P 1 TOP1 TOP1镜像大小为 4 4 4。
题解
思路
`二叉树
-
利用前序遍历和后序遍历结果还原二叉树,前序遍历的顺序为
父节点->左子->右子,中序遍历顺序为左子->父节点->右子,简单说说逻辑- 根据前序遍历规律,可以得出
preOrder[PL]为父节点的值。 - 在中序遍历结果找到父节点的位置为
j,那么可以得出左子树长度为j - 1 - IL + 1 = j - IL - 从而可以得出左/右子树前序遍历对应索引为
preOrderLeftStart = PL + 1, preOrderLeftEnd = preOrderLeftStart + len - 1preOrderRightStart = preOrderLeftEnd + 1, preOrderRightEnd = PR
- 左/右子树中序遍历对应索引为
inOrderLeftStart = IL, inOrderLeftEnd = j - 1inOrderRightStart = j + 1, inOrderRightEnd = IR
- 按照1、2、3规律便可还原二叉树
- 根据前序遍历规律,可以得出
-
利用还原的二叉树,递归获取镜像层大小大于0的节点加入数组中,遇到镜像层大小小于等于0时进行剪枝。
-
对通过2得到的数组进行升序排序,输出后k个节点值即可。
这道题和6月12号的考试题目基本差不多,华为非AI方向笔试真题 - 容器镜像平均大小统计
C++
cpp
#include<bits/stdc++.h>
using namespace std;
struct Node{
Node* left;
Node* right;
int value;
Node(){}
Node(int value):value(value), left(nullptr), right(nullptr){}
};
// 通用 切割函数 函数 将字符串str根据delimiter进行切割
vector<int> split(const string& str, const string& delimiter) {
vector<int> result;
size_t start = 0;
size_t end = str.find(delimiter);
while (end != string::npos) {
result.push_back(stoi(str.substr(start, end - start)));
start = end + delimiter.length();
end = str.find(delimiter, start);
}
// 添加最后一个部分
result.push_back(stoi(str.substr(start)));
return result;
}
// 通过前序和中序还原二叉树
Node* generateNode(vector<int>& preorder, vector<int>& inOrder, int PL, int PR, int IL, int IR) {
if (PL > PR) {
return nullptr;
}
Node* root = new Node(preorder[PL]);
// 确定左子树的长度
int j = IL;
for (; j <= IR; j++) {
if (preorder[PL] == inOrder[j]) {
break;
}
}
// 指定节点找不到
if (j > IR) {
return nullptr;
}
// 确定左右子树中序遍历范围
int inOrderLeftStart = IL;
int inOrderLeftEnd = j - 1;
int inOrderRightStart = j + 1;
int inOrderRightEnd = IR;
int len = j - 1 - IL + 1;
// 确定左右子树前序遍历结果
int preOrderLeftStart = PL + 1;
int preOrderLeftEnd = preOrderLeftStart + len - 1;
int preOrderRightStart = preOrderLeftEnd + 1;
int preOrderRightEnd = PR;
if (preOrderLeftStart <= preOrderLeftEnd) {
root->left = generateNode(preorder, inOrder, preOrderLeftStart, preOrderLeftEnd, inOrderLeftStart, inOrderLeftEnd);
}
if (preOrderRightStart <= preOrderRightEnd) {
root->right = generateNode(preorder, inOrder, preOrderRightStart, preOrderRightEnd, inOrderRightStart, inOrderRightEnd);
}
return root;
}
// 加入值为正的节点值,遇到负数剪枝
void dfs(Node* root, int last, vector<int>& node) {
if (root == nullptr) {
return;
}
int value = root->value + last;
if (value <= 0) {
return;
}
node.push_back(value);
if (root -> left != nullptr) {
dfs(root-> left , value, node);
}
if (root->right != nullptr) {
dfs(root->right, value, node);
}
}
int main() {
string input1, input2;
getline(cin, input1);
getline(cin, input2);
int k;
cin >> k;
vector<int> preOrder = split(input1, " ");
vector<int> inOrder = split(input2, " ");
int n = preOrder.size();
Node* root = generateNode(preOrder, inOrder, 0, n -1, 0, n - 1);
// 记录有效节点值
vector<int> node;
dfs(root, 0, node);
if (node.empty()) {
cout << "null";
return 0;
}
sort(node.begin(), node.end());
int m = node.size();
int begin = max(m-k, 0);
for (int i = begin; i < m; i++) {
if (i > begin) {
cout << " ";
}
cout << node[i];
}
return 0;
}
java
java
import java.io.*;
import java.util.*;
public class Main {
static class Node {
Node left;
Node right;
int value;
Node() {}
Node(int value) {
this.value = value;
}
}
// 通过前序和中序还原二叉树
static Node generateNode(int[] preorder, int[] inOrder, int PL, int PR, int IL, int IR) {
if (PL > PR) {
return null;
}
Node root = new Node(preorder[PL]);
// 确定左子树的长度
int j = IL;
for (; j <= IR; j++) {
if (preorder[PL] == inOrder[j]) {
break;
}
}
// 指定节点找不到
if (j > IR) {
return null;
}
// 确定左右子树中序遍历范围
int inOrderLeftStart = IL;
int inOrderLeftEnd = j - 1;
int inOrderRightStart = j + 1;
int inOrderRightEnd = IR;
int len = j - IL;
// 确定左右子树前序遍历结果
int preOrderLeftStart = PL + 1;
int preOrderLeftEnd = preOrderLeftStart + len - 1;
int preOrderRightStart = preOrderLeftEnd + 1;
int preOrderRightEnd = PR;
if (preOrderLeftStart <= preOrderLeftEnd) {
root.left = generateNode(preorder, inOrder, preOrderLeftStart, preOrderLeftEnd,
inOrderLeftStart, inOrderLeftEnd);
}
if (preOrderRightStart <= preOrderRightEnd) {
root.right = generateNode(preorder, inOrder, preOrderRightStart, preOrderRightEnd,
inOrderRightStart, inOrderRightEnd);
}
return root;
}
// 加入值为正的节点值,遇到负数剪枝
static void dfs(Node root, int last, List<Integer> node) {
if (root == null) {
return;
}
int value = root.value + last;
if (value <= 0) {
return;
}
node.add(value);
if (root.left != null) {
dfs(root.left, value, node);
}
if (root.right != null) {
dfs(root.right, value, node);
}
}
public static void main(String[] args) throws Exception {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
String input1 = br.readLine();
String input2 = br.readLine();
int k = Integer.parseInt(br.readLine());
String[] s1 = input1.split(" ");
String[] s2 = input2.split(" ");
int n = s1.length;
int[] preorder = new int[n];
int[] inOrder = new int[n];
for (int i = 0; i < n; i++) {
preorder[i] = Integer.parseInt(s1[i]);
inOrder[i] = Integer.parseInt(s2[i]);
}
Node root = generateNode(preorder, inOrder, 0, n - 1, 0, n - 1);
// 记录有效节点值
List<Integer> node = new ArrayList<>();
dfs(root, 0, node);
if (node.isEmpty()) {
System.out.print("null");
return;
}
Collections.sort(node);
int m = node.size();
int begin = Math.max(m - k, 0);
for (int i = begin; i < m; i++) {
if (i > begin) {
System.out.print(" ");
}
System.out.print(node.get(i));
}
}
}
python
python
import sys
sys.setrecursionlimit(1000000)
class Node:
def __init__(self, value):
self.value = value
self.left = None
self.right = None
# 通过前序和中序还原二叉树
def generate_node(preorder, inorder, pl, pr, il, ir):
if pl > pr:
return None
root = Node(preorder[pl])
# 确定左子树的长度
j = il
while j <= ir:
if preorder[pl] == inorder[j]:
break
j += 1
# 指定节点找不到
if j > ir:
return None
# 确定左右子树中序遍历范围
inorder_left_start = il
inorder_left_end = j - 1
inorder_right_start = j + 1
inorder_right_end = ir
length = j - il
# 确定左右子树前序遍历结果
preorder_left_start = pl + 1
preorder_left_end = preorder_left_start + length - 1
preorder_right_start = preorder_left_end + 1
preorder_right_end = pr
if preorder_left_start <= preorder_left_end:
root.left = generate_node(
preorder,
inorder,
preorder_left_start,
preorder_left_end,
inorder_left_start,
inorder_left_end,
)
if preorder_right_start <= preorder_right_end:
root.right = generate_node(
preorder,
inorder,
preorder_right_start,
preorder_right_end,
inorder_right_start,
inorder_right_end,
)
return root
# 加入值为正的节点值,遇到负数剪枝
def dfs(root, last, node):
if root is None:
return
value = root.value + last
if value <= 0:
return
node.append(value)
if root.left:
dfs(root.left, value, node)
if root.right:
dfs(root.right, value, node)
input1 = input().split()
input2 = input().split()
k = int(input())
preorder = list(map(int, input1))
inorder = list(map(int, input2))
n = len(preorder)
root = generate_node(preorder, inorder, 0, n - 1, 0, n - 1)
# 记录有效节点值
node = []
dfs(root, 0, node)
if not node:
print("null")
sys.exit()
node.sort()
m = len(node)
begin = max(m - k, 0)
print(*node[begin:])
javascript
js
const readline = require("readline");
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
});
const input = [];
rl.on("line", line => input.push(line));
rl.on("close", () => {
class Node {
constructor(value) {
this.value = value;
this.left = null;
this.right = null;
}
}
// 通过前序和中序还原二叉树
function generateNode(preorder, inorder, PL, PR, IL, IR) {
if (PL > PR) {
return null;
}
const root = new Node(preorder[PL]);
// 确定左子树的长度
let j = IL;
for (; j <= IR; j++) {
if (preorder[PL] === inorder[j]) {
break;
}
}
// 指定节点找不到
if (j > IR) {
return null;
}
// 确定左右子树中序遍历范围
const inOrderLeftStart = IL;
const inOrderLeftEnd = j - 1;
const inOrderRightStart = j + 1;
const inOrderRightEnd = IR;
const len = j - IL;
// 确定左右子树前序遍历结果
const preOrderLeftStart = PL + 1;
const preOrderLeftEnd = preOrderLeftStart + len - 1;
const preOrderRightStart = preOrderLeftEnd + 1;
const preOrderRightEnd = PR;
if (preOrderLeftStart <= preOrderLeftEnd) {
root.left = generateNode(preorder, inorder,
preOrderLeftStart, preOrderLeftEnd,
inOrderLeftStart, inOrderLeftEnd);
}
if (preOrderRightStart <= preOrderRightEnd) {
root.right = generateNode(preorder, inorder,
preOrderRightStart, preOrderRightEnd,
inOrderRightStart, inOrderRightEnd);
}
return root;
}
// 加入值为正的节点值,遇到负数剪枝
function dfs(root, last, node) {
if (root == null) {
return;
}
const value = root.value + last;
if (value <= 0) {
return;
}
node.push(value);
if (root.left) {
dfs(root.left, value, node);
}
if (root.right) {
dfs(root.right, value, node);
}
}
const preorder = input[0].split(" ").map(Number);
const inorder = input[1].split(" ").map(Number);
const k = Number(input[2]);
const root = generateNode(preorder, inorder, 0, preorder.length - 1, 0, preorder.length - 1);
// 记录有效节点值
const node = [];
dfs(root, 0, node);
if (node.length === 0) {
console.log("null");
return;
}
node.sort((a, b) => a - b);
const begin = Math.max(node.length - k, 0);
console.log(node.slice(begin).join(" "));
});
Go
go
package main
import (
"bufio"
"fmt"
"os"
"sort"
"strconv"
"strings"
)
type Node struct {
left *Node
right *Node
value int
}
// 通过前序和中序还原二叉树
func generateNode(preorder, inorder []int, PL, PR, IL, IR int) *Node {
if PL > PR {
return nil
}
root := &Node{value: preorder[PL]}
// 确定左子树的长度
j := IL
for ; j <= IR; j++ {
if preorder[PL] == inorder[j] {
break
}
}
// 指定节点找不到
if j > IR {
return nil
}
// 确定左右子树中序遍历范围
inOrderLeftStart := IL
inOrderLeftEnd := j - 1
inOrderRightStart := j + 1
inOrderRightEnd := IR
length := j - IL
// 确定左右子树前序遍历结果
preOrderLeftStart := PL + 1
preOrderLeftEnd := preOrderLeftStart + length - 1
preOrderRightStart := preOrderLeftEnd + 1
preOrderRightEnd := PR
if preOrderLeftStart <= preOrderLeftEnd {
root.left = generateNode(preorder, inorder,
preOrderLeftStart, preOrderLeftEnd,
inOrderLeftStart, inOrderLeftEnd)
}
if preOrderRightStart <= preOrderRightEnd {
root.right = generateNode(preorder, inorder,
preOrderRightStart, preOrderRightEnd,
inOrderRightStart, inOrderRightEnd)
}
return root
}
// 加入值为正的节点值,遇到负数剪枝
func dfs(root *Node, last int, node *[]int) {
if root == nil {
return
}
value := root.value + last
if value <= 0 {
return
}
*node = append(*node, value)
if root.left != nil {
dfs(root.left, value, node)
}
if root.right != nil {
dfs(root.right, value, node)
}
}
func main() {
in := bufio.NewReader(os.Stdin)
input1, _ := in.ReadString('\n')
input2, _ := in.ReadString('\n')
input1 = strings.TrimSpace(input1)
input2 = strings.TrimSpace(input2)
var k int
fmt.Fscan(in, &k)
s1 := strings.Split(input1, " ")
s2 := strings.Split(input2, " ")
preorder := make([]int, len(s1))
inorder := make([]int, len(s2))
for i := range s1 {
preorder[i], _ = strconv.Atoi(s1[i])
inorder[i], _ = strconv.Atoi(s2[i])
}
root := generateNode(preorder, inorder, 0, len(preorder)-1, 0, len(preorder)-1)
// 记录有效节点值
node := []int{}
dfs(root, 0, &node)
if len(node) == 0 {
fmt.Print("null")
return
}
sort.Ints(node)
begin := len(node) - k
if begin < 0 {
begin = 0
}
for i := begin; i < len(node); i++ {
if i > begin {
fmt.Print(" ")
}
fmt.Print(node[i])
}
}