华为非AI方向笔试真题 - 容器镜像平均大小统计

容器镜像平均大小统计(Py/Java/C++/Js/Go)题解

华为笔试真题 6月12号 非AI方向 第一题 200分题型

题目内容

在容器镜像管理系统中,容器镜像通常采用堆叠方式管理和挂载,为了减少镜像管理系统中重复的存储量,假定容器镜像层采用二叉树管理。镜像层二叉树节点描述镜像层大小,节点的镜像完整大小为镜像层大小及其所有父镜像层大小之和。由于业务需要,现在需要对系统中所有的客户镜像大小统计分析,统计镜像平均大小:输入为容器镜像二叉树前序遍历数组和中序遍历数组,输出为平均镜像大小,输出结果忽略小数并向下取整。

说明:当镜像完整大小小于等于 0 时,则表示该镜像节点异常,异常镜像节点需要剪枝;例如:节点 A 有子节点 B 和子节点 C,如果节点 A 完整镜像大小为 0,则节点 A 需要剪枝,即节点 A、节点 B、节点 C 均需要从镜像二叉树中删除。

输入描述

第一行:容器镜像树前序遍历结果。 第二行:容器镜像树中序遍历结果。

说明:

  1. 容器镜像树前序遍历、中序遍历结果中的数字表示当前镜像层大小,取值范围为 -10000, 10000
  2. 镜像层大小为负数时表示该层基于父镜像裁剪文件,镜像层为正数时表示该层基于父镜像新增文件,0则表示镜像层未做任何更改或者裁剪文件大小与新增文件大小相等
  3. 节点规模数量 ≤ 1000
  4. 为了能够通过前序遍历和中序遍历还原唯一的镜像二叉树,输入的各节点镜像层大小都不相同

输出描述

容器平均镜像大小,如果平均数为小数时,则向下取整。

说明:

  1. 当计算最终结果为空二叉树时输出 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. 根镜像大小为 1
  2. 左节点镜像大小为 3,为根节点1与左节点2之和
  3. 右节点镜像大小为 4,为根节点1与右节点3之和
    则平均值为 (1+3+4)/3=2.6666...(1 + 3 + 4) / 3 = 2.6666...(1+3+4)/3=2.6666...,向下取整后为 2。

题解

思路

二叉树

  1. 利用前序遍历和后序遍历结果还原二叉树,前序遍历的顺序为父节点->左子->右子,中序遍历顺序为左子->父节点->右子,简单说说逻辑

    1. 根据前序遍历规律,可以得出preOrder[PL]为父节点的值。
    2. 在中序遍历结果找到父节点的位置为j,那么可以得出左子树长度为j - 1 - IL + 1 = j - IL
    3. 从而可以得出左/右子树前序遍历对应索引为
      • preOrderLeftStart = PL + 1, preOrderLeftEnd = preOrderLeftStart + len - 1
      • preOrderRightStart = preOrderLeftEnd + 1, preOrderRightEnd = PR
    4. 左/右子树中序遍历对应索引为
      • inOrderLeftStart = IL, inOrderLeftEnd = j - 1
      • inOrderRightStart = j + 1, inOrderRightEnd = IR
    • 按照1、2、3规律便可还原二叉树
  2. 利用还原的二叉树,递归获取镜像层大小大于0的节点加入数组中,遇到镜像层大小小于等于0时进行剪枝。

  3. 统计数组总和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))
}
相关推荐
Keep_Trying_Go1 小时前
华为开源框架MindSpore基本使用
华为·开源
无限码力1 小时前
华为非AI方向0612笔试真题-循环异或加密器(详细思路+多语言题解)
算法·华为·华为非ai方向笔试真题·华为笔试真题·华为0612笔试真题
凌波粒1 小时前
LeetCode--1584. 连接所有点的最小费用(最小生成树/Prim算法/Kruskal算法)
算法·leetcode·职场和发展
simidagogogo1 小时前
生产环境推荐系统最隐蔽的坑:Training-Serving Skew 详解与实战
算法·spark·推荐算法
烛衔溟1 小时前
HarmonyOS 工程目录、配置文件与 Stage 模型核心
华为·harmonyos
祭曦念1 小时前
【共创季稿事节】鸿蒙原生 ArkTS 布局:NavRouter + NavDestination 导航布局实战
ubuntu·华为·harmonyos
大白话_NOI1 小时前
【二分答案】附通用模板
c++·算法
三行数学2 小时前
数学周刊第23期(2026年06月08日-06月14日)南师数科院万仁辉副教授成果登顶国际数学四大顶刊之一<数学年刊>
算法·数学周刊
阿文的代码库2 小时前
算法专题:独特的电子邮件地址
linux·运维·算法