240. 搜索二维矩阵 II
编写一个高效的算法来搜索 _m_ x _n_ 矩阵 matrix 中的一个目标值 target 。该矩阵具有以下特性:
- 每行的元素从左到右升序排列。
每列的元素从上到下升序排列。
示例 1:
输入:matrix = [[1,4,7,11,15],[2,5,8,12,19],[3,6,9,16,22],[10,13,14,17,24],[18,21,23,26,30]], target = 5
输出:true
示例 2:
输入:matrix = [[1,4,7,11,15],[2,5,8,12,19],[3,6,9,16,22],[10,13,14,17,24],[18,21,23,26,30]], target = 20
输出:false
提示:
m == matrix.lengthn == matrix[i].length1 <= n, m <= 300-10 <= matrix[i][j] <= 10- 每行的所有元素从左到右升序排列
- 每列的所有元素从上到下升序排列
-10 <= target <= 10
//从右上往左下搜索,每次排除一行或一列class Solution {public boolean searchMatrix(int[][] matrix, int target) {int n = matrix.length;if (n == 0)return false;int m = matrix[0].length;if (m == 0)return false;int i = 0, j = m - 1;while (i < n && j >= 0) {if (matrix[i][j] == target)return true;else if (matrix[i][j] > target)j--;elsei++;}return false;}}
378. 有序矩阵中第 K 小的元素
给你一个 n x n 矩阵 matrix ,其中每行和每列元素均按升序排序,找到矩阵中第 k 小的元素。
请注意,它是 排序后 的第 k 小元素,而不是第 k 个 不同 的元素。
示例 1:
输入:matrix = [[1,5,9],[10,11,13],[12,13,15]], k = 8
输出:13
解释:矩阵中的元素为 [1,5,9,10,11,12,13,13,15],第 8 小元素是 13
示例 2:
输入:matrix = [[-5]], k = 1
输出:-5
提示:
n == matrix.lengthn == matrix[i].length1 <= n <= 300-10 <= matrix[i][j] <= 10- 题目数据 保证
matrix中的所有行和列都按 非递减顺序 排列 1 <= k <= n
思路:
相较于240,不是查询特定的数是否存在于二维矩阵中,而是问第k小的数是什么。
二分套二分可以解决!
class Solution {public int kthSmallest(int[][] g, int k) {int n = g.length, m = g[0].length;int min = g[0][0], max = g[n - 1][m - 1];//二分结果int l = min, r = max;while (l < r) {int mid = l + (r - l >> 1);int c = 0;//二分查找小于等于当前查找值(mid)的元素个数for (int[] a : g) {int ll = 0, rr = m - 1;while (ll < rr) {int mm = ll + rr + 1 >> 1;if (a[mm] > mid)rr = mm - 1;elsell = mm;}if (a[ll] > mid)c += 0;elsec += ll + 1;}if (c < k)l = mid + 1;elser = mid;}return l;}}
373. 查找和最小的K对数字
给定两个以升序排列的整数数组 nums1 和 nums2 , 以及一个整数 k 。
定义一对值 (u,v),其中第一个元素来自 nums1,第二个元素来自 nums2 。
请找到和最小的 k 个数对 (u,v), (u,v) … (u,v) 。
示例 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]
示例 3:
输入: nums1 = [1,2], nums2 = [3], k = 3
输出: [1,3],[2,3]
解释: 也可能序列中所有的数对都被返回:[1,3],[2,3]
提示:
1 <= nums1.length, nums2.length <= 10-10 <= nums1[i], nums2[i] <= 10nums1,nums2均为升序排列1 <= k <= 1000
思路:
相较于373:
- 二维矩阵变成了两个数组的和构成的二维矩阵!
- 最终结果不是查询第k小的数,而是返回前k小数对!
除了利用之前373的二分套二分以外,还可以使用多用归并!
//二分套二分class Solution {public List<List<Integer>> kSmallestPairs(int[] nums1, int[] nums2, int k) {int n = nums1.length, m = nums2.length;int min = nums1[0] + nums2[0], max = nums1[n - 1] + nums2[m - 1];int l = min, r = max;while (l < r) {int mid = l + (r - l >> 1);long c = 0;for (int x : nums1) {if (x + nums2[m - 1] <= mid)c += m;else if (x + nums2[0] > mid) {break;} else {int ll = 0, rr = m - 1;while (ll < rr) {int mm = ll + (rr - ll + 1 >> 1);if (x + nums2[mm] > mid)rr = mm - 1;elsell = mm;}c += ll + 1;}}if (c < k)l = mid + 1;elser = mid;}List<List<Integer>> res = new ArrayList<>(k);Queue<List<Integer>> tmp = new LinkedList<>();for (int x : nums1) {for (int y : nums2) {if (x + y <= l) {List<Integer> arr = new ArrayList<>();arr.add(x);arr.add(y);if (x + y == l) {tmp.offer(arr);continue;}res.add(arr);k--;} elsebreak;}}while (k-- > 0 && !tmp.isEmpty()) {res.add(tmp.poll());}return res;}}
//多路归并class Solution {public List<List<Integer>> kSmallestPairs(int[] a, int[] b, int k) {List<List<Integer>> res = new ArrayList<>(k);int n = a.length, m = b.length;PriorityQueue<int[]> q = new PriorityQueue<>((o1, o2) -> o1[0] - o2[0]);for (int i = 0; i < m; i++)q.offer(new int[]{a[0] + b[i], 0, i});while (k-- > 0 && !q.isEmpty()) {int[] cur = q.poll();List<Integer> t = new ArrayList<>();t.add(a[cur[1]]);t.add(b[cur[2]]);res.add(t);if (cur[1] + 1 < n)q.offer(new int[]{a[cur[1] + 1] + b[cur[2]], cur[1] + 1, cur[2]});}return res;}}
2040. 两个有序数组的第 K 小乘积
给你两个 从小到大排好序 且下标从 0 开始的整数数组 nums1 和 nums2 以及一个整数 k ,请你返回第 k (从 1 开始编号)小的 nums1[i] * nums2[j] 的乘积,其中 0 <= i < nums1.length 且 0 <= j < nums2.length 。
示例 1:
输入:nums1 = [2,5], nums2 = [3,4], k = 2输出:8解释:第 2 小的乘积计算如下:- nums1[0] * nums2[0] = 2 * 3 = 6- nums1[0] * nums2[1] = 2 * 4 = 8第 2 小的乘积为 8 。
示例 2:
输入:nums1 = [-4,-2,0,3], nums2 = [2,4], k = 6输出:0解释:第 6 小的乘积计算如下:- nums1[0] * nums2[1] = (-4) * 4 = -16- nums1[0] * nums2[0] = (-4) * 2 = -8- nums1[1] * nums2[1] = (-2) * 4 = -8- nums1[1] * nums2[0] = (-2) * 2 = -4- nums1[2] * nums2[0] = 0 * 2 = 0- nums1[2] * nums2[1] = 0 * 4 = 0第 6 小的乘积为 0 。
示例 3:
输入:nums1 = [-2,-1,0,1,2], nums2 = [-3,-1,2,4,5], k = 3输出:-6解释:第 3 小的乘积计算如下:- nums1[0] * nums2[4] = (-2) * 5 = -10- nums1[0] * nums2[3] = (-2) * 4 = -8- nums1[4] * nums2[0] = 2 * (-3) = -6第 3 小的乘积为 -6 。
提示:
1 <= nums1.length, nums2.length <= 5 * 10-10 <= nums1[i], nums2[j] <= 101 <= k <= nums1.length * nums2.lengthnums1和nums2都是从小到大排好序的。
思路:
相较于373,从两个数组各元素和变为两个数组各元素的乘积,特别是存在负数的情况!
由于本题是查找第k小的乘积是多少,而不是找前k小数对,不需要用多路归并(其实是因为会超时!!!)
所以用二分套二分即可!只是要注意正负的问题!
先判断第k小数位于正数还是负数范围内再处理!
class Solution {public long kthSmallestProduct(int[] nums1, int[] nums2, long k) {List<Integer> a = new ArrayList<>();List<Integer> b = new ArrayList<>();List<Integer> na = new ArrayList<>();List<Integer> nb = new ArrayList<>();for (int x : nums1) {if (x < 0)na.add(x);elsea.add(x);}for (int x : nums2) {if (x < 0)nb.add(x);elseb.add(x);}long c = 1L * a.size() * nb.size() + b.size() * na.size();long res = 0;//正数情况if (k > c) {k -= c;Collections.reverse(na);Collections.reverse(nb);res = get(k, a, b, na, nb);} else {Collections.reverse(a);Collections.reverse(b);res = get(k, a, nb, b, na);}return res;}long get(long k, List<Integer> a, List<Integer> b, List<Integer> na, List<Integer> nb) {long res = 0;long l = (long)(-1e10 - 10), r = (long)(1e10 + 10);while (l < r) {long mid = l + r >> 1;long cnt = 0;cnt += binarySearch(mid, a, b);cnt += binarySearch(mid, na, nb);if (cnt < k)l = mid + 1;elser = mid;}return l;}long binarySearch(long mid, List<Integer> a, List<Integer> b) {long cnt = 0;if (a.size() == 0 || b.size() == 0)return cnt;for (Integer x : a) {int ll = 0, rr = b.size() - 1;while (ll < rr) {int mm = ll + (rr - ll + 1 >> 1);long val = 1L * x * b.get(mm);if (val > mid)rr = mm - 1;elsell = mm;}if (1L * x * b.get(ll) <= mid)cnt += ll + 1;}return cnt;}}
668. 乘法表中第k小的数
几乎每一个人都用 乘法表。但是你能在乘法表中快速找到第k小的数字吗?
给定高度m 、宽度n 的一张 m * n的乘法表,以及正整数k,你需要返回表中第k 小的数字。
例 1:
输入: m = 3, n = 3, k = 5
输出: 3
解释:
乘法表:
1 2 3
2 4 6
3 6 9
第5小的数字是 3 (1, 2, 2, 3, 3).
例 2:
输入: m = 2, n = 3, k = 6
输出: 6
解释:
乘法表:
1 2 3
2 4 6
第6小的数字是 6 (1, 2, 2, 3, 4, 6).
注意:
m和n的范围在 [1, 30000] 之间。k的范围在 [1, m * n] 之间。
思路:
类似于373,加法换成乘法
class Solution {public int findKthNumber(int m, int n, int k) {int l = 1 * 1, r = m * n;while (l < r) {int mid = l + (r - l >> 1);int cnt = get(m, n, mid);if (cnt < k)l = mid + 1;elser = mid;}return l;}int get(int m, int n, int x) {int cnt = 0;for (int i = 1; i <= n; i++) {cnt += Math.min(x / i, m);}return cnt;}}
