大厂真题 / 华为

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

本场考试概述

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

考点分析

  1. 第一题:电影放映调度——贪心 + 区间调度(难度简单)
  2. 第二题:快来排队买包子——队列模拟(难度中等)
  3. 第三题:容器镜像 Top-K 大小统计——前序中序重建二叉树 + 路径和剪枝(难度中等)

建议策略

  1. 第一题是经典区间调度,按结束时间排序后贪心选不重叠的区间即可稳拿
  2. 第二题队列模拟要抠清细节:多窗口同时叫号、优先分配编号最小的窗口、买完一笼还想买就回队尾重新取号
  3. 第三题先用前序加中序重建二叉树,再 DFS 累加路径和作为完整大小,完整大小 $\leq 0$ 时整棵子树剪枝,最后取最大的 $K$ 个排序输出

第 1 题:电影放映调度问题

题目描述

某电影院有一块银幕,每天需安排多部电影放映。给定 $n$ 部电影的放映时间区间 $[s_i, e_i]$,同一时间只能放映一部电影,前一场结束时间等于后一场开始时间可以连续放映。求最多可以放映多少部电影。

样例

输入

3
540 660
540 600
660 780

输出

2

输入

5
0 1000
100 900
200 800
300 700
400 600

输出

1

思路分析

第一步:识别经典问题

从若干区间中选出尽量多的两两不重叠区间,这是最经典的区间调度问题。

第二步:按结束时间贪心

将所有电影按结束时间升序排序。结束越早的电影占用银幕时间越短,能给后续电影留出越多空间。维护上一部已选电影的结束时间 last_end,扫描时只要当前电影的开始时间 $\geq$ last_end,就选中它。

第三步:端点相接

题目规定结束时间等于开始时间可以连续放映,因此衔接判定用 $\geq$ 而非 $>$。

题解代码

import sys
input = sys.stdin.readline

def solve():
    n = int(input())
    movies = []
    for _ in range(n):
        s, e = map(int, input().split())
        movies.append((e, s))
    movies.sort()
    cnt = 0
    last_end = -1
    for end, start in movies:
        if start >= last_end:
            cnt += 1
            last_end = end
    print(cnt)

solve()

复杂度分析

时间复杂度:$O(n \log n)$,排序主导。 空间复杂度:$O(n)$。


第 2 题:快来排队买包子

题目描述

包子铺有 $m$ 个窗口,大厅中有 $n$ 位顾客按取号顺序排队。每个窗口每次服务一位顾客,服务时长 1 秒(买一笼包子)。规则如下:

  • 多个窗口空闲时同时叫号,优先分配编号最小的窗口
  • 每位顾客一次只能买一笼,若还想买更多需回到队尾重新取号
  • 文本为空时的 POP 无效果

给定每位顾客想买的笼数数组和目标顾客编号 $k$(下标从 0 开始),求目标顾客买完所有包子的总耗时(秒)。

样例

输入

2 1 2
2
2

输出

3

输入

5 3 3 1 4
2
3

输出

4

思路分析

第一步:逐秒模拟

顾客数 $n \leq 100$,窗口数 $m \leq 100$,每人最多买若干笼,总笼数有限。直接按秒模拟整个过程即可。

第二步:模拟核心逻辑

每过 1 秒,所有在服务中的窗口各服务完一笼,然后:

  • 剩余笼数为 0 的顾客离开
  • 剩余笼数 > 0 的顾客按窗口编号从小到大的顺序追加回队尾
  • 重新从队首取人填满空闲窗口

第三步:命中目标

若本秒离开的顾客正好是目标顾客 $k$,当前秒数即为答案。

题解代码

import sys
from collections import deque

def solve():
    data = sys.stdin.read().split('\n')
    remaining = list(map(int, data[0].split()))
    n = len(remaining)
    k = int(data[1].strip())
    m = int(data[2].strip())

    hall = deque(range(n))
    windows = [-1] * m

    # 初始叫号:编号小的窗口优先从队首取人
    for w in range(m):
        if hall:
            windows[w] = hall.popleft()

    t = 0
    while True:
        t += 1
        returners = []
        done = False
        for w in range(m):
            c = windows[w]
            if c == -1:
                continue
            remaining[c] -= 1
            windows[w] = -1
            if remaining[c] == 0:
                if c == k:
                    done = True
            else:
                returners.append(c)
        for c in returners:
            hall.append(c)
        if done:
            print(t)
            return
        # 重新叫号填满空闲窗口
        for w in range(m):
            if windows[w] == -1 and hall:
                windows[w] = hall.popleft()

solve()

复杂度分析

时间复杂度:$O(S \times m)$,其中 $S$ 为所有顾客需求总笼数。每秒最多消化 $m$ 笼,模拟最多 $S/m$ 轮。 空间复杂度:$O(n + m)$。


第 3 题:容器镜像 Top-K 大小统计

题目描述

容器镜像层用二叉树管理,节点值表示镜像层大小(可为负数,表示裁剪文件)。节点的”镜像完整大小”为从根到该节点路径上所有值之和。

给定二叉树的前序遍历和中序遍历(各节点值互不相同),还原二叉树后:

  • 若某节点的镜像完整大小 $\leq 0$,则该节点连同整棵子树剪枝
  • 输出剩余节点中最大的 $K$ 个镜像完整大小,按从小到大排序

若剪枝后为空,输出 null

样例

输入

8 2 -3 9 5
2 8 9 -3 5
3

输出

10 10 14

输入

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

输出

4

思路分析

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

前序第一个元素是当前子树的根。在中序中找到该根的位置,左边为左子树中序、右边为右子树中序,由左子树节点个数分割前序序列。

第二步:路径和即镜像完整大小

从根往下递推:$\text{size}(v) = \text{parent_sum} + \text{layer}(v)$。在重建过程中同步计算,无需额外遍历。

第三步:剪枝

当 $\text{size}(v) \leq 0$ 时,该节点和整棵子树异常,直接停止对该子树的递归——任何子节点的路径和只会在此基础上累加,恢复为正数的可能性存在但题目要求整棵子树一律剪掉。

第四步:取 Top-K

收集所有合法节点的镜像完整大小后排序,取最大的 $K$ 个升序输出。用显式栈代替递归避免链状树的栈溢出。

题解代码

import sys

def solve():
    data = sys.stdin.read().split('\n')
    preorder = list(map(int, data[0].split()))
    inorder = list(map(int, data[1].split()))
    K = int(data[2].strip())
    n = len(preorder)

    pos = {}
    for i, v in enumerate(inorder):
        pos[v] = i

    sizes = []
    stack = []
    if n > 0:
        stack.append((0, n-1, 0, n-1, 0))
    while stack:
        pl, pr, il, ir, parent = stack.pop()
        if pl > pr:
            continue
        root = preorder[pl]
        size = parent + root
        if size <= 0:
            continue
        sizes.append(size)
        kidx = pos[root]
        left_cnt = kidx - il
        stack.append((pl + 1, pl + left_cnt, il, kidx - 1, size))
        stack.append((pl + left_cnt + 1, pr, kidx + 1, ir, size))

    if not sizes:
        print("null")
    else:
        sizes.sort()
        start = max(0, len(sizes) - K)
        print(" ".join(map(str, sizes[start:])))

solve()

复杂度分析

时间复杂度:$O(n \log n)$,重建 $O(n)$,排序 $O(n \log n)$。 空间复杂度:$O(n)$。


小结

  • 第一题是签到级的经典区间调度,按结束时间排序后贪心选取即可
  • 第二题队列模拟看似简单,但多窗口同时叫号、窗口编号优先级、买完回队尾这些细节容易漏,建议用样例手动走一遍确认
  • 第三题是二叉树重建模板题的变形,核心在于同步计算路径和并做剪枝,用显式栈代替递归处理极端链状树