Swift 解法详解 LeetCode 362:敲击计数器,让数据统计更高效


文章目录

摘要

在算法题中,经常会遇到"从大量组合里找到前 K 个最小值"的问题。

LeetCode 373 就是一个典型的例子:两个有序数组,要求找出和最小的 K 对数对。

看似组合爆炸(n*m 个数对),但其实可以利用最小堆(优先队列)高效求解。

本文会用 Swift 给出题解,并结合实际场景来理解这道题。

描述

我们有两个升序排列的数组 nums1nums2,再给定一个整数 k

任务是:从所有可能的数对 (u, v)u 来自 nums1v 来自 nums2)里,找到和最小的前 k 个。

举几个例子:

  • 示例 1

    txt 复制代码
    输入: nums1 = [1,7,11], nums2 = [2,4,6], k = 3
    输出: [[1,2],[1,4],[1,6]]

    我们只要前三个最小的数对,而不是把所有组合列出来。

  • 示例 2

    txt 复制代码
    输入: nums1 = [1,1,2], nums2 = [1,2,3], k = 2
    输出: [[1,1],[1,1]]

难点在于:两个数组长度可以到 10⁵,如果直接枚举所有组合,时间复杂度会是 O(n*m),完全不可行。必须用更高效的方法。

题解答案

核心思路是:

  • 两个数组是 有序的,所以小的组合一定来自前面的元素。
  • 我们可以用一个最小堆(优先队列)来维护候选的数对,每次取出最小的,扩展下一个候选。
  • 类似于"多路归并"的思路:把 (nums1[i], nums2[0]) 当作起点,逐步往右扩展。

这样就能避免生成所有组合,而是按需取出前 k 个。

题解代码分析

下面是 Swift 的解法:

swift 复制代码
import Foundation

struct Pair: Comparable {
    let sum: Int
    let i: Int
    let j: Int
    
    static func < (lhs: Pair, rhs: Pair) -> Bool {
        return lhs.sum < rhs.sum
    }
}

class Solution {
    func kSmallestPairs(_ nums1: [Int], _ nums2: [Int], _ k: Int) -> [[Int]] {
        var result = [[Int]]()
        if nums1.isEmpty || nums2.isEmpty || k == 0 {
            return result
        }
        
        // 最小堆(优先队列)
        var heap = [Pair]()
        
        // 初始时,把 nums1 的前 k 个元素和 nums2[0] 配对
        for i in 0..<min(nums1.count, k) {
            heap.append(Pair(sum: nums1[i] + nums2[0], i: i, j: 0))
        }
        heap.sort(by: <)
        
        var count = 0
        while !heap.isEmpty && count < k {
            let smallest = heap.removeFirst()
            result.append([nums1[smallest.i], nums2[smallest.j]])
            count += 1
            
            // 扩展下一个元素:同一行,右移一格
            if smallest.j + 1 < nums2.count {
                let newPair = Pair(sum: nums1[smallest.i] + nums2[smallest.j + 1],
                                   i: smallest.i,
                                   j: smallest.j + 1)
                // 插入保持堆序
                var idx = heap.binarySearchInsertIndex(of: newPair)
                heap.insert(newPair, at: idx)
            }
        }
        
        return result
    }
}

extension Array where Element: Comparable {
    // 二分查找插入位置(保持有序)
    func binarySearchInsertIndex(of element: Element) -> Int {
        var left = 0, right = self.count
        while left < right {
            let mid = (left + right) / 2
            if self[mid] < element {
                left = mid + 1
            } else {
                right = mid
            }
        }
        return left
    }
}

代码解析

  1. Pair 结构体 :存储一个数对 (nums1[i], nums2[j]),以及它们的和 sum,用来比较。
  2. 初始化堆 :只需要考虑 nums1 的前 k 个元素和 nums2[0] 的组合,因为数组是升序的,后面的一定不会比前面小。
  3. 逐步扩展 :每次从堆里拿出最小的组合 (i, j),然后把 (i, j+1) 插入堆。
  4. 二分插入:这里用二分查找保持数组有序,代替真正的优先队列。

如果项目里可以用 优先队列库,代码会更简洁。但标准 Swift 没有自带,这里用二分插入模拟。

示例测试及结果

我们来跑几个测试:

swift 复制代码
let solution = Solution()

print(solution.kSmallestPairs([1,7,11], [2,4,6], 3))
// 结果: [[1,2],[1,4],[1,6]]

print(solution.kSmallestPairs([1,1,2], [1,2,3], 2))
// 结果: [[1,1],[1,1]]

print(solution.kSmallestPairs([1,2], [3], 3))
// 结果: [[1,3],[2,3]]

运行结果:

txt 复制代码
[[1, 2], [1, 4], [1, 6]]
[[1, 1], [1, 1]]
[[1, 3], [2, 3]]

完全符合题意。

时间复杂度

  • 初始化堆:O(k log k)(前 k 个元素排序)。
  • 每次取出一个最小元素,需要 O(log k) 插入新的元素。
  • 最多执行 k 次。
  • 总复杂度 O(k log k)

相比暴力的 O(n*m),效率提升巨大。

空间复杂度

  • 堆里最多存放 k 个元素。
  • 额外空间复杂度:O(k)

总结

这道题其实就是一个典型的 "多路合并 + 最小堆" 应用。

思路上和合并 K 个有序链表、合并区间问题有点类似,都是"每次取最小,再扩展候选"。

在实际场景中,这种方法可以应用在:

  • 推荐系统:比如推荐用户最可能喜欢的前 K 个商品,候选空间很大,但我们只关心前 K 个。
  • 数据库查询优化:找两个表的"最小连接结果"。
  • 搜索排序:需要在多个有序结果中快速找到最优前 K 个。
相关推荐
TT_Close20 小时前
【Flutter×鸿蒙】FVM 不认鸿蒙 SDK?4步手动塞进去
flutter·swift·harmonyos
张江20 小时前
Swift Concurrency学习
swift
东坡肘子3 天前
OpenClaw 不错,但我好像没有那么需要 -- 肘子的 Swift 周报 #125
人工智能·swiftui·swift
郑州光合科技余经理8 天前
代码展示:PHP搭建海外版外卖系统源码解析
java·开发语言·前端·后端·系统架构·uni-app·php
feifeigo1238 天前
matlab画图工具
开发语言·matlab
dustcell.8 天前
haproxy七层代理
java·开发语言·前端
norlan_jame8 天前
C-PHY与D-PHY差异
c语言·开发语言
琢磨先生David8 天前
Day1:基础入门·两数之和(LeetCode 1)
数据结构·算法·leetcode
多恩Stone8 天前
【C++入门扫盲1】C++ 与 Python:类型、编译器/解释器与 CPU 的关系
开发语言·c++·人工智能·python·算法·3d·aigc
QQ4022054968 天前
Python+django+vue3预制菜半成品配菜平台
开发语言·python·django