funckSmallestPairs(nums1, nums2 []int, k int) (ans [][]int) { m, n := len(nums1), len(nums2) h := hp{nil, nums1, nums2} for i := 0; i < k && i < m; i++ { h.data = append(h.data, pair{i, 0}) } for h.Len() > 0 && len(ans) < k { p := heap.Pop(&h).(pair) i, j := p.i, p.j ans = append(ans, []int{nums1[i], nums2[j]}) if j+1 < n { heap.Push(&h, pair{i, j + 1}) } } return }
type pair struct{ i, j int } type hp struct { data []pair nums1, nums2 []int } func(h hp) Len() int { returnlen(h.data) } func(h hp) Less(i, j int) bool { a, b := h.data[i], h.data[j]; return h.nums1[a.i]+h.nums2[a.j] < h.nums1[b.i]+h.nums2[b.j] } func(h hp) Swap(i, j int) { h.data[i], h.data[j] = h.data[j], h.data[i] } func(h *hp) Push(v interface{}) { h.data = append(h.data, v.(pair)) } func(h *hp) Pop() interface{} { a := h.data; v := a[len(a)-1]; h.data = a[:len(a)-1]; return v }
复杂度分析
时间复杂度:O(k \log k),其中 k 是选择的数对的数目。优先队列中最多只保存 k 个元素,每次压入新的元素队列进行调整的时间复杂度为 \log k,入队操作一共有 2k 次, 一共需要从队列中弹出 k 个数据。
空间复杂度:O(k)。优先队列中最多只保存 k 个元素。
方法二:二分查找
思路
我们利用二分查找找到第 k 小的数对和为 pairSum。利用滑动窗口即可计算出两个数组中满足数对和小于等于目标值 target 的数对有多少个,我们找到最小的 target 且满足小于等于它的数对数目刚好大于等于 k 即为目标值 pairSum,然后在数组中找到最小的 k 个数对满足数对和小于等于 pairSum。
classSolution { public: vector<vector<int>> kSmallestPairs(vector<int>& nums1, vector<int>& nums2, int k) { int m = nums1.size(); int n = nums2.size(); auto count = [&](int target){ longlong cnt = 0; int start = 0; int end = n - 1; while (start < m && end >= 0) { if (nums1[start] + nums2[end] > target) { end--; } else { cnt += end + 1; start++; } } return cnt; };
/*二分查找第 k 小的数对和的大小*/ int left = nums1[0] + nums2[0]; int right = nums1.back() + nums2.back(); int pairSum = right; while (left <= right) { int mid = left + ((right - left) >> 1); if (count(mid) < k) { left = mid + 1; } else { pairSum = mid; right = mid - 1; } }
vector<vector<int>> ans; int pos = n - 1; /*找到小于目标值 pairSum 的数对*/ for (int i = 0; i < m; i++) { while (pos >= 0 && nums1[i] + nums2[pos] >= pairSum) { pos--; } for (int j = 0; j <= pos && k > 0; j++, k--) { ans.push_back({nums1[i], nums2[j]}); } } /*找到等于目标值 pairSum 的数对*/ pos = n - 1; for (int i = 0; i < m && k > 0; i++) { int start1 = i; while (i < m - 1 && nums1[i] == nums1[i + 1]) { i++; } while (pos >= 0 && nums1[i] + nums2[pos] > pairSum) { pos--; } int start2 = pos; while (pos > 0 && nums2[pos] == nums2[pos - 1]) { pos--; } if (nums1[i] + nums2[pos] != pairSum) { continue; } int count = (int) min((long) k, (long) (i - start1 + 1) * (start2 - pos + 1)); for (int j = 0; j < count && k > 0; j++, k--) { ans.push_back({nums1[i], nums2[pos]}); } } return ans; }
publicclassSolution { public IList<IList<int>> KSmallestPairs(int[] nums1, int[] nums2, int k) { int m = nums1.Length; int n = nums2.Length;
/*二分查找第 k 小的数对和的大小*/ int left = nums1[0] + nums2[0]; int right = nums1[m - 1] + nums2[n - 1]; int pairSum = right; while (left <= right) { int mid = left + ((right - left) >> 1); long cnt = 0; int start = 0; int end = n - 1; while (start < nums1.Length && end >= 0) { if (nums1[start] + nums2[end] > mid) { end--; } else { cnt += end + 1; start++; } } if (cnt < k) { left = mid + 1; } else { pairSum = mid; right = mid - 1; } }
IList<IList<int>> ans = new List<IList<int>>(); int pos = n - 1; /*找到小于目标值 pairSum 的数对*/ for (int i = 0; i < m; i++) { while (pos >= 0 && nums1[i] + nums2[pos] >= pairSum) { pos--; } for (int j = 0; j <= pos && k > 0; j++, k--) { IList<int> list = new List<int>(); list.Add(nums1[i]); list.Add(nums2[j]); ans.Add(list); } }
/*找到等于目标值 pairSum 的数对*/ pos = n - 1; for (int i = 0; i < m && k > 0; i++) { int start1 = i; while (i < m - 1 && nums1[i] == nums1[i + 1]) { i++; } while (pos >= 0 && nums1[i] + nums2[pos] > pairSum) { pos--; } int start2 = pos; while (pos > 0 && nums2[pos] == nums2[pos - 1]) { pos--; } if (nums1[i] + nums2[pos] != pairSum) { continue; } int count = (int) Math.Min(k, (long) (i - start1 + 1) * (start2 - pos + 1)); for (int j = 0; j < count && k > 0; j++, k--) { IList<int> list = new List<int>(); list.Add(nums1[i]); list.Add(nums2[pos]); ans.Add(list); } } return ans; } }
classSolution: defkSmallestPairs(self, nums1: List[int], nums2: List[int], k: int) -> List[List[int]]: m, n = len(nums1), len(nums2)
# 二分查找第 k 小的数对和 left, right = nums1[0] + nums2[0], nums1[m - 1] + nums2[n - 1] + 1 while left < right: mid = (left + right) // 2 cnt = 0 i, j = 0, n - 1 while i < m and j >= 0: if nums1[i] + nums2[j] > mid: j -= 1 else: cnt += j + 1 i += 1 if cnt < k: left = mid + 1 else: right = mid pairSum = left
ans = [] # 找数对和小于 pairSum 的数对 i = n - 1 for num1 in nums1: while i >= 0and num1 + nums2[i] >= pairSum: i -= 1 for j inrange(i + 1): ans.append([num1, nums2[j]]) iflen(ans) == k: return ans
# 找数对和等于 pairSum 的数对 i = n - 1 for num1 in nums1: while i >= 0and num1 + nums2[i] > pairSum: i -= 1 j = i while j >= 0and num1 + nums2[j] == pairSum: ans.append([num1, nums2[j]]) iflen(ans) == k: return ans j -= 1 return ans
funckSmallestPairs(nums1, nums2 []int, k int) (ans [][]int) { m, n := len(nums1), len(nums2)
// 二分查找第 k 小的数对和 left, right := nums1[0]+nums2[0], nums1[m-1]+nums2[n-1]+1 pairSum := left + sort.Search(right-left, func(sum int)bool { sum += left cnt := 0 i, j := 0, n-1 for i < m && j >= 0 { if nums1[i]+nums2[j] > sum { j-- } else { cnt += j + 1 i++ } } return cnt >= k })
// 找数对和小于 pairSum 的数对 i := n - 1 for _, num1 := range nums1 { for i >= 0 && num1+nums2[i] >= pairSum { i-- } for _, num2 := range nums2[:i+1] { ans = append(ans, []int{num1, num2}) iflen(ans) == k { return } } }
// 找数对和等于 pairSum 的数对 i = n - 1 for _, num1 := range nums1 { for i >= 0 && num1+nums2[i] > pairSum { i-- } for j := i; j >= 0 && num1+nums2[j] == pairSum; j-- { ans = append(ans, []int{num1, nums2[j]}) iflen(ans) == k { return } } } return }
var kSmallestPairs = function(nums1, nums2, k) { m = nums1.length n = nums2.length /*二分查找第 k 小的数对和的大小*/ let left = nums1[0] + nums2[0]; let right = nums1[m - 1] + nums2[n - 1]; let pairSum = right; while (left <= right) { const mid = left + ((right - left) >> 1); let cnt = 0; let start = 0; let end = n - 1; while (start < m && end >= 0) { if (nums1[start] + nums2[end] > mid) { end--; } else { cnt += end + 1; start++; } } if (cnt < k) { left = mid + 1; } else { pairSum = mid; right = mid - 1; } }
const ans = []; let pos = n - 1; /*找到小于目标值 pairSum 的数对*/ for (let i = 0; i < m; i++) { while (pos >= 0 && nums1[i] + nums2[pos] >= pairSum) { pos--; } for (let j = 0; j <= pos && k > 0; j++, k--) { const list = []; list.push(nums1[i]); list.push(nums2[j]); ans.push(list); } }
/*找到等于目标值 pairSum 的数对*/ pos = n - 1; for (let i = 0; i < m && k > 0; i++) { let start1 = i; while (i < m - 1 && nums1[i] == nums1[i + 1]) { i++; } while (pos >= 0 && nums1[i] + nums2[pos] > pairSum) { pos--; } let start2 = pos; while (pos > 0 && nums2[pos] == nums2[pos - 1]) { pos--; } if (nums1[i] + nums2[pos] != pairSum) { continue; } let count = Math.min(k, (i - start1 + 1) * (start2 - pos + 1)); for (let j = 0; j < count && k > 0; j++, k--) { const list = []; list.push(nums1[i]); list.push(nums2[pos]); ans.push(list); } } return ans; }