优化排序矩阵中第 N 个最大元素的代码

Posted

技术标签:

【中文标题】优化排序矩阵中第 N 个最大元素的代码【英文标题】:Optimize code for Nth largest element in sorted matrix 【发布时间】:2014-06-13 14:29:03 【问题描述】:

我有一个排序矩阵中第 n 个最大元素的代码(按行和列递增的顺序排序)

我在执行代码中的 (findNextElement) 部分时遇到了一些问题 即,如果该行已用尽,则向上一行并获取其中的下一个元素。

我已经做到了,但是代码看起来有点复杂。 (我的代码可以正常工作并正确生成输出)我将在这里发布我的代码

k 是第 K 个最大的元素 m, n 是矩阵维度(目前只支持 NxN 矩阵,但可以修改为支持 MxN)

public int findkthLargestElement(int[][] input, int k, int m, int n) 
    if (m <=1 || n <= 1 || k > m * n) 
        return Integer.MIN_VALUE;
    
    int i = 0;
    int j = 0;
    if (k < m && k < n) 
        i = m - k;
        j = n - k;
    
    PriorityQueue<Element> maxQueue = new PriorityQueue(m, new Comparator<Element>() 
        @Override
        public int compare(Element a, Element b) 
            return b.value - a.value;
        
    );

    Map<Integer, Integer> colMap = new HashMap<Integer, Integer>();
    for (int row = i; row < m; row++) 
        Element e = new Element(input[row][n - 1], row, n - 1);
        colMap.put(row, n - 1);
        maxQueue.add(e);
    
    Element largest = new Element(0, 0, 0);
    for (int l = 0; l < k; l++) 
        largest = maxQueue.poll();
        int row = largest.row;
        colMap.put(row, colMap.get(row) - 1);
        int col = colMap.get(row);
        while (col < j && row > i) 
            row = row - 1;
            colMap.put(row, colMap.get(row) - 1);
            col = Math.max(0, colMap.get(row));
        

        Element nextLargest = new Element(input[row][Math.max(0, col)], row, Math.max(0, col));
        maxQueue.add(nextLargest);
    
    return largest.value;


我在 for 循环中需要一些帮助,请建议我一个更好的方法来完成任务。

我的代码在这里运行 http://ideone.com/wIeZSo

好的,我找到了一种简单有效的方法来完成这项工作,我将 for 循环更改为 ths

    for (int l = 0; l < k; l++) 
        largest = maxQueue.poll();
        int row = largest.row;
        colMap.put(row, colMap.get(row) - 1);
        int col = colMap.get(row);
        if (col < j) 
            continue;
        
        Element nextLargest = new Element(input[row][Math.max(0, col)], row, Math.max(0, col));
        maxQueue.add(nextLargest);
    

如果我们对某一列感到筋疲力尽,那么我们不会再添加任何项目,直到我们从其他列中找到一个元素。

这也适用于仅按行而非按列排序的矩阵。

【问题讨论】:

你或许应该解释一下为什么这个方法的实现可能只是int i=m*n-1-(k-1);return input[i/n][i%n];。很可能是因为矩阵中可能有 相等 个元素...? @Marco13 我从来没有想过你提出的解决方案!但是是的,这些值可以重复。 【参考方案1】:

回应评论:即使有重复的元素,我认为没有必要使用复杂的数据结构,如优先级队列和映射,甚至内部类。我认为应该可以简单地从数组的末尾开始,走到数组的开头,然后计算值更改的频率。从值“infinity”(或此处为Integer.MAX_VALUE)开始,在kth 值更改后,一个具有kth 最大元素。

public class KthLargestElementTest

    public static void main (String[] args) throws java.lang.Exception
    
        testDistinct();
        testNonDistinct();
        testAllEqual();
    

    private static void testDistinct()
    
        System.out.println("testDistinct");
        int[][] input = new int[][] 
        
            1, 2, 3, 4,
            8, 9, 10, 11,
            33, 44, 55, 66,
            99, 150, 170, 200
        ;
        for (int i = 1; i <= 17; i ++) 
        
            System.out.println(findkthLargestElement(input, i, 4, 4));  
        
    

    private static void testNonDistinct()
    
        System.out.println("testNonDistinct");
        int[][] input = new int[][]
        
             1, 1, 1, 4 ,
             4, 4, 11, 11 ,
             11, 11, 66, 66 ,
             66, 150, 150, 150  
        ;
        for (int i = 1; i <= 6; i++)
        
            System.out.println(findkthLargestElement(input, i, 4, 4));
        
        

    private static void testAllEqual()
    
        System.out.println("testAllEqual");
        int[][] input = new int[][]
        
             4, 4, 4, 4 ,
             4, 4, 4, 4 ,
             4, 4, 4, 4 ,
             4, 4, 4, 4  
        ;
        for (int i = 1; i <= 2; i++)
        
            System.out.println(findkthLargestElement(input, i, 4, 4));
        
        

    public static int findkthLargestElement(
        int[][] input, int k, int m, int n) 
    
        int counter = 0;
        int i=m*n-1;
        int previousValue = Integer.MAX_VALUE;
        while (i >= 0)
        
            int value = input[i/n][i%n];
            if (value < previousValue)
            
                counter++;
            
            if (counter == k)
            
                return value;
            
            previousValue = value;
            i--;
        
        if (counter == k)
        
            return input[0][0];
        
        System.out.println("There are no "+k+" different values!");
        return Integer.MAX_VALUE;
    


【讨论】:

谢谢!我想我已经超越了自己并实现了 K 方式合并来处理仅按列排序的矩阵。例如1,2,3 9,10,11 4,5,6 我认为你的代码会在这个int[][] i = 10, 20, 30, 40, 15, 25, 35, 45, 24, 29, 37, 48, 32, 33, 39, 50;上中断 @jay 好的,就我的理解而言,最后一个不是“排序”的。也许很难给出一个精确的问题陈述和有代表性的测试用例,但它可能是必要的(至少有人期望一个精确和有用的答案)

以上是关于优化排序矩阵中第 N 个最大元素的代码的主要内容,如果未能解决你的问题,请参考以下文章

[leetcode]378. 有序矩阵中第K小的元素

378. 有序矩阵中第 K 小的元素

冒泡排序python优化版本

如何查找无序数组中的Top n

leetcode378 有序矩阵中第k小的元素

排序算法入门之冒泡排序及其优化(java实现)