題目描述:
給定一個 n x n 矩陣,其中每行和每列元素均按升序排序,找到矩陣中第k小的元素。
請注意,它是排序后的第k小元素,而不是第k個元素。
示例:
matrix = [
[ 1, 5, 9],
[10, 11, 13],
[12, 13, 15]
],
k = 8,
返回 13。
說明:
你可以假設 k 的值永遠是有效的, 1 ≤ k ≤ n2 。
解法1:縱向最小堆,使用lamba表達式會極慢
class Solution { public int kthSmallest(int[][] matrix, int k) { int count=0; int n=matrix[0].length; PriorityQueue<int[]> heap =new PriorityQueue<>((o1,o2)->{ return o1[2]-o2[2]; }); for(int i=0;i<n;i++){ int[] tmp =new int[3]; tmp[0]=i; tmp[1]=0; tmp[2]=matrix[i][0]; heap.offer(tmp); } while(!heap.isEmpty()){ int[] tmp=heap.poll(); count++; if(count==k)return tmp[2]; if(tmp[1]<n-1){ tmp[1]++; tmp[2]=matrix[tmp[0]][tmp[1]]; heap.offer(tmp); } } return -1; } }
解法2:橫向最小堆 復雜度為 (k-1)log(n),定義一個元組類實現comparable接口,時間提升很多
public class Solution {
public int kthSmallest(int[][] matrix, int k) {
int n = matrix.length;
PriorityQueue<Tuple> pq = new PriorityQueue<Tuple>();
for(int j = 0; j <= n-1; j++) pq.offer(new Tuple(0, j, matrix[0][j]));
for(int i = 0; i < k-1; i++) {
Tuple t = pq.poll();
if(t.x == n-1) continue;
pq.offer(new Tuple(t.x+1, t.y, matrix[t.x+1][t.y]));
}
return pq.poll().val;
}
}
class Tuple implements Comparable<Tuple> {
int x, y, val;
public Tuple (int x, int y, int val) {
this.x = x;
this.y = y;
this.val = val;
}
@Override
public int compareTo (Tuple that) {
return this.val - that.val;
}
}
解法3:二分查找
二分查找的關鍵是找出“查找空間”。有兩種查找空間:基於下標的和基於范圍的(最大值和最小值之間的范圍)。大多數情況,如果數組是在一個方向上有序,我們使用下標作為搜索空間。如果數組無序,我們使用范圍作為搜索空間。
這題不用下標作為搜索空間的原因是這個二維數組在兩個方向上排序,不能找到一個線性的方向把數組的值和下標綁定在一起。
1 public class Solution { 2 public int kthSmallest(int[][] matrix, int k) { 3 int lo = matrix[0][0], hi = matrix[matrix.length - 1][matrix[0].length - 1] + 1;//[lo, hi) 4 while(lo < hi) { 5 int mid = lo + (hi - lo) / 2; 6 int count = 0, j = matrix[0].length - 1; 7 for(int i = 0; i < matrix.length; i++) { 8 while(j >= 0 && matrix[i][j] > mid) j--; 9 count += (j + 1); 10 } 11 if(count < k) lo = mid + 1; 12 else hi = mid; 13 } 14 return lo; 15 } 16 }
