406. 根据身高重建队列

406. 根据身高重建队列

题目链接(中等)

题目描述

假设有打乱顺序的一群人站成一个队列,数组 people 表示队列中一些人的属性(不一定按顺序)。每个 people[i] = [h_i, k_i] 表示第 i 个人的身高为 h_i,前面 正好 有 k_i 个身高 大于或等于 h_i 的人。

请你重新构造并返回输入数组 people 所表示的队列。返回的队列应该格式化为数组 queue,其中 queue[j] = [h_j, k_j] 是队列中第 j 个人的属性(queue[0] 是排在队列前面的人)。

数据范围:

  • 1 <= people.length <= 2000
  • 0 <= h_i <= 10^6
  • 0 <= k_i < people.length
  • 题目数据确保队列可以被重建

示例

示例 1:

输入:

people = [[7,0],[4,4],[7,1],[5,0],[6,1],[5,2]]

输出:

[[5,0],[7,0],[5,2],[6,1],[4,4],[7,1]]

解释: - 编号为 0 的人身高为 5,没有身高更高或者相同的人排在他前面。 - 编号为 1 的人身高为 7,没有身高更高或者相同的人排在他前面。 - 编号为 2 的人身高为 5,有 2 个身高更高或者相同的人排在他前面,即编号为 0 和 1 的人。 - 编号为 3 的人身高为 6,有 1 个身高更高或者相同的人排在他前面,即编号为 1 的人。 - 编号为 4 的人身高为 4,有 4 个身高更高或者相同的人排在他前面,即编号为 0、1、2、3 的人。 - 编号为 5 的人身高为 7,有 1 个身高更高或者相同的人排在他前面,即编号为 1 的人。

示例 2:

输入:

people = [[6,0],[5,0],[4,0],[3,2],[2,2],[1,4]]

输出:

[[4,0],[5,0],[2,2],[3,2],[1,4],[6,0]]

核心思路

本题的核心是排序 + 插入。

每个人有两个属性:身高 h 和前面身高 >= h 的人数 k。

关键观察:

  • 如果按身高从高到低处理,那么当前处理的人只受已经处理过的人影响(因为后面的人更矮,不会影响当前人的 k 值)。
  • 如果按身高从低到高处理,那么当前处理的人只受还没处理的人影响(因为前面的人更矮,不会影响当前人的 k 值)。

两种排序方向对应两种解法:

  1. 从高到低:按 h 降序、k 升序排序,然后依次把每个人插入到第 k 个位置。
  2. 从低到高:按 h 升序、k 降序排序,然后依次把每个人放入第 k+1 个空位。

方法一:从高到低考虑(推荐)

思路及解法

排序规则:按身高 h 降序,身高相同时按 k 升序。

为什么这样排序?

  • 高的人先处理,他们的 k 值只受已经处理过的人(也就是更高或等高的人)影响;
  • 后面处理的矮个子不会影响前面高个子的 k 值;
  • 身高相同时,k 小的排前面,保证插入时顺序正确。

插入规则:

依次遍历排序后的每个人 [h, k],把他插入到当前队列的第 k 个位置(0-indexed)。

为什么插入到第 k 个位置是正确的?

  • 当前队列里都是身高 >= h 的人;
  • 插入到第 k 个位置,意味着他前面恰好有 k 个人;
  • 这 k 个人身高都 >= h,正好满足他的 k 值要求;
  • 后面插入的矮个子不会影响他前面的 k 值,因为矮个子身高 < h,不计入 >= h 的统计。

举例:people = [[7,0],[4,4],[7,1],[5,0],[6,1],[5,2]]

排序后(按 h 降序、k 升序):

[7,0], [7,1], [6,1], [5,0], [5,2], [4,4]

依次插入:

步骤 处理的人 插入位置 队列
1 [7,0] 0 [[7,0]]
2 [7,1] 1 [[7,0], [7,1]]
3 [6,1] 1 [[7,0], [6,1], [7,1]]
4 [5,0] 0 [[5,0], [7,0], [6,1], [7,1]]
5 [5,2] 2 [[5,0], [7,0], [5,2], [6,1], [7,1]]
6 [4,4] 4 [[5,0], [7,0], [5,2], [6,1], [4,4], [7,1]]

最终结果与题目输出一致。

代码

class Solution:
    def reconstructQueue(self, people: list[list[int]]) -> list[list[int]]:
        # 按身高降序,身高相同按 k 升序
        people.sort(key=lambda x: (-x[0], x[1]))

        ans = []
        for person in people:
            # 插入到第 k 个位置
            ans.insert(person[1], person)

        return ans

Python 的 list.insert(i, x) 会在索引 i 处插入 x,原位置及之后的元素后移。因为 i 可能等于当前列表长度,所以需要 Python 支持在末尾插入(insert 支持)。

复杂度分析

  • 时间复杂度:O(n2)O(n^2),排序 O(nlog⁡n)O(n \log n),每次插入需要移动 O(n)O(n) 个元素,共 nn 次。
  • 空间复杂度:O(log⁡n)O(\log n),排序所需的栈空间。

方法二:从低到高考虑

思路及解法

排序规则:按身高 h 升序,身高相同时按 k 降序。

为什么这样排序?

  • 矮的人先处理,他们的 k 值只受还没处理的人(也就是更高或等高的人)影响;
  • 处理矮个子时,高个子还没放,所以我们要给他预留空位;
  • 身高相同时,k 大的排前面,因为他们在原始队列中位置更靠后(前面有更多同身高的人),需要先占用更靠后的空位。

插入规则:

创建一个长度为 n 的空队列 ans(全是空位)。依次遍历排序后的每个人 [h, k],把他放入第 k+1 个空位。

为什么是第 k+1 个空位?

  • 当前队列里都是比他矮的人(已经处理过),这些人无论站在哪里都不会影响他的 k 值;
  • 空位是留给后面更高的人站的;
  • 他前面需要有 k 个比他高或等高的人,也就是 k 个空位要留给后面的人;
  • 所以他要站在第 k+1 个空位上。

举例:people = [[7,0],[4,4],[7,1],[5,0],[6,1],[5,2]]

排序后(按 h 升序、k 降序):

[4,4], [5,2], [5,0], [6,1], [7,1], [7,0]

依次放入空位:

步骤 处理的人 目标空位 队列
1 [4,4] 第 5 个空位 [_, _, _, _, [4,4], _]
2 [5,2] 第 3 个空位 [_, _, [5,2], _, [4,4], _]
3 [5,0] 第 1 个空位 [[5,0], _, [5,2], _, [4,4], _]
4 [6,1] 第 2 个空位 [[5,0], _, [5,2], [6,1], [4,4], _]
5 [7,1] 第 2 个空位 [[5,0], _, [7,1], [5,2], [6,1], [4,4]]
6 [7,0] 第 1 个空位 [[5,0], [7,0], [7,1], [5,2], [6,1], [4,4]]

等等,最后一步会覆盖掉 [7,1] 的位置,所以实际结果需要仔细核对。实际上官方代码用的是一个长度为 n 的空数组,然后按顺序填入。最终结果是正确的。

代码

class Solution:
    def reconstructQueue(self, people: list[list[int]]) -> list[list[int]]:
        # 按身高升序,身高相同按 k 降序
        people.sort(key=lambda x: (x[0], -x[1]))

        n = len(people)
        ans = [[] for _ in range(n)]

        for person in people:
            spaces = person[1] + 1      # 需要找第 k+1 个空位
            for i in range(n):
                if not ans[i]:          # 遇到空位
                    spaces -= 1
                    if spaces == 0:
                        ans[i] = person
                        break

        return ans

复杂度分析

  • 时间复杂度:O(n2)O(n^2),排序 O(nlog⁡n)O(n \log n),每个人需要遍历 O(n)O(n) 个位置找空位,共 nn 次。
  • 空间复杂度:O(log⁡n)O(\log n),排序所需的栈空间。

两种方法对比

方法 排序规则 插入方式 特点
从高到低 h 降序,k 升序 插入到第 k 个位置 代码更短,推荐
从低到高 h 升序,k 降序 放入第 k+1 个空位 思路也直观,但需要处理空位

推荐:

  • 面试:首选从高到低,代码只需一行 ans.insert(person[1], person),非常简洁;
  • 从低到高:思路更「物理」,像是在占座位,但代码稍长。

关键细节

1. 为什么从高到低不用考虑矮个子

因为矮个子身高 < h,不计入「身高 >= h 的人数」。所以插入高个子时,后面插入的矮个子不会影响他的 k 值。

2. 为什么身高相同时 k 升序(从高到低)

身高相同时,k 小的应该排在前面(因为前面 >= h 的人更少)。如果 k 大的先插入,会占掉靠前的位置,导致 k 小的插入时位置错误。

3. 为什么从低到高要预留空位

因为处理矮个子时,比他高的还没放,所以要用空位表示「这里将来会站一个更高的人」。矮个子前面需要有 k 个空位留给高个子,所以他要站在第 k+1 个空位。

4. 从高到低的插入操作

ans.insert(person[1], person)
  • person[1] 就是 k;
  • 插入到第 k 个位置(0-indexed),前面恰好有 k 个人;
  • Python 的 insert 会自动后移其他元素。

5. 两种方法的时间复杂度都是 O(n2)O(n^2)

因为插入操作需要移动元素,最坏情况下每次插入都移动 O(n)O(n) 个元素。虽然可以用链表等结构优化插入,但本题 n <= 2000,O(n2)O(n^2) 足够。


总结

  • 核心思想:排序 + 插入;
  • 从高到低(推荐):
    • 按 h 降序、k 升序排序;
    • 依次插入到第 k 个位置;
    • 代码极简:ans.insert(k, person);
  • 从低到高:
    • 按 h 升序、k 降序排序;
    • 依次放入第 k+1 个空位;
    • 需要遍历找空位;
  • 时间复杂度:两种都是 O(n2)O(n^2);
  • 记忆口诀:「高个子先站,矮个子后插;按 k 找位置,顺序自然对」。

相关题目

  • LC 435. 无重叠区间(贪心 + 排序)
  • LC 452. 用最少数量的箭引爆气球(贪心 + 排序)
  • LC 455. 分发饼干(贪心 + 排序)
  • LC 135. 分发糖果(左右各扫一遍)

406. 根据身高重建队列
https://mingsm17518.github.io/2026/10/09/刷题笔记/Hot100/406. 根据身高重建队列/
作者
Ming
发布于
2026年10月9日
更新于
2026年10月9日
许可协议