有序矩阵中第K小的元素
方法一:暴力法
将矩阵中的元素存入一维数组,然后进行排序,返回下标为k-1
的数。
1 | class Solution { |
2 | public int kthSmallest(int[][] matrix, int k) { |
3 | // 暴力算法 |
4 | int rows = matrix.length, columns = matrix[0].length; |
5 | int[] sorted = new int[rows * columns]; |
6 | int index = 0; |
7 | for (int[] row : matrix) { |
8 | for (int num : row) { |
9 | sorted[index++] = num; |
10 | } |
11 | } |
12 | Arrays.sort(sorted); |
13 | return sorted[k - 1]; |
14 | } |
15 | } |
复杂度分析:
- 时间复杂度:$O(n^2logn)$。
- 空间复杂度:$O(n^2)$。
方法二:小根堆
思路同topK问题,先将第一列的数存入小根堆,之后进行k-1次循环,每一次循环都删除堆顶并插入堆顶后的一个数,k-1次循环后,堆顶的元素就为第k小的元素。
1 | class Solution { |
2 | public int kthSmallest(int[][] matrix, int k) { |
3 | // 堆排序 |
4 | int n = matrix.length; |
5 | PriorityQueue<int[]> pq = new PriorityQueue<>((o1, o2) -> o1[0] - o2[0]); |
6 | for(int i=0; i<n; i++){ |
7 | // 保存当前元素、行、列 |
8 | pq.offer(new int[]{matrix[i][0], i, 0}); |
9 | } |
10 | for(int i=0; i<k-1; i++){ |
11 | int[] cur = pq.poll(); |
12 | if(cur[2] < n-1){ |
13 | pq.offer(new int[]{matrix[cur[1]][cur[2]+1], cur[1], cur[2]+1}); |
14 | } |
15 | } |
16 | return pq.poll()[0]; |
17 | } |
18 | } |
复杂度分析:
- 时间复杂度:$O(klogn)$。k在最坏情况下为$n^2$。
- 空间复杂度:$O(n)$。
方法三:二分查找
分析可知矩阵内的元素是从左上到右下递增的,其中最小的元素是$matrix[0][0]$,最大的元素是$matrix[n-1][n-1]$,分别记为$l$和$r$。
如果任取一个数$mid$满足$l<=mid<=r$,那么矩阵中不大于$mid$的数,肯定全分布在矩阵的左上角。我们可以从矩阵的左下角出发,统计出所有不大于$mid$的矩阵中数的数量。方法如下:
- 初始位置在矩阵左下角$matrix[n-1][0]$
- 设当前位置为$matrix[i][j]$,若$matrix[i][j]<=mid$,那么将当前列所在的不大于$mid$的数的数量(即$i+1$)累加到答案中,并向右移动,否则向上移动。
- 不断移动直到走出矩阵。
如下图所示:
1 | class Solution { |
2 | public int kthSmallest(int[][] matrix, int k) { |
3 | int n = matrix.length; |
4 | int left = matrix[0][0]; |
5 | int right = matrix[n - 1][n - 1]; |
6 | while (left <= right) { |
7 | int mid = left + ((right - left) >> 1); |
8 | if (check(matrix, mid, k, n)) { |
9 | right = mid - 1; |
10 | } else { |
11 | left = mid + 1; |
12 | } |
13 | } |
14 | return left; |
15 | } |
16 | |
17 | public boolean check(int[][] matrix, int mid, int k, int n) { |
18 | int i = n - 1; |
19 | int j = 0; |
20 | int num = 0; |
21 | while (i >= 0 && j < n) { |
22 | if (matrix[i][j] <= mid) { |
23 | num += i + 1; |
24 | j++; |
25 | } else { |
26 | i--; |
27 | } |
28 | } |
29 | return num >= k; |
30 | } |
31 | } |
复杂度分析:
时间复杂度:$O(nlog(r-l))$
空间复杂度:$O(1)$