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

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

相关推荐
大胆飞猪1 小时前
递归、剪枝、回溯算法---全排列、子集问题(力扣.46,78)
算法·leetcode·剪枝
Kisorge3 小时前
【电机控制】基于STM32F103C8T6的二轮平衡车设计——LQR线性二次线控制器(算法篇)
stm32·嵌入式硬件·算法
铭哥的编程日记4 小时前
深入浅出蓝桥杯:算法基础概念与实战应用(二)基础算法(下)
算法·职场和发展·蓝桥杯
Swift社区4 小时前
LeetCode 421 - 数组中两个数的最大异或值
算法·leetcode·职场和发展
cici158744 小时前
基于高光谱成像和偏最小二乘法(PLS)的苹果糖度检测MATLAB实现
算法·matlab·最小二乘法
StarPrayers.5 小时前
自蒸馏学习方法
人工智能·算法·学习方法
大锦终5 小时前
【动规】背包问题
c++·算法·动态规划
智者知已应修善业6 小时前
【c语言蓝桥杯计算卡片题】2023-2-12
c语言·c++·经验分享·笔记·算法·蓝桥杯
hansang_IR6 小时前
【题解】洛谷 P2330 [SCOI2005] 繁忙的都市 [生成树]
c++·算法·最小生成树
Croa-vo7 小时前
PayPal OA 全流程复盘|题型体验 + 成绩反馈 + 通关经验
数据结构·经验分享·算法·面试·职场和发展