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

则平均值为 (8+5+10+14+10)/5=9.4(8 + 5 + 10 + 14 + 10) / 5 = 9.4(8+5+10+14+10)/5=9.4,向下取整后为 9。
样例2
输入
1 2 3 -5 5 6
2 1 3 5 5 -5 6
输出
2
说明
如图所示,左图为输入的二叉树,由于 -5 节点计算出镜像完整大小为 -1,小于等于 0,因此属于非法节点,需要剪枝处理,剪枝后得到最右边的二叉树。

- 根镜像大小为 1
- 左节点镜像大小为 3,为根节点1与左节点2之和
- 右节点镜像大小为 4,为根节点1与右节点3之和
则平均值为 (1+3+4)/3=2.6666...(1 + 3 + 4) / 3 = 2.6666...(1+3+4)/3=2.6666...,向下取整后为 2。
题解
思路
二叉树
-
利用前序遍历和后序遍历结果还原二叉树,前序遍历的顺序为
父节点->左子->右子,中序遍历顺序为左子->父节点->右子,简单说说逻辑- 根据前序遍历规律,可以得出
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时进行剪枝。
-
统计数组总和
sum,并执行sum / 数组长度就是结果
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);
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 << 0;
return 0;
}
int sum = 0;
int m = node.size();
for (auto& v : node){
sum += v;
}
cout << sum / m;
return 0;
}
java
java
import java.io.*;
import java.util.*;
public class Main {
static class Node {
Node left;
Node right;
int value;
Node(int value) {
this.value = value;
}
}
// 通过前序和中序还原二叉树
static Node generateNode(List<Integer> preorder, List<Integer> inOrder,
int PL, int PR, int IL, int IR) {
if (PL > PR) {
return null;
}
Node root = new Node(preorder.get(PL));
// 确定左子树的长度
int j = IL;
for (; j <= IR; j++) {
if (preorder.get(PL).equals(inOrder.get(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();
String[] preStr = input1.split(" ");
String[] inStr = input2.split(" ");
List<Integer> preOrder = new ArrayList<>();
List<Integer> inOrder = new ArrayList<>();
for (String s : preStr) {
preOrder.add(Integer.parseInt(s));
}
for (String s : inStr) {
inOrder.add(Integer.parseInt(s));
}
int n = preOrder.size();
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.println(0);
return;
}
int sum = 0;
for (int v : node) {
sum += v;
}
System.out.println(sum / node.size());
}
}
python
python
import sys
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 is not None:
dfs(root.left, value, node)
if root.right is not None:
dfs(root.right, value, node)
lines = sys.stdin.read().strip().splitlines()
input1 = lines[0]
input2 = lines[1]
preorder = list(map(int, input1.split()))
inorder = list(map(int, input2.split()))
n = len(preorder)
root = generate_node(preorder, inorder, 0, n - 1, 0, n - 1)
# 记录有效节点值
node = []
dfs(root, 0, node)
if not node:
print(0)
else:
print(sum(node) // len(node))
javascript
js
const readline = require('readline');
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout
});
const lines = [];
rl.on('line', line => {
lines.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 !== null) {
dfs(root.left, value, node);
}
if (root.right !== null) {
dfs(root.right, value, node);
}
}
const preorder = lines[0].trim().split(/\s+/).map(Number);
const inorder = lines[1].trim().split(/\s+/).map(Number);
const n = preorder.length;
const root = generateNode(
preorder,
inorder,
0,
n - 1,
0,
n - 1
);
// 记录有效节点值
const node = [];
dfs(root, 0, node);
if (node.length === 0) {
console.log(0);
return;
}
let sum = 0;
for (const v of node) {
sum += v;
}
console.log(Math.floor(sum / node.length));
});
Go
go
package main
import (
"bufio"
"fmt"
"os"
"strconv"
"strings"
)
type Node struct {
left *Node
right *Node
value int
}
// 通过前序和中序还原二叉树
func generateNode(preorder []int, 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() {
scanner := bufio.NewScanner(os.Stdin)
scanner.Scan()
input1 := scanner.Text()
scanner.Scan()
input2 := scanner.Text()
preStr := strings.Fields(input1)
inStr := strings.Fields(input2)
preorder := make([]int, len(preStr))
inorder := make([]int, len(inStr))
for i, s := range preStr {
preorder[i], _ = strconv.Atoi(s)
}
for i, s := range inStr {
inorder[i], _ = strconv.Atoi(s)
}
n := len(preorder)
root := generateNode(
preorder,
inorder,
0,
n-1,
0,
n-1,
)
// 记录有效节点值
node := make([]int, 0)
dfs(root, 0, &node)
if len(node) == 0 {
fmt.Println(0)
return
}
sum := 0
for _, v := range node {
sum += v
}
fmt.Println(sum / len(node))
}