根据身高重建队列 图解题解
这道题到底在问什么
- 输入
- 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]]
先想最直接的笨办法
笨办法:把所有人排成 n 的阶乘种顺序,逐个检查每个人前面的高个数是否等于 k。n 一到 2000,排列数直接爆炸,根本跑不动。(动画第 3 步)
最优解:一步一步想明白
- 3笨办法:把所有人排成 n 的阶乘种顺序,逐个检查每个人前面的高个数是否等于 k。n 一到 2000,排列数直接爆炸,根本跑不动。
- 4转折点:先按身高从高到低排,同身高的按 k 从小到大排。这样每次要插的人,都比队列里已有的人矮或一样高,于是直接把他插到下标 k,前面就刚好有 k 个不比他矮的人。
- 5为什么后插的矮个不会破坏前面高个的 k?因为高个只数「不比自己矮的人」,而后插进来的矮个比他矮,根本不会被高个数进去。插矮个对高个的 k 是「隐形」的。
- 6people.sort:身高降序,同高 k 升序排完序:7、7、6、5、5、4 身高从高到低。注意主舞台这个空队列就是答案 ans,待插入的人在右侧面板里排好队,一个个往里塞。
- 7ans.insert(0, [7,0])最高的 7 先进场。它的 k=0,要求前面没人不比它矮,所以放到队首下标 0。
- 8ans.insert(1, [7,1])第二个 7,k=1,插到下标 1。它前面正好站着一个 [7,0],身高 7 不比它矮,k 满足。
- 9ans.insert(1, [6,1])6 比队列里的两个 7 都矮。它 k=1,插到下标 1:原来站这儿的 [7,1] 被往右挤一格。看下标 2 的 [7,1] 是怎么被顶过去的。
- 10insert 的真相:插一个,后面全体右移一格把这一步删掉文字也要看懂:[6,1] 占了下标 1,[7,1] 从 1 退到 2。[7,1] 前面新增的是更矮的 [6,1],6 不比 7 矮的人不增加,所以 [7,1] 的 k=1 依然成立。这就是「矮个对高个隐形」的现场。
- 11ans.insert(0, [5,0])5 比前面所有人都矮,k=0,插到队首。后面三个人全体右移一格,但他们前面新增的是更矮的 5,k 全都不受影响。
- 12ans.insert(2, [5,2])第二个 5,k=2,插到下标 2。它前面是 [5,0] 和 [7,0],身高都至少 5,正好 2 个,条件满足。
- 13ans.insert(4, [4,4])最矮的 4 最后进场,k=4,插到下标 4。它前面四个人身高都不比 4 矮,全部满足,队列拼完。
- 14验证:数 [6,1] 前面有几个不比 6 矮的人随手抽查 [6,1]。它前面三个人里,只有 [7,0] 身高不比 6 矮,另外两个 5 都比 6 矮不算。正好 1 个,k=1 成立。算法对了。
- 15return ans最终队列不一定按身高有序,但每个人的 k 条件全都对。注意:答案可能不唯一,但本题保证至少有一个合法答案。
- 18一句话本质:当一个约束只跟「比自己更高的人」有关,就先把高个排定,后来的矮个对高个完全隐形,于是 k 直接当插入下标。
- 20若同高按 k 降序,错误一路潜伏到 [5,2] 才爆用本课代码真跑「同高 k 降序」:前几步并不会当场崩,错误是悄悄累积的——同高的两人里 k 大的先插,随后被 k 小的插入挤动,相对次序就乱了。最终在更矮的 [5,2] 处暴露:它前面挤进了 [5,0]、[7,0]、[6,1] 三个不矮于 5 的人,而 k 只要 2,对不上。错误队列 [[5,0],[7,0],[6,1],[5,2],[4,4],[7,1]] 中标红的 [5,2] 就是崩点。这才是「同高必须 k 升序」的真实理由。
- 24这题的内核是「先按一个维度排定,让后续操作对它无害」。同一招还能解:LC135 分发糖果(左右各扫一遍)、LC435 无重叠区间(按右端点排序贪心)、LC452 用最少箭引爆气球。学会「排哪个维度能让后续不破坏前面」,这一类贪心就通了——更多在 贪心专题 继续,卡住时让小欧私教陪你把这套骨架套到下一题。
⚠️ 容易写错的地方
✗ 错:按身高从低到高排
✓ 对:按身高从高到低排
矮个先放,后插的高个会被它前面的人数搅乱;只有先放高个,后插的矮个才对高个隐形。
✗ 错:同身高按 k 从大到小排
✓ 对:同身高按 k 从小到大排
同高两人会互相计入对方的 k。若 k 大者先插,会被随后 k 小者的插入挤动,同高相对次序与 k 约束不自洽;错位会一路潜伏,最终在某个更矮的人(本例 [5,2])处 k 对不上才暴露。k 小的先占靠前位置才稳。
✗ 错:把人 append 到队尾
✓ 对:insert 到下标 k
k 描述的是「在最终队列里的位置约束」,不是遍历顺序。必须按 k 当下标精确插入。
完整代码(Python / C++ / Java)
Python
class Solution:
def reconstructQueue(self, people):
# 身高降序;同高时 k 升序
people.sort(key=lambda p: (-p[0], p[1]))
ans = []
for p in people:
ans.insert(p[1], p) # k 当下标插入
return ansC++
class Solution {
public:
vector<vector<int>> reconstructQueue(vector<vector<int>>& people) {
sort(people.begin(), people.end(), [](const vector<int>& a, const vector<int>& b){
return a[0] == b[0] ? a[1] < b[1] : a[0] > b[0];
});
vector<vector<int>> ans;
for (auto& p : people)
ans.insert(ans.begin() + p[1], p);
return ans;
}
};Java
class Solution {
public int[][] reconstructQueue(int[][] people) {
Arrays.sort(people, (a, b) -> a[0] == b[0] ? a[1] - b[1] : b[0] - a[0]);
List<int[]> ans = new ArrayList<>();
for (int[] p : people)
ans.add(p[1], p); // 在下标 k 处插入
return ans.toArray(new int[people.length][]);
}
}套路模板:贪心排序题的可背骨架(挖空)
# 贪心排序通法:定排序维度 → 顺序处理 → 维护一个结构
items.sort(key=lambda x: (____, ____)) # ① 关键:先排哪个维度
result = [] # ② 累积答案的结构
for x in items:
pos = ____ # ③ 由当前元素算出落点/决策
result.insert(pos, x) # 本题:pos = x 的 k
return result// 贪心排序通法骨架
sort(items.begin(), items.end(), [](auto& a, auto& b){
return /* ① 先排哪个维度,同值时按次关键字 */;
});
vector<T> result; // ② 累积结构
for (auto& x : items) {
int pos = /* ③ 由 x 算落点 */; // 本题 pos = x[1]
result.insert(result.begin() + pos, x);
}
return result;// 贪心排序通法骨架
Arrays.sort(items, (a, b) -> /* ① 排序维度 */);
List<T> result = new ArrayList<>(); // ② 累积结构
for (T x : items) {
int pos = /* ③ 由 x 算落点 */; // 本题 pos = x[1]
result.add(pos, x);
}
// return result -> 数组复杂度
时间复杂度
O(n²)
排序 O(n log n);但 list 在中间 insert 一次最坏要移动 O(n) 个元素,n 次插入合计 O(n²),这才是主项。
空间复杂度
O(n)
ans 存重建后的整支队列;排序若原地则只占常数额外空间。
看不够?换成动画再走一遍
上面的推演每一步都对应一帧动画。点开交互动画版,能一步步看着 根据身高重建队列 的数据怎么变、指针怎么走,还能切 Python / Java / C++ 跟着练。
面试官可能追问
排序规则为什么是「身高降序、同高 k 升序」?反过来排会怎样?+
身高降序保证后插的人都不比已有的人高,矮个对高个隐形;同高 k 升序保证 k 小的先占前面的坑。若同高按 k 降序,错误不会当场暴露,而是悄悄累积——同高两人 k 大者先插会被后来 k 小者挤动,相对次序乱掉,最终在某个更矮的人(本例跑出来是 [5,2],它前面挤进 3 个不矮于 5 的人而 k 只要 2)处 k 对不上。
为什么 insert 的下标可以直接用 k,不用真的去数前面有几个高个?+
因为插入时刻,队列里已有的人全都不比当前人矮。所以把他放到下标 k,他前面就恰好有 k 个「不比他矮」的人——下标 k 等价于约束 k。
复杂度瓶颈在哪?能不能优化掉 O(n²)?+
瓶颈是 n 次中间插入,单次 O(n),合计 O(n²),比排序 O(n log n) 更高。可用树状数组 / 平衡 BST 维护「第 k 个空位」做到 O(n log n) 插入,但工程上 n≤2000 时直接 insert 已足够。
本平台为独立第三方培训机构,与华为技术有限公司无任何关联;课程的服务内容与权益以购买协议为准,学习效果因个人情况而异。「华为 OD」「华为可信」等仅为对岗位与考试方向的客观描述,相关商标归各自权利人所有。