Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit dd48c2b

Browse files
Merge pull request SharingSource#309 from SharingSource/ac_oier
✨feat: Add 373
2 parents ff512be + ceeb4f0 commit dd48c2b

File tree

4 files changed

+265
-0
lines changed

4 files changed

+265
-0
lines changed

‎Index/二分.md‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
| [354. 俄罗斯套娃信封问题](https://leetcode-cn.com/problems/russian-doll-envelopes/) | [LeetCode 题解链接](https://leetcode-cn.com/problems/russian-doll-envelopes/solution/zui-chang-shang-sheng-zi-xu-lie-bian-xin-6s8d/) | 困难 | 🤩🤩🤩 |
2121
| [363. 矩形区域不超过 K 的最大数值和](https://leetcode-cn.com/problems/max-sum-of-rectangle-no-larger-than-k/) | [LeetCode 题解链接](https://leetcode-cn.com/problems/max-sum-of-rectangle-no-larger-than-k/solution/gong-shui-san-xie-you-hua-mei-ju-de-ji-b-dh8s/) | 困难 | 🤩🤩🤩 |
2222
| [367. 有效的完全平方数](https://leetcode-cn.com/problems/valid-perfect-square/) | [LeetCode 题解链接](https://leetcode-cn.com/problems/valid-perfect-square/solution/gong-shui-san-xie-yi-ti-shuang-jie-er-fe-g5el/) | 简单 | 🤩🤩🤩🤩🤩 |
23+
| [373. 查找和最小的K对数字](https://leetcode-cn.com/problems/find-k-pairs-with-smallest-sums/) | [LeetCode 题解链接](https://leetcode-cn.com/problems/find-k-pairs-with-smallest-sums/solution/gong-shui-san-xie-duo-lu-gui-bing-yun-yo-pgw5/) | 中等 | 🤩🤩🤩🤩🤩 |
2324
| [374. 猜数字大小](https://leetcode-cn.com/problems/guess-number-higher-or-lower/) | [LeetCode 题解链接](https://leetcode-cn.com/problems/guess-number-higher-or-lower/solution/gong-shui-san-xie-shi-yong-jiao-hu-han-s-tocm/) | 简单 | 🤩🤩🤩 |
2425
| [441. 排列硬币](https://leetcode-cn.com/problems/arranging-coins/) | [LeetCode 题解链接](https://leetcode-cn.com/problems/arranging-coins/solution/gong-shui-san-xie-yi-ti-shuang-jie-shu-x-sv9o/) | 简单 | 🤩🤩🤩 |
2526
| [475. 供暖器](https://leetcode-cn.com/problems/heaters/) | [LeetCode 题解链接](https://leetcode-cn.com/problems/heaters/solution/gong-shui-san-xie-er-fen-shuang-zhi-zhen-mys4/) | 中等 | 🤩🤩🤩🤩 |

‎Index/堆.md‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
| [264. 丑数 II](https://leetcode-cn.com/problems/ugly-number-ii/) | [LeetCode 题解链接](https://leetcode-cn.com/problems/ugly-number-ii/solution/gong-shui-san-xie-yi-ti-shuang-jie-you-x-3nvs/) | 中等 | 🤩🤩🤩 |
66
| [295. 数据流的中位数](https://leetcode-cn.com/problems/find-median-from-data-stream/) | [LeetCode 题解链接](https://leetcode-cn.com/problems/find-median-from-data-stream/solution/gong-shui-san-xie-jing-dian-shu-ju-jie-g-pqy8/) | 中等 | 🤩🤩🤩🤩 |
77
| [313. 超级丑数](https://leetcode-cn.com/problems/super-ugly-number/) | [LeetCode 题解链接](https://leetcode-cn.com/problems/super-ugly-number/solution/gong-shui-san-xie-yi-ti-shuang-jie-you-x-jyow/) | 中等 | 🤩🤩🤩 |
8+
| [373. 查找和最小的K对数字](https://leetcode-cn.com/problems/find-k-pairs-with-smallest-sums/) | [LeetCode 题解链接](https://leetcode-cn.com/problems/find-k-pairs-with-smallest-sums/solution/gong-shui-san-xie-duo-lu-gui-bing-yun-yo-pgw5/) | 中等 | 🤩🤩🤩🤩🤩 |
89
| [407. 接雨水 II](https://leetcode-cn.com/problems/trapping-rain-water-ii/) | [LeetCode 题解链接](https://leetcode-cn.com/problems/trapping-rain-water-ii/solution/gong-shui-san-xie-jing-dian-dijkstra-yun-13ik/) | 困难 | 🤩🤩🤩🤩 |
910
| [451. 根据字符出现频率排序](https://leetcode-cn.com/problems/sort-characters-by-frequency/) | [LeetCode 题解链接](https://leetcode-cn.com/problems/sort-characters-by-frequency/solution/gong-shui-san-xie-shu-ju-jie-gou-yun-yon-gst9/) | 中等 | 🤩🤩🤩🤩 |
1011
| [480. 滑动窗口中位数](https://leetcode-cn.com/problems/sliding-window-median/) | [LeetCode 题解链接](https://leetcode-cn.com/problems/sliding-window-median/solution/xiang-jie-po-su-jie-fa-you-xian-dui-lie-mo397/) | 困难 | 🤩🤩🤩🤩 |

‎Index/多路归并.md‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,6 @@
33
| [21. 合并两个有序链表](https://leetcode-cn.com/problems/merge-two-sorted-lists/) | [LeetCode 题解链接](https://leetcode-cn.com/problems/merge-two-sorted-lists/solution/shua-chuan-lc-shuang-zhi-zhen-jie-fa-sha-b22z/) | 简单 | 🤩🤩🤩🤩🤩 |
44
| [264. 丑数 II](https://leetcode-cn.com/problems/ugly-number-ii/) | [LeetCode 题解链接](https://leetcode-cn.com/problems/ugly-number-ii/solution/gong-shui-san-xie-yi-ti-shuang-jie-you-x-3nvs/) | 中等 | 🤩🤩🤩🤩 |
55
| [313. 超级丑数](https://leetcode-cn.com/problems/super-ugly-number/) | [LeetCode 题解链接](https://leetcode-cn.com/problems/super-ugly-number/solution/gong-shui-san-xie-yi-ti-shuang-jie-you-x-jyow/) | 中等 | 🤩🤩🤩 |
6+
| [373. 查找和最小的K对数字](https://leetcode-cn.com/problems/find-k-pairs-with-smallest-sums/) | [LeetCode 题解链接](https://leetcode-cn.com/problems/find-k-pairs-with-smallest-sums/solution/gong-shui-san-xie-duo-lu-gui-bing-yun-yo-pgw5/) | 中等 | 🤩🤩🤩🤩🤩 |
67
| [786. 第 K 个最小的素数分数](https://leetcode-cn.com/problems/k-th-smallest-prime-fraction/) | [LeetCode 题解链接](https://leetcode-cn.com/problems/k-th-smallest-prime-fraction/solution/gong-shui-san-xie-yi-ti-shuang-jie-you-x-8ymk/) | 中等 | 🤩🤩🤩 |
78

Lines changed: 262 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,262 @@
1+
### 题目描述
2+
3+
这是 LeetCode 上的 **[373. 查找和最小的K对数字](https://leetcode-cn.com/problems/find-k-pairs-with-smallest-sums/solution/gong-shui-san-xie-duo-lu-gui-bing-yun-yo-pgw5/)** ,难度为 **中等**
4+
5+
Tag : 「优先队列」、「二分」、「多路归并」
6+
7+
8+
9+
给定两个以升序排列的整数数组 `nums1``nums2` , 以及一个整数 `k`
10+
11+
定义一对值 $(u,v),ドル其中第一个元素来自 `nums1`,第二个元素来自 `nums2`
12+
13+
请找到和最小的 `k` 个数对 $(u_1,v_1), (u_2,v_2) ... (u_k,v_k)$ 。
14+
15+
示例 1:
16+
```
17+
输入: nums1 = [1,7,11], nums2 = [2,4,6], k = 3
18+
19+
输出: [1,2],[1,4],[1,6]
20+
21+
解释: 返回序列中的前 3 对数:
22+
[1,2],[1,4],[1,6],[7,2],[7,4],[11,2],[7,6],[11,4],[11,6]
23+
```
24+
示例 2:
25+
```
26+
输入: nums1 = [1,1,2], nums2 = [1,2,3], k = 2
27+
28+
输出: [1,1],[1,1]
29+
30+
解释: 返回序列中的前 2 对数:
31+
[1,1],[1,1],[1,2],[2,1],[1,2],[2,2],[1,3],[1,3],[2,3]
32+
```
33+
示例 3:
34+
```
35+
输入: nums1 = [1,2], nums2 = [3], k = 3
36+
37+
输出: [1,3],[2,3]
38+
39+
解释: 也可能序列中所有的数对都被返回:[1,3],[2,3]
40+
```
41+
42+
提示:
43+
* 1ドル <= nums1.length, nums2.length <= 10^4$
44+
* $-10^9 <= nums1[i], nums2[i] <= 10^9$
45+
* $nums1, nums2 均为升序排列$
46+
* 1ドル <= k <= 1000$
47+
48+
---
49+
50+
### 基本分析
51+
52+
这道题和 [(题解) 786. 第 K 个最小的素数分数](https://leetcode-cn.com/problems/k-th-smallest-prime-fraction/solution/gong-shui-san-xie-yi-ti-shuang-jie-you-x-8ymk/) 几乎是一模一样,先做哪一道都是一样的,难度上没有区别 🤣
53+
54+
最常规的做法是使用「多路归并」,还不熟悉「多路归并」的同学,建议先学习前置🧀:[多路归并入门](https://mp.weixin.qq.com/s?__biz=MzU4NDE3MTEyMA==&mid=2247490029&idx=1&sn=bba9ddff88d247db310406ee418d5a15&chksm=fd9cb2f2caeb3be4b1f84962677337dcb5884374e5b6b80340834eaff79298d11151da2dd5f7&token=252055586&lang=zh_CN#rd),里面讲述了如何从「朴素优先队列」往「多路归并」进行转换。
55+
56+
---
57+
58+
### 多路归并
59+
60+
令 $nums1$ 的长度为 $n,ドル$nums2$ 的长度为 $m,ドル所有的点对数量为 $n * m$。
61+
62+
其中每个 $nums1[i]$ 参与所组成的点序列为:
63+
64+
$$
65+
[(nums1[0], nums2[0]), (nums1[0], nums2[1]), ..., (nums1[0], nums2[m - 1])]\\
66+
[(nums1[1], nums2[0]), (nums1[1], nums2[1]), ..., (nums1[1], nums2[m - 1])]\\
67+
...\\
68+
[(nums1[n - 1], nums2[0]), (nums1[n - 1], nums2[1]), ..., (nums1[n - 1], nums2[m - 1])]\\
69+
$$
70+
71+
由于 $nums1$ 和 $nums2$ 均已按升序排序,因此每个 $nums1[i]$ 参与构成的点序列也为升序排序,这引导我们使用「多路归并」来进行求解。
72+
73+
具体的,起始我们将这 $n$ 个序列的首位元素(点对)以二元组 $(i, j)$ 放入优先队列(小根堆),其中 $i$ 为该点对中 $nums1[i]$ 的下标,$j$ 为该点对中 $nums2[j]$ 的下标,这步操作的复杂度为 $O(n\log{n})$。这里也可以得出一个小优化是:我们始终确保 $nums1$ 为两数组中长度较少的那个,然后通过标识位来记录是否发生过交换,确保答案的点顺序的正确性。
74+
75+
每次从优先队列(堆)中取出堆顶元素(含义为当前未被加入到答案的所有点对中的最小值),加入答案,并将该点对所在序列的下一位(如果有)加入优先队列中。
76+
77+
举个 🌰,首次取出的二元组为 $(0, 0),ドル即点对 $(nums1[0], nums2[0]),ドル取完后将序列的下一位点对 $(nums1[0], nums2[1])$ 以二元组 $(0, 1)$ 形式放入优先队列。
78+
79+
可通过「反证法」证明,每次这样的「取当前,放入下一位」的操作,可以确保当前未被加入答案的所有点对的最小值必然在优先队列(堆)中,即前 $k$ 个出堆的元素必然是所有点对的前 $k$ 小的值。
80+
81+
**代码(感谢 [@Benhao](/u/himymben/) 同学提供的其他语言版本):**
82+
```Java
83+
class Solution {
84+
boolean flag = true;
85+
public List<List<Integer>> kSmallestPairs(int[] nums1, int[] nums2, int k) {
86+
List<List<Integer>> ans = new ArrayList<>();
87+
int n = nums1.length, m = nums2.length;
88+
if (n > m && !(flag = false)) return kSmallestPairs(nums2, nums1, k);
89+
PriorityQueue<int[]> q = new PriorityQueue<>((a,b)->(nums1[a[0]]+nums2[a[1]])-(nums1[b[0]]+nums2[b[1]]));
90+
for (int i = 0; i < Math.min(n, k); i++) q.add(new int[]{i, 0});
91+
while (ans.size() < k && !q.isEmpty()) {
92+
int[] poll = q.poll();
93+
int a = poll[0], b = poll[1];
94+
ans.add(new ArrayList<>(){{
95+
add(flag ? nums1[a] : nums2[b]);
96+
add(flag ? nums2[b] : nums1[a]);
97+
}});
98+
if (b + 1 < m) q.add(new int[]{a, b + 1});
99+
}
100+
return ans;
101+
}
102+
}
103+
```
104+
-
105+
```Python3
106+
class Solution:
107+
def kSmallestPairs(self, nums1: List[int], nums2: List[int], k: int) -> List[List[int]]:
108+
flag, ans = (n := len(nums1)) > (m := len(nums2)), []
109+
if flag:
110+
n, m, nums1, nums2 = m, n, nums2, nums1
111+
pq = []
112+
for i in range(min(n, k)):
113+
heapq.heappush(pq, (nums1[i] + nums2[0], i, 0))
114+
while len(ans) < k and pq:
115+
_, a, b = heapq.heappop(pq)
116+
ans.append([nums2[b], nums1[a]] if flag else [nums1[a], nums2[b]])
117+
if b + 1 < m:
118+
heapq.heappush(pq, (nums1[a] + nums2[b + 1], a, b + 1))
119+
return ans
120+
```
121+
-
122+
```Golang
123+
func kSmallestPairs(nums1 []int, nums2 []int, k int) [][]int {
124+
n, m, ans := len(nums1), len(nums2), [][]int{}
125+
flag := n > m
126+
if flag {
127+
n, m, nums1, nums2 = m, n, nums2, nums1
128+
}
129+
if n > k {
130+
n = k
131+
}
132+
pq := make(hp, n)
133+
for i := 0; i < n; i++ {
134+
pq[i] = []int{nums1[i] + nums2[0], i, 0}
135+
}
136+
heap.Init(&pq)
137+
for pq.Len() > 0 && len(ans) < k {
138+
poll := heap.Pop(&pq).([]int)
139+
a, b := poll[1], poll[2]
140+
if flag{
141+
ans = append(ans, []int{nums2[b], nums1[a]})
142+
}else{
143+
ans = append(ans, []int{nums1[a], nums2[b]})
144+
}
145+
if b < m - 1 {
146+
heap.Push(&pq, []int{nums1[a] + nums2[b + 1], a, b + 1})
147+
}
148+
}
149+
return ans
150+
}
151+
// 最小堆模板
152+
type hp [][]int
153+
func (h hp) Len() int { return len(h) }
154+
func (h hp) Less(i, j int) bool { return h[i][0] < h[j][0] }
155+
func (h hp) Swap(i, j int) { h[i], h[j] = h[j], h[i] }
156+
func (h *hp) Push(v interface{}) { *h = append(*h, v.([]int)) }
157+
func (h *hp) Pop() interface{} { a := *h; v := a[len(a)-1]; *h = a[:len(a)-1]; return v }
158+
```
159+
* 时间复杂度:令 $M$ 为 $n$、$m$ 和 $k$ 三者中的最小值,复杂度为 $O(M + k) * \log{M})$
160+
* 空间复杂度:$O(M)$
161+
162+
---
163+
164+
### 二分
165+
166+
我们还能够使用多次「二分」来做。
167+
168+
假设我们将所有「数对和」按照升序排序,两端的值分别为 $l = nums1[0] + nums2[0]$ 和 $r = nums1[n - 1] + nums2[m - 1]$。
169+
170+
因此我们可以在值域 $[l, r]$ 上进行二分,找到第一个满足「点对和小于等于 $x$ 的,且数量超过 $k$ 的值 $x$」。
171+
172+
之所以能够二分,是因为 $x$ 所在的点对和数轴上具有二段性:
173+
174+
* 点对和小于 $x$ 的点对数量少于 $k$ 个;
175+
* 点对和大于等于 $x$ 的点对数量大于等于 $k$ 个。
176+
177+
判定小于等于 $x$ 的点对数量是否大于等于 $k$ 个这一步可直接使用循环来做,由于二分是从中间值开始,这一步不会出现跑满两层循环的情况。
178+
179+
当二分出第 $k$ 小的值为 $x$ 后,由于存在不同点对的点对和值相等,我们需要先将所有点对和小于等于 $x$ 的值加入答案,然后酌情把值等于 $x$ 的点对加入答案,知道满足答案数量为 $k$。
180+
181+
找值为 $x$ 的所有点对这一步,可以通过枚举 $nums1[i],ドル然后在 $nums2$ 上二分目标值 $x - nums1[i]$ 的左右端点来做。
182+
183+
最后,在所有处理过程中,我们都可以利用答案数组的大小与 $k$ 的关系做剪枝。
184+
185+
代码:
186+
```Java
187+
class Solution {
188+
int[] nums1, nums2;
189+
int n, m;
190+
public List<List<Integer>> kSmallestPairs(int[] n1, int[] n2, int k) {
191+
nums1 = n1; nums2 = n2;
192+
n = nums1.length; m = nums2.length;
193+
List<List<Integer>> ans = new ArrayList<>();
194+
int l = nums1[0] + nums2[0], r = nums1[n - 1] + nums2[m - 1];
195+
while (l < r) {
196+
int mid = l + r >> 1;
197+
if (check(mid, k)) r = mid;
198+
else l = mid + 1;
199+
}
200+
int x = r;
201+
for (int i = 0; i < n; i++) {
202+
for (int j = 0; j < m; j++) {
203+
if (nums1[i] + nums2[j] < x) {
204+
List<Integer> temp = new ArrayList<>();
205+
temp.add(nums1[i]); temp.add(nums2[j]);
206+
ans.add(temp);
207+
} else break;
208+
}
209+
}
210+
for (int i = 0; i < n && ans.size() < k; i++) {
211+
int a = nums1[i], b = x - a;
212+
int c = -1, d = -1;
213+
l = 0; r = m - 1;
214+
while (l < r) {
215+
int mid = l + r >> 1;
216+
if (nums2[mid] >= b) r = mid;
217+
else l = mid + 1;
218+
}
219+
if (nums2[r] != b) continue;
220+
c = r;
221+
l = 0; r = m - 1;
222+
while (l < r) {
223+
int mid = l + r + 1 >> 1;
224+
if (nums2[mid] <= b) l = mid;
225+
else r = mid - 1;
226+
}
227+
d = r;
228+
for (int p = c; p <= d && ans.size() < k; p++) {
229+
List<Integer> temp = new ArrayList<>();
230+
temp.add(a); temp.add(b);
231+
ans.add(temp);
232+
}
233+
}
234+
return ans;
235+
}
236+
boolean check(int x, int k) {
237+
int ans = 0;
238+
for (int i = 0; i < n && ans < k; i++) {
239+
for (int j = 0; j < m && ans < k; j++) {
240+
if (nums1[i] + nums2[j] <= x) ans++;
241+
else break;
242+
}
243+
}
244+
return ans >= k;
245+
}
246+
}
247+
```
248+
* 时间复杂度:假设点对和的值域大小范围为 $M,ドル第一次二分的复杂度为 $O((n * m) * \log{M})$;统计点对和值小于目标值 $x$ 的复杂度为 $O(n * m)$;统计所有点对和等于目标值的复杂度为 $O(\max(n * \log{m}, k))$(整个处理过程中利用了大小关系做了剪枝,大多循环都不会跑满,实际计算量会比理论分析的要低)
249+
* 空间复杂度:$O(k)$
250+
251+
---
252+
253+
### 最后
254+
255+
这是我们「刷穿 LeetCode」系列文章的第 `No.373` 篇,系列开始于 2021年01月01日,截止于起始日 LeetCode 上共有 1916 道题目,部分是有锁题,我们将先把所有不带锁的题目刷完。
256+
257+
在这个系列文章里面,除了讲解解题思路以外,还会尽可能给出最为简洁的代码。如果涉及通解还会相应的代码模板。
258+
259+
为了方便各位同学能够电脑上进行调试和提交代码,我建立了相关的仓库:https://github.com/SharingSource/LogicStack-LeetCode
260+
261+
在仓库地址里,你可以看到系列文章的题解链接、系列文章的相应代码、LeetCode 原题链接和其他优选题解。
262+

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /