Leetcode 378. 有序矩阵中第K小的元素
题目描述:
给定一个 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 }