LeetCode 406 - 根据身高重建队列


文章目录

摘要

这道题看起来像是"排队问题",但实际上是一道非常典型的**贪心算法(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 个比他高的人。

贪心策略是:

  1. 先让高个子的人排好队(他们不受矮个子影响);
  2. 再把矮个子插入到他们的正确位置中。

核心思路:

  1. 排序规则:

    • 先按身高 h 降序排序(高个子优先);
    • 当身高相同时,按 k 升序排序(前面人少的先排)。

    这样做的好处是:我们可以优先确定"高个子"的相对顺序,因为他们不会被矮个子挡住。

  2. 插入队列:

    • 按照排序好的顺序,一个一个把人插入到结果数组中;
    • 每次都插入到索引位置 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))

代码逐步解析

  1. 排序逻辑:

    swift 复制代码
    let 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]]

    也就是说先排高个子,再排低个子。

  2. 插入逻辑:

    swift 复制代码
    var 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 的人。

  3. 过程理解:

    • 先放 [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 布局系统中优先放置固定位置元素;
  • 排序系统中优先处理高优先级任务;
  • 渐进式布局算法中优先处理大块区域。

所以,不仅仅是算法题,它也能训练我们在系统设计中"找主次关系"的思维。

相关推荐
敲上瘾3 小时前
背包dp——动态规划
c++·算法·动态规划
oe10193 小时前
CNCF Kepler与MCP:开启云原生绿色计算的人机协作新纪元
1024程序员节
野犬寒鸦3 小时前
从零起步学习MySQL || 第九章:从数据页的角度看B+树及MySQL中数据的底层存储原理(结合常见面试题深度解析)
java·服务器·数据库·后端·mysql·oracle·1024程序员节
热爱运维的小七3 小时前
从传统架构到云原生,如何应对数据增长挑战?
数据库·it运维·devops·1024程序员节
huangyuchi.3 小时前
【Linux网络】套接字Socket编程预备
linux·运维·服务器·端口号·linux网络·套接字·1024程序员节
稍带温度的风3 小时前
node 后端服务 PM2 相关命令
node.js·pm2·1024程序员节
More more3 小时前
Element Plus 表格table中,el-image实现图片预览无层级显示问题
1024程序员节
Brandon汐3 小时前
MySQL的安装与卸载
1024程序员节
胡耀超4 小时前
5、服务器互连技术(小白入门版)
服务器·网络·ai·网络拓扑·gpu·pcie·1024程序员节