Matrix Fill - Microsoft transfrom boolean matrix



 * Author: Keith Schwarz (htiek@cs.stanford.edu)
 *
 * The question in this problem is the following: you are given a boolean
 * matrix of dimensions m x n. You want to transform the matrix to produce a
 * new matrix according to the following rule: entry (i, j) should be true
 * (we'll denote it '1') if there was a 1 anywhere on row i or column j, and
 * should be 0 otherwise. For example, given this input matrix:
 *                                0 0 0 1 0
 *                                1 0 0 0 0
 *                                0 0 0 0 0
 *                                0 0 0 1 0
 *
 * the output would be
 *
 *                                1 1 1 1 1
 *                                1 1 1 1 1
 *                                1 0 0 1 0
 *                                1 1 1 1 1
* The challenge is the following: is it possible to do this in time O(mn)
 * and space only O(1)? That is, can you solve this problem in constant space
 * and linear time? 
 *
 * The initial problem with trying to solve this in O(1) space is that if we
 * start changing entries in the matrix from 0 to 1 as we begin filling in the
 * matrix, we might end up confusing ourselves later between the case where the
 * element was originally 1 (meaning we should fill its row and column with 1s)
 * and the case where the element was originally 0 (meaning that we shouldn't.)

* To motivate the correct solution, let's begin with a solution that uses
 * O(n) space rather than O(1) space.
 *
 * If you think about it, for each row and each column, we only need to store
 * one bit of information: should that column be filled or not? Therefore, one
 * simple solution would be the following:
 *
 * 1. Create an array of size m storing which rows contain 1s. This can be
 *    filled in in time O(m) and uses O(m) space.
 * 2. Create an array of size n storing which columns contain 1s. This can be
 *    filled in time O(n) and uses O(n) space.
 * 3. For each (i, j), set position (i, j) to 1 if entry i in the row array or
 *    entry j in the column array is a 1. This also takes only O(mn) time.
 *
 * This overall approach will use time O(mn), but needs space O(m + n), which
 * is too much for what we're trying to do.
 *
 * However, we can reduce the space usage down to O(n) by using a clever
 * observation. Start as before by filling in an array that marks which
 * columns contain 1s. However, instead of creating a secondary array to store
 * this information for rows, instead just iterate across each row, filling it
 * with 1s if any of its entries are 1. After doing this, use the auxiliary
 * array with information about the marked columns to fill in the columns that
 * contain 1s. Why does this work? Well, if an element is a 0 in the result,
 * it means that there were no 1s in its row and no 1s in its column, so it
 * should indeed be a 0. If the element is a 1, then there was either a 1 in
 * its row or a 1 in its column, so it should indeed be a 1.
 *
 * As an example, let's trace this algorithm on this input array:
 *
 *                                0 0 0 1 0
 *                                1 0 0 0 0
 *                                0 0 0 0 0
 *                                0 0 0 1 0
 *
 * We start off by making an auxiliary array for the columns and filling it in:
 *
 *                                0 0 0 1 0
 *                                1 0 0 0 0
 *                                0 0 0 0 0
 *                                0 0 0 1 0
 *
 *                                1 0 0 1 0 (aux array)
 *
 * Next, we fill in each row containing a 1:
 *
 *                                1 1 1 1 1
 *                                1 1 1 1 1
 *                                0 0 0 0 0
 *                                1 1 1 1 1
 *
 *                                1 0 0 1 0 (aux array)
 *
 * Finally, we fill each column with a 1 in the aux array:
 *
 *                                1 1 1 1 1
 *                                1 1 1 1 1
 *                                1 0 0 1 0
 *                                1 1 1 1 1
 *
 *                                1 0 0 1 0 (aux array)
 *
 * and we're done!
 *
 * This approach is more memory-efficient than before, but it's still using
 * too much memory. We need to get it down to O(1) memory, not O(n).

* This is where we can use a really clever trick. Let's begin with an
 * observation: if every row in the matrix contains a 1, then the resulting
 * matrix will all be 1's. Therefore, we can start off our search by checking
 * if all the rows have a 1 in them and, if so, filling in the entire matrix.
 *
 * If, however, some row is all zeros, we know that in the "fill in each row
 * that contains a 1" step, the row won't be touched. In fact, the only way
 * that any entries on this row will get set to 1 is if those entries are in
 * columns that contain 1s. But wait - that sounds a lot like our auxiliary
 * array, which should only have 1s in columns containing 1s! This leads us to
 * the most important insight of the algorithm: we can treat one of the rows of
 * zeros as our auxiliary array, meaning that we don't need to allocate a new
 * auxiliary array!
 *
 * The new algorithm is pretty much the same as before, but with the auxiliary
 * array packed into the matrix itself. Let's do an example:
 *
 *                                1 0 0 1 0 1
 *                                0 0 0 0 0 0
 *                                1 0 0 0 0 0
 *                                0 0 0 0 0 0
 *                                0 0 0 1 0 1
 *
 * We start by scanning to find a row of all 0s to use as our auxiliary array.
 * This is shown here:
 *
 *                                1 0 0 1 0 1
 *                                0 0 0 0 0 0 -- (aux)
 *                                1 0 0 0 0 0
 *                                0 0 0 0 0 0
 *                                0 0 0 1 0 1
 *
 * Now, we fill in the auxiliary array, as before:
 *
 *                                1 0 0 1 0 1
 *                                1 0 0 1 0 1 -- (aux)
 *                                1 0 0 0 0 0
 *                                0 0 0 0 0 0
 *                                0 0 0 1 0 1
 *
 * Next, we fill in each row in the matrix containing at least one 1, *except*
 * for the auxiliary array row (after all, it's really all 0's. We're just
 * appropriating the bits for other purposes). This is shown here:
 *
 *                                1 1 1 1 1 1
 *                                1 0 0 1 0 1 -- (aux)
 *                                1 1 1 1 1 1
 *                                0 0 0 0 0 0
 *                                1 1 1 1 1 1
 *
 * Finally, using the auxiliary array, we fill in each column that contained a
 * 1 in the initial array:
 *
 *                                1 1 1 1 1 1
 *                                1 0 0 1 0 1 -- (aux)
 *                                1 1 1 1 1 1
 *                                1 0 0 1 0 1
 *                                1 1 1 1 1 1
 *
 * And we're done! This whole process takes time O(mn) because we're making a
 * constant number of passes over the original matrix and only uses O(1) space
 * (enough to hold the index of the auxiliary array and incidental loop
 * variables).

public class MatrixFill {
    /* This class isn't meant to be instantiated. */
    private MatrixFill() {
        // Do nothing    }

    /**
     * Given a boolean matrix of 0s and 1s, transforms the grid by setting each
     * entry that was in the same row or column as a 1 into a 1. The
     * algorithm used here uses only O(1) space and runs in linear time.
     *
     * @param matrix The matrix, which is modified by the algorithm.
     */

    public static void fill(boolean[][] matrix) {
        /* Start by finding a row to use as an auxiliary array. If one can't
         * be found, that's great! The whole matrix should be 1s and we're
         * done. Note that if the matrix has no rows, there will be no aux row.
         */

        int auxRow = findAuxiliaryRow(matrix);
        if (auxRow == -1) {
            fillMatrixWithOnes(matrix);
            return;
        }

        /* Now, populate the auxiliary array by scanning through the columns
         * and marking which ones have 1s in them.
         */

        populateAuxiliaryRow(matrix, auxRow);

        /* Next, fill in each row (except the auxiliary row) that contains a 1
         * with 1s.
         */

        fillRowsWithOnes(matrix, auxRow);

        /* Finally, fill in each column that originally contained a 1 with
         * 1s.
         */

        fillColumnsWithOnes(matrix, auxRow);
    }

    /**
     * Given a boolean matrix, finds a row of all 0s in that matrix, returning
     * -1 as a sentinel if one is not found. This algorithm runs in time O(mn) and
     * uses only O(1) space.
     *
     * @param matrix The matrix in question.
     * @return The index of a row of all 0s, or -1 if none exists.
     */

    private static int findAuxiliaryRow(boolean[][] matrix) {
        for (int row = 0; row < matrix.length; row++) {
            if (isZeroRow(matrix, row)) return row;
        }
        return -1;
    }

    /**
     * Given a boolean matrix and a row in that matrix, returns whether every
     * entry in that row is 0. This method runs in O(n) time and uses O(1) space.
     *
     * @param matrix The matrix in question.
     * @param row The row index.
     * @return Whether every entry in that row is 0.
     */

    private static boolean isZeroRow(boolean[][] matrix, int row) {
        for (int col = 0; col < matrix[row].length; col++) {
            if (matrix[row][col]) return false;
        }
        return true;
    }

    /**
     * Given a boolean matrix and a column in that matrix, returns whether every
     * entry in that column is 0. This method runs in O(m) time and uses O(1)
     * space.
     *
     * @param matrix The matrix in question.
     * @param col The column index.
     * @return Whether every entry in that column is 0.
     */

    private static boolean isZeroColumn(boolean[][] matrix, int col) {
        for (int row = 0; row < matrix.length; row++) {
            if (matrix[row][col]) return false;
        }
        return true;
    }

    /**
     * Fills a boolean matrix with the value 1's (true's). This method runs in
     * O(mn) time and uses O(1) space.
     *
     * @param matrix The matrix to fill with 1's.
     */

    private static void fillMatrixWithOnes(boolean[][] matrix) {
        for (int row = 0; row < matrix.length; row++) {
            for (int col = 0; col < matrix[row].length; col++) {
                matrix[row][col] = true;
            }
        }
    }

    /**
     * Given a matrix and an auxiliary row (a row of all 0's), fills in the
     * auxiliary row with information about which columns contain 1's. This method
     * runs in time O(mn) and uses O(1) space.
     *
     * @param matrix The matrix in question.
     * @param auxRow The row number of the auxiliary row.
     */

    private static void populateAuxiliaryRow(boolean[][] matrix, int auxRow) {
        for (int col = 0; col < matrix[auxRow].length; col++) {
            if (!isZeroColumn(matrix, col)) matrix[auxRow][col] = true;
        }
    }

    /**
     * Given a matrix, fills each row in the matrix (except for the auxiliary row)
     * with 1's if any of the entries in that row are 1's. This methods runs in
     * time O(mn) and uses O(1) space.
     *
     * @param matrix The matrix to process.
     * @param auxRow The index of the auxiliary row.
     */

    private static void fillRowsWithOnes(boolean[][] matrix, int auxRow) {
        for (int row = 0; row < matrix.length; row++) {
            /* Skip the auxiliary row; the 1's in it are spurious. */
            if (row == auxRow) continue;

            if (!isZeroRow(matrix, row)) fillRow(matrix, row);
        }
    }

    /**
     * Given a matrix and a row in the matrix, fills that row of the matrix with
     * 1's. This method runs im time O(n) and uses O(1) space.
     *
     * @param matrix The matrix to process.
     * @param row The row number.
     */

    private static void fillRow(boolean[][] matrix, int row) {
        for (int col = 0; col < matrix[row].length; col++) {
            matrix[row][col] = true;
        }
    }

    /**
     * Given a matrix and a column in the matrix, fills that column of the matrix
     * with 1's. This method runs im time O(m) and uses O(1) space.
     *
     * @param matrix The matrix to process.
     * @param col The column number.
     */

    private static void fillColumn(boolean[][] matrix, int col) {
        for (int row = 0; row < matrix.length; row++) {
            matrix[row][col] = true;
        }
    }

    /**
     * Given a matrix and the row number of the auxiliary row, fills in every
     * column in the matrix with a 1 in the auxiliary row with 1's.
     *
     * @param matrix The matrix to process.
     * @param auxRow The index of the auxiliary row.
     */

    private static void fillColumnsWithOnes(boolean[][] matrix, int auxRow) {
        for (int col = 0; col < matrix[auxRow].length; col++) {
            if (matrix[auxRow][col]) fillColumn(matrix, col);
        }
    }
}

Labels

LeetCode (1432) GeeksforGeeks (1122) LeetCode - Review (1067) Review (882) Algorithm (668) to-do (609) Classic Algorithm (270) Google Interview (237) Classic Interview (222) Dynamic Programming (220) DP (186) Bit Algorithms (145) POJ (141) Math (137) Tree (132) LeetCode - Phone (129) EPI (122) Cracking Coding Interview (119) DFS (115) Difficult Algorithm (115) Lintcode (115) Different Solutions (110) Smart Algorithm (104) Binary Search (96) BFS (91) HackerRank (90) Binary Tree (86) Hard (79) Two Pointers (78) Stack (76) Company-Facebook (75) BST (72) Graph Algorithm (72) Time Complexity (69) Greedy Algorithm (68) Interval (63) Company - Google (62) Geometry Algorithm (61) Interview Corner (61) LeetCode - Extended (61) Union-Find (60) Trie (58) Advanced Data Structure (56) List (56) Priority Queue (53) Codility (52) ComProGuide (50) LeetCode Hard (50) Matrix (50) Bisection (48) Segment Tree (48) Sliding Window (48) USACO (46) Space Optimization (45) Company-Airbnb (41) Greedy (41) Mathematical Algorithm (41) Tree - Post-Order (41) ACM-ICPC (40) Algorithm Interview (40) Data Structure Design (40) Graph (40) Backtracking (39) Data Structure (39) Jobdu (39) Random (39) Codeforces (38) Knapsack (38) LeetCode - DP (38) Recursive Algorithm (38) String Algorithm (38) TopCoder (38) Sort (37) Introduction to Algorithms (36) Pre-Sort (36) Beauty of Programming (35) Must Known (34) Binary Search Tree (33) Follow Up (33) prismoskills (33) Palindrome (32) Permutation (31) Array (30) Google Code Jam (30) HDU (30) Array O(N) (29) Logic Thinking (29) Monotonic Stack (29) Puzzles (29) Code - Detail (27) Company-Zenefits (27) Microsoft 100 - July (27) Queue (27) Binary Indexed Trees (26) TreeMap (26) to-do-must (26) 1point3acres (25) GeeksQuiz (25) Merge Sort (25) Reverse Thinking (25) hihocoder (25) Company - LinkedIn (24) Hash (24) High Frequency (24) Summary (24) Divide and Conquer (23) Proof (23) Game Theory (22) Topological Sort (22) Lintcode - Review (21) Tree - Modification (21) Algorithm Game (20) CareerCup (20) Company - Twitter (20) DFS + Review (20) DP - Relation (20) Brain Teaser (19) DP - Tree (19) Left and Right Array (19) O(N) (19) Sweep Line (19) UVA (19) DP - Bit Masking (18) LeetCode - Thinking (18) KMP (17) LeetCode - TODO (17) Probabilities (17) Simulation (17) String Search (17) Codercareer (16) Company-Uber (16) Iterator (16) Number (16) O(1) Space (16) Shortest Path (16) itint5 (16) DFS+Cache (15) Dijkstra (15) Euclidean GCD (15) Heap (15) LeetCode - Hard (15) Majority (15) Number Theory (15) Rolling Hash (15) Tree Traversal (15) Brute Force (14) Bucket Sort (14) DP - Knapsack (14) DP - Probability (14) Difficult (14) Fast Power Algorithm (14) Pattern (14) Prefix Sum (14) TreeSet (14) Algorithm Videos (13) Amazon Interview (13) Basic Algorithm (13) Codechef (13) Combination (13) Computational Geometry (13) DP - Digit (13) LCA (13) LeetCode - DFS (13) Linked List (13) Long Increasing Sequence(LIS) (13) Math-Divisible (13) Reservoir Sampling (13) mitbbs (13) Algorithm - How To (12) Company - Microsoft (12) DP - Interval (12) DP - Multiple Relation (12) DP - Relation Optimization (12) LeetCode - Classic (12) Level Order Traversal (12) Prime (12) Pruning (12) Reconstruct Tree (12) Thinking (12) X Sum (12) AOJ (11) Bit Mask (11) Company-Snapchat (11) DP - Space Optimization (11) Dequeue (11) Graph DFS (11) MinMax (11) Miscs (11) Princeton (11) Quick Sort (11) Stack - Tree (11) 尺取法 (11) 挑战程序设计竞赛 (11) Coin Change (10) DFS+Backtracking (10) Facebook Hacker Cup (10) Fast Slow Pointers (10) HackerRank Easy (10) Interval Tree (10) Limited Range (10) Matrix - Traverse (10) Monotone Queue (10) SPOJ (10) Starting Point (10) States (10) Stock (10) Theory (10) Tutorialhorizon (10) Kadane - Extended (9) Mathblog (9) Max-Min Flow (9) Maze (9) Median (9) O(32N) (9) Quick Select (9) Stack Overflow (9) System Design (9) Tree - Conversion (9) Use XOR (9) Book Notes (8) Company-Amazon (8) DFS+BFS (8) DP - States (8) Expression (8) Longest Common Subsequence(LCS) (8) One Pass (8) Quadtrees (8) Traversal Once (8) Trie - Suffix (8) 穷竭搜索 (8) Algorithm Problem List (7) All Sub (7) Catalan Number (7) Cycle (7) DP - Cases (7) Facebook Interview (7) Fibonacci Numbers (7) Flood fill (7) Game Nim (7) Graph BFS (7) HackerRank Difficult (7) Hackerearth (7) Inversion (7) Kadane’s Algorithm (7) Manacher (7) Morris Traversal (7) Multiple Data Structures (7) Normalized Key (7) O(XN) (7) Radix Sort (7) Recursion (7) Sampling (7) Suffix Array (7) Tech-Queries (7) Tree - Serialization (7) Tree DP (7) Trie - Bit (7) 蓝桥杯 (7) Algorithm - Brain Teaser (6) BFS - Priority Queue (6) BFS - Unusual (6) Classic Data Structure Impl (6) DP - 2D (6) DP - Monotone Queue (6) DP - Unusual (6) DP-Space Optimization (6) Dutch Flag (6) How To (6) Interviewstreet (6) Knapsack - MultiplePack (6) Local MinMax (6) MST (6) Minimum Spanning Tree (6) Number - Reach (6) Parentheses (6) Pre-Sum (6) Probability (6) Programming Pearls (6) Rabin-Karp (6) Reverse (6) Scan from right (6) Schedule (6) Stream (6) Subset Sum (6) TSP (6) Xpost (6) n00tc0d3r (6) reddit (6) AI (5) Abbreviation (5) Anagram (5) Art Of Programming-July (5) Assumption (5) Bellman Ford (5) Big Data (5) Code - Solid (5) Code Kata (5) Codility-lessons (5) Coding (5) Company - WMware (5) Convex Hull (5) Crazyforcode (5) DFS - Multiple (5) DFS+DP (5) DP - Multi-Dimension (5) DP-Multiple Relation (5) Eulerian Cycle (5) Graph - Unusual (5) Graph Cycle (5) Hash Strategy (5) Immutability (5) Java (5) LogN (5) Manhattan Distance (5) Matrix Chain Multiplication (5) N Queens (5) Pre-Sort: Index (5) Quick Partition (5) Quora (5) Randomized Algorithms (5) Resources (5) Robot (5) SPFA(Shortest Path Faster Algorithm) (5) Shuffle (5) Sieve of Eratosthenes (5) Strongly Connected Components (5) Subarray Sum (5) Sudoku (5) Suffix Tree (5) Swap (5) Threaded (5) Tree - Creation (5) Warshall Floyd (5) Word Search (5) jiuzhang (5)

Popular Posts