排序矩阵中的第K个最小元素

时间:2013-03-02 21:17:12

标签: arrays algorithm data-structures matrix multidimensional-array

这是一个面试问题。

在具有已排序行和列的矩阵中找到K th 最小元素 K th 最小元素是a[i, j]之一,例如i + j = K,这是正确的吗?

9 个答案:

答案 0 :(得分:33)

假。

考虑一个像这样的简单矩阵:

1 3 5
2 4 6
7 8 9

9是最大的(第9个最小的)元素。但是9是在A [3,3]和3 + 3!= 9.(无论你使用什么索引约定,都不可能是真的。)


你可以在O(k log n)时间内通过逐步合并行来解决这个问题,用堆扩充来有效地找到最小元素。

基本上,您将第一列的元素放入堆中并跟踪它们来自的行。在每一步中,从堆中删除最小元素并从它来自的行中推送下一个元素(如果到达行的末尾,则不要推送任何内容)。删除最小值和添加新元素都需要花费O(log n)。在第j步中,删除j最小元素,因此在k步之后,您将完成O(k log n)次操作的总成本(其中n是矩阵中的行数) )。

对于上面的矩阵,您最初从堆中的1,2,7开始。您删除1并添加3(因为第一行为1 3 5)以获取2,3,7。您删除2并添加4以获取3,4,7。移除3并添加5以获取4,5,7。移除4并添加6以获取5,6,7。请注意,我们将按全局排序顺序删除元素。您可以看到,继续此过程将在k次迭代后产生k个最小元素。

(如果矩阵的行数多于列数,则对列进行操作以减少运行时间。)

答案 1 :(得分:22)

O(k log(k))解决方案。

  • 建立一个minheap。

  • (0,0)添加到堆中。虽然我们没有找到kth最小元素,但是从堆中删除顶部元素(x,y)并添加下两个元素[(x+1,y)(x,y+1)](如果它们尚未访问过)之前。

我们正在对O(k)大小的堆执行O(k)操作,因此复杂性很高。

答案 2 :(得分:5)

可以使用二进制搜索和排序矩阵中的优化计数来解决此问题。二进制搜索要花费 O(log(n))时间,对于每个搜索值,平均要进行 n 次迭代才能找到小于所搜索数字的数字。二进制搜索的搜索空间限制为矩阵mat[0][0]的最小值和最大值mat[n-1][n-1]

对于从二分搜索中选择的每个数字,我们需要计算小于或等于该特定数字的数字。因此,可以找到k^th最小的数字。

为更好地理解您可以参考以下视频:

https://www.youtube.com/watch?v=G5wLN4UweAM&t=145s

答案 3 :(得分:1)

从左上角开始遍历矩阵(0,0)并使用二进制堆来存储"边界" - 矩阵的访问部分与其余部分之间的边界。

Java实现:

private static class Cell implements Comparable<Cell> {

    private final int x;
    private final int y;
    private final int value;

    public Cell(int x, int y, int value) {
        this.x = x;
        this.y = y;
        this.value = value;
    }

    @Override
    public int compareTo(Cell that) {
        return this.value - that.value;
    }

}

private static int findMin(int[][] matrix, int k) {

    int min = matrix[0][0];

    PriorityQueue<Cell> frontier = new PriorityQueue<>();
    frontier.add(new Cell(0, 0, min));

    while (k > 1) {

        Cell poll = frontier.remove();

        if (poll.y + 1 < matrix[poll.x].length) frontier.add(new Cell(poll.x, poll.y + 1, matrix[poll.x][poll.y + 1]));
        if (poll.x + 1 < matrix.length) frontier.add(new Cell(poll.x + 1, poll.y, matrix[poll.x + 1][poll.y]));

        if (poll.value > min) {
            min = poll.value;
            k--;
        }

    }

    return min;

}

答案 4 :(得分:1)

正如人们之前提到的,最简单的方法是建立一个min heap。这是使用PriorityQueue的Java实现:

private int kthSmallestUsingHeap(int[][] matrix, int k) {

    int n = matrix.length;

    // This is not necessary since this is the default Int comparator behavior
    Comparator<Integer> comparator = new Comparator<Integer>() {
        @Override
        public int compare(Integer o1, Integer o2) {
            return o1 - o2;
        }
    };

    // building a minHeap
    PriorityQueue<Integer> pq = new PriorityQueue<>(n*n, comparator);
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < n; j++) {
            pq.add(matrix[i][j]);
        }
    }

    int ans = -1;
    // remove the min element k times
    for (int i = 0; i < k; i++) {
        ans = pq.poll();
    }

    return ans;
}

答案 5 :(得分:1)

上述解决方案无法处理对角线条件,因此无法应用于下方矩阵

int arr2[][] = { { 1, 4, 7, 11, 15 }, 
                 { 2, 5, 8, 12, 19 }, 
                 { 3, 6, 9, 16, 22 }, 
                 { 10, 13, 14, 17, 24 },
                 { 18, 21, 23, 26, 30 } }

并且k = 5

返回7而答案是5

答案 6 :(得分:0)

似乎这只是使用了这个功能:每一行都被排序,但不使用它的逐列排序功能。

答案 7 :(得分:0)

矩阵中的第K个最小元素:

问题可以缩小如下。

如果k是20,那么取k * k矩阵(答案肯定是谎言。)

现在,您可以重复合并行中的行以构建排序数组,然后找到第k个最小数字。

答案 8 :(得分:-1)

//int arr[][] = {{1, 5, 10, 14},
//        {2, 7, 12, 16},
//        {4, 10, 15, 20},
//        {6, 13, 19, 22}
//};
// O(k) Solution
public static int myKthElement(int arr[][], int k) {
    int lRow = 1;
    int lCol = 0;
    int rRow = 0;
    int rCol = 1;
    int count = 1;

    int row = 0;
    int col = 0;

    if (k == 1) {
        return arr[row][col];
    }

    int n = arr.length;
    if (k > n * n) {
        return -1;
    }

    while (count < k) {
        count++;

        if (arr[lRow][lCol] < arr[rRow][rCol]) {
            row = lRow;
            col = lCol;

            if (lRow < n - 1) {
                lRow++;
            } else {
                if (lCol < n - 1) {
                    lCol++;
                }

                if (rRow < n - 1) {
                    lRow = rRow + 1;
                }
            }
        } else {
            row = rRow;
            col = rCol;

            if (rCol < n - 1) {
                rCol++;
            } else {
                if (rRow < n - 1) {
                    rRow++;
                }
                if (lCol < n - 1) {
                    rCol = lCol + 1;
                }
            }
        }
    }

    return arr[row][col];
}