これは面接の質問です。
Kを見つけるth 行と列がソートされた行列の最小要素。
Kが正しいですかth 最小の要素は、a[i, j]
などのi + j = K
の1つですか?
誤り。
次のような単純な行列を考えてみましょう。
_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
番目に小さい要素が生成されることがわかります。
(マトリックスに列よりも多くの行がある場合は、代わりに列を操作して実行時間を短縮します。)
O(k log(k))
ソリューション。
ミンヒープを構築します。
ヒープに_(0,0)
_を追加します。 kth
の最小要素が見つかりませんでしたが、ヒープから一番上の要素_(x,y)
_を削除し、次の2つの要素[(x+1,y)
と_(x,y+1)]
_がない場合は追加します。 t以前に訪問されました。
サイズO(k)
のヒープでO(k)
操作を実行しているため、複雑です。
左上隅(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;
}
これは機能を使用しているようです。すべての行が並べ替えられますが、列ごとに並べ替えられた機能は使用されません。
行列のK番目に小さい要素:
問題は以下のように絞り込むことができます。
kが20の場合、k * k行列を取ります(答えは間違いなくあります)。
これで、行をペアで繰り返しマージして、並べ替えられた配列を作成し、k番目に小さい数を見つけることができます。
前述のように、最も簡単な方法は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;
}