leetcode 378. 有序矩阵中第K小的元素(Kth Smallest Element in a Sorted Matrix)

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

示例:

matrix = [
   [ 1,  5,  9],
   [10, 11, 13],
   [12, 13, 15]
],
k = 8,

返回 13。

说明: 
你可以假设 k 的值永远是有效的, 1 ≤ k ≤ n2 。

 

 

 

看完题解,自己也写了一遍,不过还是理解原理重要

 

class Solution {
    public int kthSmallest(int[][] matrix, int k) {
        PriorityQueue<Tuple> pq = new PriorityQueue<>();
        for(int i=0;i<matrix[0].length;i++) 
        	pq.add(new Tuple(0,i,matrix[0][i]));
        
        for(int i=0;i<k-1;i++) {
        	Tuple t = pq.poll();
        	if(t.x==matrix.length-1)
        		continue;
        	pq.add(new Tuple(t.x+1,t.y,matrix[t.x+1][t.y]));
        }
        
        return pq.poll().val;
    }
    
    class Tuple implements Comparable<Tuple>{
    	
    	int x;
    	int y;
    	int val;
    	public Tuple() {
    		
    	}
    	public Tuple(int x, int y, int val) {
    		this.x = x;
    		this.y = y;
    		this.val = val;
    	}
    	
		@Override
		public int compareTo(Tuple t) {
			return this.val - t.val;
		}
    	
    }
}

 

 

 

参考自http://www.cnblogs.com/mydesky2012/p/5763967.html

//优先级队列,是从jdk1.5开始提供的新的数据结构接口
//如果不提供Comparator的话,优先队列中元素默认按自然顺序排列
//每次从队列中取出的是具有最高优先权的元素。
import java.util.PriorityQueue;

/**
 * 已知一个n*m的矩阵,每一行、每一列都是有序的。
 * 求这个矩阵中第k小的元素
 * @author i333083
 *
 */
public class KthSmallestEleInSortedMatrix {

    public static void main(String[] args) {
        // TODO Auto-generated method stub

        int[][] a = new int[][]{{1,5,9,11},{2,6,10,14},{3,7,13,15}};
        System.out.println(kthSmallest(a,11));
    }
    
    //求矩阵中第k小的元素
    public static int kthSmallest(int[][] matrix,int k){
        int rows = matrix.length; //矩阵的行数
        int cols = matrix[0].length;//列数
        
        //优先队列
        PriorityQueue<Tuple> queue = new PriorityQueue<Tuple>(); //入队列
        
        /**
         * 这里有一个设计非常巧妙的地方,将第一行升序的序列先入队列,根据Tuple类型的定义,最小的元素将会在队首。
         * 每当队首元素出队列后,都将其所在列的下一个元素入队列,直到最后一行;
         * 这样,就可以比较一个元素的同一行与同一列中相邻的元素中哪个较小,从而保证队首元素始终是矩阵中所有元素的最小值
         */
        for(int j = 0; j < cols; ++j)
            queue.add(new Tuple(0,j,matrix[0][j]));
        //进行k-1次出队、入队,最后队首元素一定是所有元素中的第k小
        for(int i = 0; i < k - 1; ++i){
            Tuple p = queue.poll(); //出队列
            System.out.print(p.val + " ");
            if(p.x == rows - 1) //若是最后,则没有元素需要入队列了
                continue;
            queue.add(new Tuple(p.x + 1,p.y,matrix[p.x + 1][p.y]));
        }
        
        return queue.poll().val;
    }
}

//实现Comparable接口要覆盖compareTo方法, 在compareTo方法里面实现比较
//自定义一个数据结构-元组,继承自comparable接口
class Tuple implements Comparable<Tuple>{
    int x;
    int y;
    int val;
    public Tuple(int x,int y,int val){
        this.x = x;
        this.y = y;
        this.val = val;
    }
    
    @Override
    public int compareTo(Tuple t){
        return this.val - t.val; //若新加入的元素较小,则排在队首
    }
}

 

 

解决方案1:堆
这是我的解决方案的一步:

 

  1. 从第一行构建一个minHeap元素。
  2. 执行以下操作k-1次:
    每次轮询根(堆中的顶部元素)时,您需要知道该元素的行号和列号(因此我们可以在这里创建一个元组类),替换它root与同一列中的下一个元素。

 

完成此问题后,请多考虑一下:

 

  1. 对于这个问题,您还可以从第一列构建一个min Heap,并执行与上面类似的操作。(将root替换为同一行中的下一个元素)
  2. 更重要的是,这个问题与Leetcode373找到最小和的K对完全相同,我使用相同的代码打败了96.42%,解决了这个问题之后,你可以查看这个链接:
    https://discuss.leetcode。 COM /主题/ 52953 /股,我的解决方案-这拍,96-42
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;
    }
}

 

解决方案2:二进制搜索
我们在这里完成,但让我们以另一种方式思考这个问题:
任何二进制搜索的关键点是找出“搜索空间”。对我来说,我认为有两种“搜索空间” - 索引和范围(从最小数字到最大数字的范围)。最常见的情况是,当数组按一个方向排序时,我们可以使用索引作为“搜索空间”,当数组未排序并且我们要查找特定数字时,我们可以使用“范围”。

 

让我举两个这两个“搜索空间”的例子

 

  1. index - 一堆例子 - https://leetcode.com/problems/find-minimum-in-rotated-sorted-array/(数组已排序)
  2. 范围 - https://leetcode.com/problems/find-the-duplicate-number/(未排序数组)

 

我们没有使用索引作为“搜索空间”来解决这个问题的原因是矩阵在两个方向上排序,我们找不到一种线性方式来映射数字及其索引。

 

public class Solution {
    public int kthSmallest(int[][] matrix, int k) {
        int lo = matrix[0][0], hi = matrix[matrix.length - 1][matrix[0].length - 1] + 1;//[lo, hi)
        while(lo < hi) {
            int mid = lo + (hi - lo) / 2;
            int count = 0,  j = matrix[0].length - 1;
            for(int i = 0; i < matrix.length; i++) {
                while(j >= 0 && matrix[i][j] > mid) j--;
                count += (j + 1);
            }
            if(count < k) lo = mid + 1;
            else hi = mid;
        }
        return lo;
    }
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值