LC406. 基于身高重建队列【贪心】

基于身高重建队列

cpp 复制代码
#include <vector>
#include <algorithm>

using namespace std;

class Solution {
public:
    vector<vector<int>> reconstructQueue(vector<vector<int>>& people) {
        // 自定义排序规则:
        // 1. 如果身高 a[0] 和 b[0] 相同,则按照 k 值(a[1] 和 b[1])升序排列
        // 2. 否则,按照身高降序排列
        sort(people.begin(), people.end(), [](const vector<int>& a, const vector<int>& b) {
            if (a[0] == b[0]) return a[1] < b[1];
            return a[0] > b[0];
        });
        
        vector<vector<int>> res;
        // 遍历排序后的数组,根据 k 值将每个人插入到 res 的第 k 个位置
        for (const vector<int>& p : people) {
            res.insert(res.begin() + p[1], p);
        }
        
        return res;
    }
};
  • 时间复杂度 : O ( N 2 ) O(N^2) O(N2)。其中 N N N 是数组 people 的长度。排序的时间复杂度是 O ( N log ⁡ N ) O(N \log N) O(NlogN)。在结果数组中进行插入操作的平均/最坏时间复杂度是 O ( N ) O(N) O(N),遍历并插入 N N N 次,所以插入的总时间复杂度是 O ( N 2 ) O(N^2) O(N2)。综合来看整体时间复杂度为 O ( N 2 ) O(N^2) O(N2)。
  • 空间复杂度 : O ( log ⁡ N ) O(\log N) O(logN) 或 O ( N ) O(N) O(N)。取决于具体语言内部排序算法的栈空间开销,以及用来存储结果的数组/列表开销。

我们可以把这道题想象成在操场上排队。每个人的要求有两个维度:身高 h前面比自己高或一样高的人数 k 。同时处理两个维度会顾此失彼,所以我们的核心思路是:先确定一个维度,再确定另一个维度

以下是基于那份 C++ 代码的详细拆解:

1. 核心破局点:"矮个子对高个子是透明的"

题目要求 k 是"前面身高大于或等于 自己的人数"。 这意味着:如果你把高个子先排好,之后无论你怎么把矮个子插队到他们前面,都不会影响高个子的 k 值计算。

基于这个破局点,我们的策略呼之欲出:

  1. 先把高个子安排好位置。
  2. 再把矮个子一个个按他们自己的 k 值插进队伍里。

2. 详细拆解第一步:自定义排序

C++

复制代码
sort(people.begin(), people.end(), [](const vector<int>& a, const vector<int>& b) {
    if (a[0] == b[0]) return a[1] < b[1]; // 规则 2:身高相同,k 小的排前面
    return a[0] > b[0];                   // 规则 1:按身高降序排(高的在前)
});

这段代码的目的是把所有的人排成一个特定的出场顺序。

  • 规则 1:按身高降序排列 (a[0] > b[0]) 让最高的人先出场。这样在后续插入时,每次处理的人都比队列里已经存在的人矮。
  • 规则 2:身高相同时,按 k 值升序排列 (a[1] < b[1]) 如果有两个人一样高(比如 [7,0][7,1]),谁先出场?显然必须是 [7,0] 先出场排在最前面,然后 [7,1] 才能排在它后面。如果反过来,[7,1] 先占了第 0 个位置,逻辑就乱了。

3. 详细拆解第二步:按索引插入

C++

复制代码
vector<vector<int>> res;
for (const vector<int>& p : people) {
    res.insert(res.begin() + p[1], p);
}

这是整个算法最"神奇"的一步。为什么直接把 p[1](也就是 k 值)当成插入的索引就可以了呢?

当我们拿出一个元素准备插入 res 时:

  1. 当前队列 res 里的所有人,身高都大于或等于当前这个人(因为我们是按身高从高到矮遍历的)。
  2. 当前这个人的要求是:"我前面要有 k 个比我高或一样高的人"。
  3. 既然队列里的人都满足"比我高或一样高",那么我只需要直接插到第 k 个位置 ,我前面就刚好有 k 个人了!
  4. 后顾之忧 :以后再插入的人会影响我吗?不会!因为以后插入的人都比我矮,就算他们插到我前面,我的 k 值(只计算大于等于我身高的人)也不会发生任何改变。

4. 动画般的情景推演

假设输入是 [[7,0], [4,4], [7,1], [5,0], [6,1], [5,2]]

排序后,大家的出场顺序变成了: [7,0], [7,1], [6,1], [5,0], [5,2], [4,4]

开始一个个排队(插入):

  1. 叫到 [7,0]:前面要 0 个人。直接站到索引 0。 队列:[[7,0]]
  2. 叫到 [7,1]:前面要 1 个人。队列里 [7,0] 符合,站到索引 1。 队列:[[7,0], [7,1]]
  3. 叫到 [6,1]:前面要 1 个人。队列里目前两人都比他高,直接站到索引 1(插队到 [7,1] 前面)。 队列:[[7,0], [6,1], [7,1]] (注意看:[6,1] 插队没有影响 [7,1],因为 6 比 7 矮,[7,1] 依然只看到前面有一个大于等于 7 的 [7,0])
  4. 叫到 [5,0]:前面要 0 个人。直接站到索引 0。 队列:[[5,0], [7,0], [6,1], [7,1]]
  5. 叫到 [5,2]:前面要 2 个人。站到索引 2。 队列:[[5,0], [7,0], [5,2], [6,1], [7,1]]
  6. 叫到 [4,4]:前面要 4 个人。站到索引 4。 队列:[[5,0], [7,0], [5,2], [6,1], [4,4], [7,1]]

这就是 C++ 代码里那个简短的 insert 能够完美运作的原因。

你对这段代码中的 Lambda 表达式写法,或者 vector 的底层插入逻辑还有什么疑问吗?可以随时提出来。

相关推荐
bIo7lyA8v1 小时前
算法稳定性分析中的输入扰动建模的技术9
算法
CoderCodingNo1 小时前
【GESP】C++三级真题 luogu-B4499, [GESP202603 三级] 二进制回文串
数据结构·c++·算法
sinat_286945191 小时前
AI Coding 时代的 TDD:从理念到工程落地
人工智能·深度学习·算法·tdd
炽烈小老头1 小时前
【 每天学习一点算法 2026/04/12】x 的平方根
学习·算法
ASKED_20191 小时前
从排序到生成:腾讯广告算法大赛 2025 baseline解读
人工智能·算法
田梓燊1 小时前
leetcode 160
算法·leetcode·职场和发展
_深海凉_2 小时前
LeetCode热题100-颜色分类
python·算法·leetcode
hetao17338372 小时前
2026-04-09~12 hetao1733837 的刷题记录
c++·算法
6Hzlia2 小时前
【Hot 100 刷题计划】 LeetCode 136. 只出现一次的数字 | C++ 哈希表&异或基础解法
c++·算法·leetcode
MWWZ3 小时前
最近的一些软件更新
opencv·算法·计算机视觉