题目

给你一个 n x n 矩阵 matrix ,其中每行和每列元素均按升序排序,找到矩阵中第 k 小的元素。
请注意,它是 排序后 的第 k 小元素,而不是第 k不同 的元素。

你必须找到一个内存复杂度优于 O(n2) 的解决方案。

示例 1:

1
2
3
输入: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:

1
2
输入:matrix = [[-5]], k = 1
输出:-5

提示:

  • n == matrix.length
  • n == matrix[i].length
  • 1 <= n <= 300
  • -109 <= matrix[i][j] <= 109
  • 题目数据 保证 matrix 中的所有行和列都按 非递减顺序 排列
  • 1 <= k <= n2

实现代码

方式一

分治算法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
class Solution {
public int kthSmallest(int[][] matrix, int k) {

// 合并多链表思路
int[] mergeArray = kthSmallest(matrix, 0, matrix.length - 1);
return mergeArray[k-1];
}

public int[] kthSmallest(int[][] matrix, int left, int right) {

if (left == right) {
return matrix[left];
}
int mid = left + (right - left) / 2;
int[] leftArray = kthSmallest(matrix, left, mid);
int[] rightArray = kthSmallest(matrix, mid + 1, right);
return merge(leftArray, rightArray);
}

private int[] merge(int[] array1, int[] array2) {

int[] newArray = new int[array1.length + array2.length];
int k = 0;
int j = 0;
int n = 0;
while (array1.length != k && array2.length != j) {
if (array1[k] < array2[j]) {
newArray[n] = array1[k];
k++;
n++;
} else {
newArray[n] = array2[j];
j++;
n++;
}
}
if (array1.length != k){
for (int i = k; i < array1.length; i++) {
newArray[n] = array1[i];
n++;
}
}
if (array2.length != j){
for (int i = j; i < array2.length; i++) {
newArray[n] = array2[i];
n++;
}
}
return newArray;
}
}

时间复杂度

时间组成:

空间复杂度

方式二

优先级队列

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
class Solution {
public int kthSmallest(int[][] matrix, int k) {
// 定义最小堆
PriorityQueue<int[]> pq = new PriorityQueue<>((a, b) -> {
return a[0] - b[0];
});
// 初始化堆(因为是数组,所以需要预留第3个位置存放索引角标)
// 第二个位置用于记录当前是第几行
for (int i = 0; i < matrix.length; i++) {
pq.offer(new int[] { matrix[i][0], i, 0 });
}
int[] poll;
// 排序
while (!pq.isEmpty() && k > 0) {
// 取出来
poll = pq.poll();
k--;
if (k == 0) {
return poll[0];
}
// 放进去
int nextIndex = poll[2] + 1;
if (nextIndex < matrix.length) {
pq.offer(new int[] { matrix[poll[1]][nextIndex], poll[1], nextIndex });
}
}
return 0;
}
}

核心难点在于如何定义优先队列的元素,因为数组不像链表那样有记录下一个的指针,所以构建的元素需要额外的空间进行存储

时间复杂度

时间组成:

  • 初始化堆:假设matrix的长度为n,总共O(n*logn)
    • 每次offer操作:O(logn)
    • 一共n次
  • while循环:poll+offer = O(logn),总共O(k*logn)
    • poll操作:
      • 每次O(logn)
      • 一共k
    • offer操作
      • 可能会有一次,O(logn)

总时间复杂度:O(nlogn) + O(klogn) = O((n + k)logn)

空间复杂度

空间组成:

  • 堆:O(n)

总空间复杂度:O(n)