

文章目录
摘要
这道题看起来像是"排队问题",但实际上是一道非常典型的**贪心算法(Greedy Algorithm)**题。
我们需要根据每个人的身高和前面有多少个比他高的人,重新还原出整个队列的顺序。
一开始看这题可能有点懵:"前面到底谁该站哪儿?"
但其实它隐藏着一个非常自然的规律:高个子的人不受矮个子影响,矮个子必须让高个子先排好 。
抓住这个思路,整个问题就能一步步理清。

描述
题目给了一个二维数组 people,每个元素 [h, k] 表示:
h:这个人的身高;k:他前面有多少个身高大于等于h的人。
我们要根据这些条件,还原出他们最初排队的顺序。
示例 1:
txt
输入: [[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 个比自己高或一样高的人。
示例 2:
txt
输入: [[6,0],[5,0],[4,0],[3,2],[2,2],[1,4]]
输出: [[4,0],[5,0],[2,2],[3,2],[1,4],[6,0]]
题解答案
这题的关键点是:
我们希望每个人都能在"正确的位置",也就是在他前方有 k 个比他高的人。
贪心策略是:
- 先让高个子的人排好队(他们不受矮个子影响);
- 再把矮个子插入到他们的正确位置中。
核心思路:
-
排序规则:
- 先按身高
h降序排序(高个子优先); - 当身高相同时,按
k升序排序(前面人少的先排)。
这样做的好处是:我们可以优先确定"高个子"的相对顺序,因为他们不会被矮个子挡住。
- 先按身高
-
插入队列:
- 按照排序好的顺序,一个一个把人插入到结果数组中;
- 每次都插入到索引位置
k,这样能保证前面有k个比他高或一样高的人。

题解代码分析
下面是完整的 Swift 实现,逻辑清晰且可以直接运行
swift
import Foundation
class Solution {
func reconstructQueue(_ people: [[Int]]) -> [[Int]] {
// Step 1: 按照规则排序
// 身高降序;如果身高相同,则按 k 升序
let sortedPeople = people.sorted {
if $0[0] == $1[0] {
return $0[1] < $1[1]
}
return $0[0] > $1[0]
}
// Step 2: 逐个插入
var result = [[Int]]()
for person in sortedPeople {
let index = person[1]
// 在 index 位置插入该人
if index >= result.count {
result.append(person)
} else {
result.insert(person, at: index)
}
}
return result
}
}
// 示例测试
let solution = Solution()
let example1 = [[7,0],[4,4],[7,1],[5,0],[6,1],[5,2]]
let example2 = [[6,0],[5,0],[4,0],[3,2],[2,2],[1,4]]
print(solution.reconstructQueue(example1))
print(solution.reconstructQueue(example2))
代码逐步解析
-
排序逻辑:
swiftlet sortedPeople = people.sorted { if $0[0] == $1[0] { return $0[1] < $1[1] } return $0[0] > $1[0] }举个例子,如果输入是:
[[7,0],[4,4],[7,1],[5,0],[6,1],[5,2]]排序结果会变成:
[[7,0],[7,1],[6,1],[5,0],[5,2],[4,4]]也就是说先排高个子,再排低个子。
-
插入逻辑:
swiftvar result = [[Int]]() for person in sortedPeople { let index = person[1] if index >= result.count { result.append(person) } else { result.insert(person, at: index) } }这里的"插入"其实就是把当前人放到他应在的位置上。
比如
[7,1]会插入到下标 1,代表他前面有 1 个身高 ≥7 的人。 -
过程理解:
- 先放
[7,0]→[[7,0]] - 再放
[7,1]→[[7,0],[7,1]] - 放
[6,1]→ 插入到索引 1 →[[7,0],[6,1],[7,1]] - 依此类推。
最终重建出来的队列是:
[[5,0],[7,0],[5,2],[6,1],[4,4],[7,1]] - 先放
示例测试及结果
让我们来跑几组测试看看效果:
swift
print(solution.reconstructQueue([[7,0],[4,4],[7,1],[5,0],[6,1],[5,2]]))
// 输出: [[5,0],[7,0],[5,2],[6,1],[4,4],[7,1]]
print(solution.reconstructQueue([[6,0],[5,0],[4,0],[3,2],[2,2],[1,4]]))
// 输出: [[4,0],[5,0],[2,2],[3,2],[1,4],[6,0]]
输出完全符合预期。
时间复杂度
- 排序:O(n log n)
- 插入:最坏情况下,每次插入都可能移动 O(n) 元素,因此是 O(n²)
总时间复杂度:
O(n²)
空间复杂度
我们额外使用了一个结果数组:
O(n)
总结
这道题其实是贪心思想在现实场景中的一个非常有趣的例子。
如果你想象一下排队的画面,高个子总是"先站",他们不会被别人遮挡;矮个子要找到属于自己的"缝隙"插进去。
这正好对应了我们算法中的操作:
- 按身高从高到低排序;
- 按 k 值插入合适位置。
在实际开发中,这种"先确定约束强的一类,再插入约束弱的一类"的策略非常常见,比如:
- UI 布局系统中优先放置固定位置元素;
- 排序系统中优先处理高优先级任务;
- 渐进式布局算法中优先处理大块区域。
所以,不仅仅是算法题,它也能训练我们在系统设计中"找主次关系"的思维。