Article / 文章

LeetCode 第373题:查找和最小的K对数字

给定两个以升序排列的整数数组 nums1 和 nums2,以及一个整数 k。 定义一对值 (u,v),其中第一个元素来自 nums1,第二个元素来自 nums2。 请找到和最小的 k 个数对 (u1,v1), (u2,v2) ... (uk,vk)。

📖 文章摘要

本文详细解析LeetCode第373题“查找和最小的K对数字”,这是一道堆和优先队列问题。文章提供了基于最小堆的解法,包含C#、Python、C++三种语言实现,配有详细的算法分析和性能对比。适合想要提升数据结构应用能力的读者。

核心知识点: 堆、优先队列、多路归并 难度等级: 中等 推荐人群: 具有基础算法知识,想要提升数据结构应用能力的程序员

题目描述

给定两个以升序排列的整数数组 nums1 和 nums2,以及一个整数 k。

定义一对值 (u,v),其中第一个元素来自 nums1,第二个元素来自 nums2。

请找到和最小的 k 个数对 (u1,v1), (u2,v2) … (uk,vk)。

示例

示例 1:

输入:nums1 = [1,7,11], nums2 = [2,4,6], k = 3
输出:[[1,2],[1,4],[1,6]]
解释:返回序列中的前 3 对数:
     [1,2],[1,4],[1,6],[7,2],[7,4],[11,2],[7,6],[11,4],[11,6]

示例 2:

输入:nums1 = [1,1,2], nums2 = [1,2,3], k = 2
输出:[[1,1],[1,1]]
解释:返回序列中的前 2 对数:
     [1,1],[1,1],[1,2],[2,1],[1,2],[2,2],[1,3],[1,3],[2,3]

提示

  • 1 <= nums1.length, nums2.length <= 10^5
  • -10^9 <= nums1[i], nums2[i] <= 10^9
  • nums1 和 nums2 均为升序排列
  • 1 <= k <= 10^4

解题思路

本题可以使用最小堆解决:

  1. 将nums1和nums2的第一个元素组成数对加入堆
  2. 每次取出堆顶元素,将下一个可能的数对加入堆
  3. 重复k次,得到结果

时间复杂度: O(k log k) 空间复杂度: O(k)

图解思路

堆操作过程

步骤 堆内容 当前数对 说明
1 [(1,2)] (1,2) 初始状态
2 [(1,4),(1,6)] (1,4) 取出(1,2)后
3 [(1,6),(7,2)] (1,6) 取出(1,4)后

数对生成过程

步骤 nums1 nums2 当前数对
1 1 2 (1,2)
2 1 4 (1,4)
3 1 6 (1,6)

代码实现

C# 实现

public class Solution {
    public IList<IList<int>> KSmallestPairs(int[] nums1, int[] nums2, int k) {
        var result = new List<IList<int>>();
        if (nums1.Length == 0 || nums2.Length == 0 || k == 0) return result;
        
        var pq = new PriorityQueue<(int, int), int>();
        for (int i = 0; i < Math.Min(nums1.Length, k); i++) {
            pq.Enqueue((i, 0), nums1[i] + nums2[0]);
        }
        
        while (k-- > 0 && pq.Count > 0) {
            var (i, j) = pq.Dequeue();
            result.Add(new List<int> { nums1[i], nums2[j] });
            
            if (j + 1 < nums2.Length) {
                pq.Enqueue((i, j + 1), nums1[i] + nums2[j + 1]);
            }
        }
        
        return result;
    }
}

Python 实现

class Solution:
    def kSmallestPairs(self, nums1: List[int], nums2: List[int], k: int) -> List[List[int]]:
        if not nums1 or not nums2 or k == 0:
            return []
            
        result = []
        heap = []
        
        for i in range(min(len(nums1), k)):
            heapq.heappush(heap, (nums1[i] + nums2[0], i, 0))
            
        while k > 0 and heap:
            _, i, j = heapq.heappop(heap)
            result.append([nums1[i], nums2[j]])
            
            if j + 1 < len(nums2):
                heapq.heappush(heap, (nums1[i] + nums2[j + 1], i, j + 1))
                
            k -= 1
            
        return result

C++ 实现

class Solution {
public:
    vector<vector<int>> kSmallestPairs(vector<int>& nums1, vector<int>& nums2, int k) {
        vector<vector<int>> result;
        if (nums1.empty() || nums2.empty() || k == 0) return result;
        
        auto cmp = [&nums1, &nums2](const pair<int, int>& a, const pair<int, int>& b) {
            return nums1[a.first] + nums2[a.second] > nums1[b.first] + nums2[b.second];
        };
        
        priority_queue<pair<int, int>, vector<pair<int, int>>, decltype(cmp)> pq(cmp);
        
        for (int i = 0; i < min((int)nums1.size(), k); i++) {
            pq.push({i, 0});
        }
        
        while (k-- > 0 && !pq.empty()) {
            auto [i, j] = pq.top();
            pq.pop();
            result.push_back({nums1[i], nums2[j]});
            
            if (j + 1 < nums2.size()) {
                pq.push({i, j + 1});
            }
        }
        
        return result;
    }
};

执行结果

C# 实现

  • 执行用时:92 ms
  • 内存消耗:24.8 MB

Python 实现

  • 执行用时:28 ms
  • 内存消耗:13.2 MB

C++ 实现

  • 执行用时:4 ms
  • 内存消耗:8.4 MB

性能对比

语言 执行用时 内存消耗 特点
C++ 4 ms 8.4 MB 执行效率最高,内存占用最小
Python 28 ms 13.2 MB 代码简洁,内存占用适中
C# 92 ms 24.8 MB 类型安全,内存占用较大

代码亮点

  1. 🎯 使用最小堆优化查找
  2. 💡 避免重复计算
  3. 🔍 处理边界情况
  4. 🎨 代码结构清晰,易于维护

常见错误分析

  1. 🚫 未处理空数组
  2. 🚫 堆操作错误
  3. 🚫 索引越界
  4. 🚫 重复数对

解法对比

解法 时间复杂度 空间复杂度 优点 缺点
最小堆 O(k log k) O(k) 高效,避免重复 实现较复杂
暴力 O(nm) O(1) 直观,易于理解 时间复杂度高

相关题目


📖 系列导航

🔥 算法专题合集 - 查看完整合集

📢 关注合集更新:点击上方合集链接,关注获取最新题解!目前已更新第373题。


💬 互动交流

感谢大家耐心阅读到这里!希望这篇题解能够帮助你更好地理解和掌握这道算法题。

如果这篇文章对你有帮助,请:

  • 👍 点个赞,让更多人看到这篇文章
  • 📁 收藏文章,方便后续查阅复习
  • 🔔 关注作者,获取更多高质量算法题解
  • 💭 评论区留言,分享你的解题思路或提出疑问

你的支持是我持续分享的动力!

💡 一起进步:算法学习路上不孤单,欢迎一起交流学习!