406. 根据身高重建队列

难度:中等 | 主题:贪心——排序 + 插空

题目

每个人表示为 [h, k],h 是身高,k 是前面身高 ≥ h 的人数。重建队列。

示例:

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

思路

先讲个故事:电影院的排队

电影院入场,大家按身高排成一列。每个人只记得”前面有多少人比我高(或一样高)”。

如果你是检票员,你怎么帮他们恢复正确的顺序?

关键洞察:最高的人不受矮的人影响。因为矮的人站在高的人前面或后面,都不会改变高的人前面≥他的人数——矮的人本来就小于高的人。


引导式推导:从高到矮逐个插入

排序规则:按身高降序、k 值升序。高的先排。

插入规则:每个人插入到结果队列的第 k 个位置。

 
people.sort(key=lambda x: (-x[0], x[1]))
 
res = []
 
for p in people:
 
    res.insert(p[1], p)
 

为什么这样是对的?

排到某个人时,结果队列里已经全是身高 ≥ 他的人(因为高的先排)。所以此时他的 k 值就是正确的插入位置。


graph LR

    subgraph 排序+插空

        A["排序<br/>高→矮, k 小→大"] --> B["取最高的人"]

        B --> C["插入到第 k 位"]

        C --> D{"还有人吗?"}

        D -->|是| B

        D -->|否| E["返回结果"]

    end


代码

 
def reconstructQueue(self, people):
 
    people.sort(key=lambda x: (-x[0], x[1]))
 
    result = []
 
    for p in people:
 
        result.insert(p[1], p)
 
    return result
 

复杂度

指标解释
时间O(n²)排序 O(n log n),插入 O(n²)
空间O(n)结果列表

实战考量

频率分析

贪心排序经典题,约 20% 考察对”谁先排”的洞察。

延伸思考

Q:为什么先排高的而不是矮的?

A:矮的人插入不会影响高的人的 k 值——矮的人不计入高的人前面≥他的人数。从高到矮保证了每次插入时,结果队列中所有人的身高都 ≥ 当前人。

Q:时间能优化吗?

A:用平衡树(如 bisect + 链表)代替数组插入,可降到 O(n log n)。

Q:如果 k 是后面 ≥ 他的人数呢?

A:反过来,从矮到高排,或者从后往前插入。

易错点

  • 排序 key 是 (-x[0], x[1]),不是 (x[0], x[1])

  • insert 位置是 p[1],不是 p[0]

  • Python list insert 是 O(n)


生活类比

电影院排队 → 排序插空

最高的人先站好,他前面即使站了很多人,只要不比他高就不影响他的记忆。

这就好比在篮球场上,姚明先站到位置,然后其他人逐个插入。

矮的人插在姚明前面,姚明不会觉得”前面有人比我高”——因为确实没有。

先排不受影响的人,再排受影响的人。


相关题目

题目关系
452用最少数量的箭引爆气球区间排序
56合并区间区间排序
135分发糖果双向贪心

→ 返回题单:LeetCode学习路线图 > 十一、贪心

速记卡(面试闪卡)

Q1:一句话讲清「406. 根据身高重建队列」到底是什么?

A:按身高降序、k 升序排序后,逐个把人插入第 k 位,先排高个子再排矮的,贪心插空重建队列。

Q2:电影院排队(greedy insert) —— 怎么理解?

A:类比:每人只记”前面几个不低于我”。关键洞察:高个子不受矮个子影响——矮的站哪儿都不改变高的计数。所以让最高的人先站,其他人再逐个插到第 k 个空位,像姚明先定位、矮个随后插队。(Tall first)

Q3:排序加插空(sort + insert) —— 怎么理解?

A:类比:people.sort(key=lambda x:(-x[0], x[1])) 高→矮、k 小→大;再 result.insert(p[1], p) 插到第 k 位。排到某人时,队列里早已全是≥他的人,k 值天然正确。像发扑克牌按身高入座。(Sort then insert)

Q4:复杂度与优化(O(n²) time) —— 怎么理解?

A:类比:排序 O(n log n),但 list.insert 是 O(n),总 O(n²)、空间 O(n)。想提速用平衡树(bisect+链表)降到 O(n log n)。注意 insert 位置是 p[1] 不是 p[0]。(Insert cost)

Q5:为何先排高的(who goes first) —— 怎么理解?

A:类比:因为矮的插入不影响高的 k——矮的不算”不低于高的人”。若反过来先排矮的,后面高的一插就把矮的 k 全打乱。先排不受影响的,再排受影响的,是贪心排序的通用哲学。(Greedy order)

Q6:核心速记主线有哪些?

  • 题目:按 [h,k] 重建队列,k=前面≥h的人数

  • 思路:高个子先排,矮个后插(greedy)

  • 代码:排序(-h, k) 后 insert 到第 k 位

  • 复杂度:时间 O(n²)、空间 O(n)

  • 实战:先排不受影响者,insert 用 p[1]

口诀

A:身高排队有诀窍,

高的先站矮后到;

插到 k 位空处坐,

前面高个不乱套。

相关链接