17

0 と 1 で満たされたサイズ nxm の行列が与えられた場合

例えば:

1 1 0 1 0

0 0 0 0 0

0 1 0 0 0

1 0 1 1 0

行列の (i,j) に 1 がある場合、列 j と行 i を 1 で埋めます

つまり、次のようになります。

1 1 1 1 1

1 1 1 1 0

1 1 1 1 1

1 1 1 1 1

必要な複雑さ: O(n*m) 時間と O(1) スペース

注: マトリックス エントリには「0」または「1」以外を格納することはできません

上記は、マイクロソフト インタビューの質問です。

ここで2時間考えました。手がかりはあるが、これ以上先に進めない。


Ok。この問題の最初の重要な部分は、Even using a straight forward brute-force way簡単には解決できないということです。

2 つのループを使用してマトリックス内のすべてのセルを反復処理し、それに応じて行と列を変更すると、結果のマトリックスは元のマトリックスに基づく必要があるため、実行できません。

たとえば、 が表示されている場合、すべてをにa[0][0] == 1変更することはできません。これは、 には元々 0 がないため影響します。row 0column 01row 1row 1


2 番目に気付いたのは、行rに のみが含まれ0、列cに のみが含まれる場合、 ;0a[r][c]なければならないということです。0このパターンにない他のポジションは1.

a[r][c]次に、別の質問があります。そのような行と列が見つかった場合、対応するセルをspecialすでに 0 としてマークするにはどうすればよいですか。


私の直感は、これに対してある種のビット操作を使用する必要があるということです。または、必要な複雑さを満たすために、次のようなことをしなければなりませんAfter I take care of a[i][j], I should then proceed to deal with a[i+1][j+1], instead of scan row by row or column by column

時間の複雑さを考慮しない力ずくの場合でも、他の条件では解決できません。

誰にも手がかりがありますか?


解決策: Java バージョン

@japreiss はこの質問に答えており、彼/彼女の答えは賢明で正しいものです。彼のコードは Python で書かれており、今は Java バージョンを提供しています。クレジットはすべて @japreiss に送られます

public class MatrixTransformer {

    private int[][] a;
    private int m;
    private int n;
    
    public MatrixTransformer(int[][] _a, int _m, int _n) {
        a = _a;
        m = _m;
        n = _n;
    }
    
    private int scanRow(int i) {
        int allZero = 0;
        for(int k = 0;k < n;k++)
            if (a[i][k] == 1) {
                allZero = 1;
                break;
            }
        
        return allZero;
    }
    
    private int scanColumn(int j) {
        int allZero = 0;
        for(int k = 0;k < m;k++)
            if (a[k][j] == 1) {
                allZero = 1;
                break;
            }
        
        return allZero;
    }
    
    private void setRowToAllOnes(int i) {
        for(int k = 0; k < n;k++)
            a[i][k] = 1;
    }
    
    private void setColToAllOnes(int j) {
        for(int k = 0; k < m;k++)
            a[k][j] = 1;
    }
        
//  # we're going to use the first row and column
//  # of the matrix to store row and column scan values,
//  # but we need aux storage to deal with the overlap
//  firstRow = scanRow(0)
//  firstCol = scanCol(0)
//
//  # scan each column and store result in 1st row - O(mn) work
    
        
        
    public void transform() {
        int firstRow = scanRow(0);
        int firstCol = scanColumn(0);
                
                
        for(int k = 0;k < n;k++) {
            a[0][k] = scanColumn(k);
        }

        // now row 0 tells us whether each column is all zeroes or not
        // it's also the correct output unless row 0 contained a 1 originally

        for(int k = 0;k < m;k++) {
            a[k][0] = scanRow(k);
        }
        
        a[0][0] = firstCol | firstRow;
        
        for (int i = 1;i < m;i++)
            for(int j = 1;j < n;j++)
                a[i][j] = a[0][j] | a[i][0];


        
        if (firstRow == 1) {
            setRowToAllOnes(0);
        }
        
        if (firstCol == 1) 
            setColToAllOnes(0);
    }
    
    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder();
        
        for (int i = 0; i< m;i++) {
            for(int j = 0;j < n;j++) {
                sb.append(a[i][j] + ", ");
            }
            sb.append("\n");
        }
        
        return sb.toString();
    }
    /**
     * @param args
     */
    public static void main(String[] args) {
        int[][] a = {{1, 1, 0, 1, 0}, {0, 0, 0, 0, 0},{0, 1, 0, 0, 0},{1, 0, 1, 1, 0}};
        MatrixTransformer mt = new MatrixTransformer(a, 4, 5);
        mt.transform();
        System.out.println(mt);
    }

}
4

5 に答える 5

19

boolこれは、2つの追加のストレージを使用するPython擬似コードのソリューションです。英語よりもはっきりしていると思います。

def scanRow(i):
    return 0 if row i is all zeroes, else 1

def scanColumn(j):
    return 0 if col j is all zeroes, else 1

# we're going to use the first row and column
# of the matrix to store row and column scan values,
# but we need aux storage to deal with the overlap
firstRow = scanRow(0)
firstCol = scanCol(0)

# scan each column and store result in 1st row - O(mn) work
for col in range(1, n):
    matrix[0, col] = scanColumn(col)

# now row 0 tells us whether each column is all zeroes or not
# it's also the correct output unless row 0 contained a 1 originally

# do the same for rows into column 0 - O(mn) work
for row in range(1, m):
    matrix[row, 0] = scanRow(row)

matrix[0,0] = firstRow or firstCol

# now deal with the rest of the values - O(mn) work
for row in range(1, m):
    for col in range(1, n):
        matrix[row, col] = matrix[0, col] or matrix[row, 0]


# 3 O(mn) passes!

# go back and fix row 0 and column 0
if firstRow:
    # set row 0 to all ones

if firstCol:
    # set col 0 to all ones
于 2012-05-31T20:01:03.597 に答える
8

これは、問題を解決するためのクリーンでシンプルなアルゴリズムを提供する別の直感です。

O(n) 空間を使用する初期アルゴリズム。

ここでは、O(1) メモリ制約を無視しましょう。O(n) メモリを使用できるとします (行列が m × n の場合)。これにより、この問題がはるかに簡単になり、次の戦略を使用できます。

  • 列ごとに 1 つのエントリを持つブール配列を作成します。
  • 列ごとに、列に 1 があるかどうかを判断し、その情報を適切な配列エントリに格納します。
  • 行ごとに、行に 1 がある場合、その行をすべて 1 に設定します。
  • 各列について、対応する配列エントリが設定されている場合は、その列をすべて 1 に設定します。

例として、次の配列を考えてみましょう。

1 1 0 1 0
0 0 0 0 0
0 1 0 0 0
1 0 1 1 0

補助配列を作成して入力することから始めます。これは、各列を一度に 1 つずつアクセスすることで、時間 O(mn) で実行できます。これを次に示します。

1 1 0 1 0
0 0 0 0 0
0 1 0 0 0
1 0 1 1 0

1 1 1 1 0  <--- aux array

次に、行全体を繰り返し処理し、1 が含まれている場合はそれぞれを埋めます。これにより、次の結果が得られます。

1 1 1 1 1
0 0 0 0 0
1 1 1 1 1
1 1 1 1 1

1 1 1 1 0  <--- aux array

最後に、補助配列のその位置に 1 がある場合は、各列に 1 を入力します。これを次に示します。

1 1 1 1 1
1 1 1 1 0
1 1 1 1 1
1 1 1 1 1

1 1 1 1 0  <--- aux array

問題が 1 つあります。これは O(n) スペースを使用しますが、これはありません! では、なぜこのルートを下るのですか?

O(1) スペースを使用する改訂されたアルゴリズム。

O(1) 空間を使用してこのアルゴリズムを実行するには、非常にかわいいトリックを使用できることがわかりました。重要な観察が必要です。すべての行に少なくとも 1 つの 1 が含まれている場合、マトリックス全体が 1 になります。したがって、これが当てはまるかどうかを確認することから始めます。もしそうなら、素晴らしいです!終わったね。

それ以外の場合は、マトリックス内にすべて 0 の行が存在する必要があります。この行はすべて 0 であるため、「1 を含む各行を 1 で埋める」ステップでは行が埋められないことがわかります。したがって、その行を補助配列として使用できます。

これを実際に見てみましょう。これで始めます:

1 1 0 1 0
0 0 0 0 0
0 1 0 0 0
1 0 1 1 0

これで、すべてが 0 の行を見つけて、補助配列として使用できます。

1 1 0 1 0
0 0 0 0 0  <-- Aux array
0 1 0 0 0
1 0 1 1 0

各列を見て、どの列に少なくとも 1 つの 1 が含まれているかをマークして、補助配列に入力します。

1 1 0 1 0
1 1 1 1 0  <-- Aux array
0 1 0 0 0
1 0 1 1 0

ここで 1 を入力しても問題ありません。とにかく入力されることがわかっているからです。ここで、1 を含む各行について、補助配列 row を除いて、これらの行に 1 を入力します。

1 1 1 1 1
1 1 1 1 0  <-- Aux array
1 1 1 1 1
1 1 1 1 1

補助配列は、最初はすべて 0 だったのでスキップします。通常、この配列は埋められません。最後に、補助配列の各列に 1 を 1 で埋めて、次の最終結果を得ます。

1 1 1 1 1
1 1 1 1 0  <-- Aux array
1 1 1 1 1 
1 1 1 1 1

別の例を見てみましょう。次の設定を検討してください。

1 0 0 0
0 0 1 0
0 0 0 0
0 0 1 0

次に示すように、すべてゼロの行を見つけることから始めます。

1 0 0 0
0 0 1 0
0 0 0 0 <-- Aux array
0 0 1 0

次に、1 を含む列をマークして、その行にデータを入力しましょう。

1 0 0 0
0 0 1 0
1 0 1 0 <-- Aux array
0 0 1 0

次に、1 を含むすべての行に入力します。

1 1 1 1
1 1 1 1
1 0 1 0 <-- Aux array
1 1 1 1

次に、aux 配列の 1 を含むすべての列に 1 を入力します。これはすでにここで行われ、結果が得られました!

別の例として、次の配列を考えてみましょう。

1 0 0
0 0 1
0 1 0

ここのすべての行には少なくとも 1 つの 1 が含まれているため、行列に 1 を入力するだけで完了です。

最後に、次の例を試してみましょう。

0 0 0 0 0
0 0 0 0 0
0 1 0 0 0
0 0 0 0 0
0 0 0 1 0

補助配列には多くの選択肢があるため、最初の行を選択しましょう。

0 0 0 0 0 <-- aux array
0 0 0 0 0
0 1 0 0 0
0 0 0 0 0
0 0 0 1 0

次に、aux 配列に入力します。

0 1 0 1 0 <-- aux array
0 0 0 0 0 
0 1 0 0 0
0 0 0 0 0
0 0 0 1 0

次に、行に入力します。

0 1 0 1 0 <-- aux array
0 0 0 0 0 
1 1 1 1 1
0 0 0 0 0
1 1 1 1 1

ここで、aux 配列に基づいて列に入力します。

0 1 0 1 0 <-- aux array
0 1 0 1 0 
1 1 1 1 1
0 1 0 1 0
1 1 1 1 1

これで完了です。全体が O(mn) 時間で実行されるため、

  • aux 配列を見つけるために O(mn) を実行します。存在しない場合は、O(mn) がすぐに実行される可能性があります。
  • 補助配列を埋めるために O(mn) 作業を行います。
  • O(mn) を実行して、1 を含む行を埋めます。
  • 1 を含む列を埋めるために O(mn) 作業を行います。

さらに、補助配列のインデックスと行列をループするのに十分な変数を格納する必要があるだけなので、O(1) スペースしか使用しません。

EDIT :このアルゴリズムの Java 実装があり、それを詳細に説明するコメントが私の個人サイトで入手できます。楽しみ!

お役に立てれば!

于 2014-03-06T19:33:40.957 に答える
1

行列が 0 から始まると仮定します。つまり、最初の要素は mat[0][0] にあります。

  1. 最初の行と最初の列をテーブル ヘッダーとして使用して、それぞれ列と行の情報を含めます。1.1 mat[0][0] の要素に注意してください。1の場合、最後に特別な処理が必要になります(後述)

  2. ここで、index[1][1] から最後の要素 2.1 までの内部行列のスキャンを開始します [row][col] == 1 の要素の場合、次のようにテーブル ヘッダー データを更新します 行: mat[row][0 ] = 1; 列: mat[0][col] = 1;

この時点で、どの列と行を 1 に設定する必要があるかについての完全な情報が得られました。

  1. 再び、mat[1][1] から始まる内部行列のスキャンを開始し、現在の行または列のいずれかにテーブル ヘッダーに 1 が含まれている場合は、各要素を 1 に設定します。 | (mat[0][col] == 1) ) 次に、mat[row][col] を 1 に設定します。

この時点で、内部マトリックスのすべてのセルを処理しましたが、テーブル ヘッダー自体はまだ処理していません。

  1. テーブル ヘッダーを処理します。matt[0][0] == 1 の場合、最初の列と最初の行のすべての要素を 1 に設定します。
  2. 終わり

時間計算量 O(2*((n-1)(m-1)+(n+m-1))、つまり O(2*n*m - (n+m) + 1)、つまり O(2* n*m) スペース O(1)

http://codepad.org/fycIyflwで私の実装を参照してください

于 2012-06-03T05:48:15.373 に答える
-1

別の解決策は、通常どおりマトリックスをスキャンし、最初の 1 でマトリックスを 4 つの象限に分割することです。次に、行と列を 1 に設定し、各象限を再帰的に処理します。象限のみをスキャンしている場合でも、列と行全体を設定してください。

于 2012-06-12T19:04:59.347 に答える
-1
public void setOnes(int [][] matrix){

    boolean [] row = new boolean [matrix.length]
    boolean [] col = new boolean [matrix[0].length]
    for (int i=0;i<matrix.length;i++){
        for(int j=0;j<matrix[0].length;j++){
            if (matrix[i][j] == 1){
                row[i] = true
                col[j] = true
            }
        }
    }

    for (int i=0;i<matrix.length;i++){
        for(int j=0;j<matrix[0].length;j++){
            if (row[i] || col[j]){
                matrix[i][j] = 1;
            }
        }
    }
}
于 2014-01-16T06:39:27.080 に答える