大厂真题 / 华为

华为 6.12 笔试真题 - 研发岗(非AI方向)

本场考试概述

考试时间:2026年6月12日 考试岗位:研发岗(通软、嵌软、测试、普通算法、数据科学等非AI方向) 难度评级:中等

考点分析

  1. 第一题:循环异或加密器——字符串模拟 + 位运算(难度简单)
  2. 第二题:容器镜像平均大小统计——前序中序重建二叉树 + 路径和剪枝(难度中等)
  3. 第三题:楼内救人——双层网格 BFS 最短路(难度中等)

建议策略

  1. 第一题是送分题,用下标对 key 长度取模就能同时处理”循环使用”和”截断”,先稳稳拿下
  2. 第二题是二叉树重建模板题,难点在于一边还原一边递推完整大小、并对非正子树整体剪枝,注意累加和要用 64 位整数
  3. 第三题把每个格子建模成”楼层 + 行 + 列”三维状态,楼梯作为唯一的跨层边,套 BFS 即可

第 1 题:循环异或加密器

题目描述

给定一个十六进制密钥 key 和一个十六进制数据 data,实现循环异或操作。将 key 和 data 的每个十六进制字符转换为 4 位二进制数值,对 data 的每一位与 key 的对应位进行异或。对齐方式为从左向右对齐:当 key 长度小于 data 时循环使用 key;当 key 长度大于 data 时截断多余部分。

输入 $T$ 组数据,每组一对 key 和 data,输出异或后的十六进制大写字符串。

样例

输入

2
AB 12345
FF A5

输出

B99FF
5A

思路分析

第一步:观察题目性质

两个十六进制字符逐位异或等价于将它们各自转换为 0~15 的数值后直接做整数异或运算,因为异或是按比特操作的,4 位二进制恰好对应一个十六进制字符。

第二步:处理循环和截断

data 的第 $i$ 个字符固定与 key 的第 $i \mod \text{len(key)}$ 个字符异或。取模运算天然实现了 key 不足时循环、超长时截断两种情形,无需显式拼接或裁剪 key。

第三步:实现

Python 内置 int(ch, 16) 将十六进制字符转数值,异或后用 f"{v:X}" 转回大写十六进制字符。

题解代码

import sys
input = sys.stdin.readline

t = int(input())
for _ in range(t):
    key, data = input().split()
    klen = len(key)
    res = []
    for i, ch in enumerate(data):
        dv = int(ch, 16) ^ int(key[i % klen], 16)
        res.append(f"{dv:X}")
    print("".join(res))

复杂度分析

时间复杂度:$O(\sum L)$,$L$ 为每组 data 的长度

空间复杂度:$O(L)$


第 2 题:容器镜像平均大小统计

题目描述

容器镜像层采用二叉树管理,节点值表示镜像层大小,节点的镜像完整大小为自身层大小与所有父镜像层大小之和(即根到该节点的路径和)。输入为容器镜像二叉树的前序遍历数组和中序遍历数组,各节点值互不相同。

当镜像完整大小 $\leq 0$ 时,该节点异常,需要连同其整棵子树一并剪枝。输出剩余存活节点的平均镜像完整大小(向下取整)。当结果为空树时输出 $0$。

样例

输入

1 2 3 -5 6
2 1 3 -5 6

输出

2

思路分析

第一步:前序 + 中序重建二叉树

前序遍历的首元素是当前子树的根;在中序遍历中定位这个根,其左侧为左子树、右侧为右子树。对左右两段递归套用同一规则即可唯一还原整棵树。

为避免树退化成链时递归过深(栈溢出),使用迭代写法:顺序扫描前序序列,用栈维护当前尚未接上右孩子的祖先链,中序指针标记下一个待回溯的节点。

第二步:路径和计算与剪枝

从根做 DFS,向下传递路径和。节点 $u$ 的完整大小 $S(u) = S(\text{父}) + \text{val}(u)$。一旦 $S(u) \leq 0$,立即跳过(等价于删除整棵子树,因为子树中任一节点都以 $u$ 为必经祖先)。

第三步:统计结果

遍历中累计存活节点的完整大小之和 total 与数量 count。空树输出 $0$,否则输出 $\lfloor \text{total} / \text{count} \rfloor$。存活节点的完整大小恒为正数,整除即向下取整。

题解代码

import sys
input = sys.stdin.readline

pre = list(map(int, input().split()))
ino = list(map(int, input().split()))
n = len(pre)
if n == 0:
    print(0)
    sys.exit()

lc = [-1] * n
rc = [-1] * n

stack = [0]
ii = 0
for k in range(1, n):
    if pre[stack[-1]] != ino[ii]:
        lc[stack[-1]] = k
    else:
        last = stack[-1]
        while stack and pre[stack[-1]] == ino[ii]:
            last = stack.pop()
            ii += 1
        rc[last] = k
    stack.append(k)

total = 0
count = 0
stk = [(0, 0)]
while stk:
    node, parent_sum = stk.pop()
    full_size = parent_sum + pre[node]
    if full_size <= 0:
        continue
    count += 1
    total += full_size
    if lc[node] != -1:
        stk.append((lc[node], full_size))
    if rc[node] != -1:
        stk.append((rc[node], full_size))

print(0 if count == 0 else total // count)

复杂度分析

时间复杂度:$O(n)$,重建和遍历各扫一遍

空间复杂度:$O(n)$,存储左右孩子数组和栈


第 3 题:楼内救人

题目描述

游戏地图是一个 $m \times n \times 2$ 的双层楼地图,每个格子代表一个区域:$0$ 是空地(可通行),$1$ 是墙壁(不可通行),$2$ 是起点(有且仅一个),$3$ 是终点(有且仅一个),$4$ 是楼梯(每层有且仅一个)。

玩家从起点出发,每次只能上下左右移动一格,不能穿过墙壁。通过楼梯跨层算 $1$ 步,除楼梯外无法跨层移动。求从起点到终点的最短路径长度(步数),无法到达返回 $-1$。

样例

输入

3 3
2 0 1
0 1 4
0 0 0
0 4 0
1 0 1
1 0 3

输出

9

思路分析

第一步:建模为图论问题

把每个可通行格子看作一个节点,用三元组 $(\text{层}, \text{行}, \text{列})$ 标识。同层四邻接各连一条权为 $1$ 的边;每层有且仅有一个楼梯,两层楼梯之间连一条权为 $1$ 的跨层边。

第二步:为什么用 BFS

所有边权都等于 $1$,BFS 按距离逐层扩展,节点首次出队时记录的距离即为最短距离,无需 Dijkstra。

第三步:实现要点

  • 用三维数组 dist[层][行][列] 记录步数,初值 $-1$ 同时充当未访问标记
  • 节点出队后向四个方向扩展,目标格需在界内、非墙壁、未访问
  • 仅当出队节点本身是楼梯(值为 $4$)时,才允许花 $1$ 步跳至另一层楼梯所在格
  • BFS 首次取出终点格时其 dist 即最短步数;队列耗尽仍未取出终点则输出 $-1$

题解代码

import sys
from collections import deque
input = sys.stdin.readline

m, n = map(int, input().split())
g = [[[0] * n for _ in range(m)] for _ in range(2)]
for l in range(2):
    for r in range(m):
        row = list(map(int, input().split()))
        for c in range(n):
            g[l][r][c] = row[c]

sl = sr = sc = 0
stair = [[0, 0], [0, 0]]
for l in range(2):
    for r in range(m):
        for c in range(n):
            if g[l][r][c] == 2:
                sl, sr, sc = l, r, c
            elif g[l][r][c] == 4:
                stair[l] = [r, c]

dist = [[[-1] * n for _ in range(m)] for _ in range(2)]
dist[sl][sr][sc] = 0
q = deque()
q.append((sl, sr, sc))
ans = -1
dr = [1, -1, 0, 0]
dc = [0, 0, 1, -1]

while q:
    l, r, c = q.popleft()
    d = dist[l][r][c]
    if g[l][r][c] == 3:
        ans = d
        break
    for k in range(4):
        nr, nc = r + dr[k], c + dc[k]
        if 0 <= nr < m and 0 <= nc < n and g[l][nr][nc] != 1 and dist[l][nr][nc] == -1:
            dist[l][nr][nc] = d + 1
            q.append((l, nr, nc))
    if g[l][r][c] == 4:
        ol = 1 - l
        tr, tc = stair[ol]
        if dist[ol][tr][tc] == -1:
            dist[ol][tr][tc] = d + 1
            q.append((ol, tr, tc))

print(ans)

复杂度分析

时间复杂度:$O(m \cdot n)$,每个状态至多入队出队各一次

空间复杂度:$O(m \cdot n)$,三维距离数组


小结

  • 第一题是纯模拟,取模运算一招搞定循环和截断
  • 第二题结合了经典的前序+中序重建二叉树与路径和计算,剪枝条件是”完整大小 $\leq 0$ 则连同子树删除”
  • 第三题是 BFS 最短路的变体,关键建模思路是把楼梯看作跨层边,三维状态空间确保每个位置只访问一次