[data structure and algorithm] in-depth analysis of the solution idea and algorithm example of "maximum Rectangle"

Posted by solarisuser on Fri, 11 Feb 2022 15:49:53 +0100

1, Title Requirements

  • Given a two-dimensional binary matrix containing only 0 and 1 and the size of rows x cols, find the largest rectangle containing only 1 and return its area.
  • Example 1:

Input: matrix = [["1","0","1","0","0"],["1","0","1","1","1"],["1","1","1","1","1"],["1","0","0","1","0"]]
Output: 6
  • Example 2:
Input: matrix = []
Output: 0
  • Example 3:
Input: matrix = [["0"]]
Output: 0
  • Example 4:
Input: matrix = [["1"]]
Output: 1
  • Example 5:
Input: matrix = [["0","0"]]
Output: 0
  • Tips:
    • rows == matrix.length;
    • cols == matrix[0].length;
    • 1 <= row, cols <= 200;
    • matrix[i][j] is' 0 'or' 1 '.

2, Solving algorithm

① Brute force cracking

  • Traverse each point and find all matrix areas with this point as the lower right corner of the matrix. As shown below, orange is the currently traversed point, and then the matrix circled by the dotted box is one of them:

  • How to find such a matrix? As shown below, if you know the number of consecutive ones ending at this point, the problem becomes simple:

  • Specific steps:
    • A. First, find the rectangular area with height of 1, that is, its own number, such as orange 4 in the above figure, and the area is 4.
    • B. Then expand a row upward, increase the height by one, select the smallest number in the current column as the width of the matrix, and calculate the area, corresponding to the rectangular box in the figure above.
    • C. Then continue to expand upward and repeat step B.
  • According to the above method, traverse all points and find all matrices.
  • Take the orange point as the lower right corner:
    • Height 1:

    • Height 2:

    • Height 3:

  • Java example:
public int maximalRectangle(char[][] matrix) {
    if (matrix.length == 0) {
        return 0;
    }
    // Saves the number of consecutive ones ending with the current number
    int[][] width = new int[matrix.length][matrix[0].length];
    int maxArea = 0;
    // Traverse each line
    for (int row = 0; row < matrix.length; row++) {
        for (int col = 0; col < matrix[0].length; col++) {
            // Update width
            if (matrix[row][col] == '1') {
                if (col == 0) {
                    width[row][col] = 1;
                } else {
                    width[row][col] = width[row][col - 1] + 1;
                }
            } else {
                width[row][col] = 0;
            }
            // Record the minimum number of all rows
            int minWidth = width[row][col];
            // Expand row up
            for (int up_row = row; up_row >= 0; up_row--) {
                int height = row - up_row + 1;
                // Find the smallest number as the width of the matrix
                minWidth = Math.min(minWidth, width[up_row][col]);
                // Renewal area
                maxArea = Math.max(maxArea, height * minWidth);
            }
        }
    }
    return maxArea;
}

② Stack

  • It's not hard to think of it. Just find the heights [] of each layer and pass it to the function of the previous question.
  • Java example:
public int maximalRectangle(char[][] matrix) {
    if (matrix.length == 0) {
        return 0;
    }
    int[] heights = new int[matrix[0].length];
    int maxArea = 0;
    for (int row = 0; row < matrix.length; row++) {
        // Traverse each column and update the height
        for (int col = 0; col < matrix[0].length; col++) {
            if (matrix[row][col] == '1') {
                heights[col] += 1;
            } else {
                heights[col] = 0;
            }
        }
        // Update function
        maxArea = Math.max(maxArea, largestRectangleArea(heights));
    }
    return maxArea;
}

public int largestRectangleArea(int[] heights) {
    int maxArea = 0;
    Stack<Integer> stack = new Stack<>();
    int p = 0;
    while (p < heights.length) {
        // Stack empty stack
        if (stack.isEmpty()) {
            stack.push(p);
            p++;
        } else {
            int top = stack.peek();
            // The current height is greater than the top of the stack, and the stack
            if (heights[p] >= heights[top]) {
                stack.push(p);
                p++;
            } else {
                // Save stack top height
                int height = heights[stack.pop()];
                // The first one on the left is smaller than the subscript of the current column
                int leftLessMin = stack.isEmpty() ? -1 : stack.peek();
                // The first one on the right is smaller than the subscript of the current column
                int RightLessMin = p;
                // Calculated area
                int area = (RightLessMin - leftLessMin - 1) * height;
                maxArea = Math.max(area, maxArea);
            }
        }
    }
    while (!stack.isEmpty()) {
        // Save stack top height
        int height = heights[stack.pop()];
        // The first one on the left is smaller than the subscript of the current column
        int leftLessMin = stack.isEmpty() ? -1 : stack.peek();
        // There is no column less than the current height on the right, so it is assigned as the length of the array for easy calculation
        int RightLessMin = heights.length;
        int area = (RightLessMin - leftLessMin - 1) * height;
        maxArea = Math.max(area, maxArea);
    }
    return maxArea;
}

③ Dynamic programming

  • In solution ②, update the heights once, use the previous algorithm to find leftLessMin [] and rightLessMin [], and then update the area. In fact, leftLessMin [] and rightLessMin [] can be updated by using the previous leftLessMin [] and rightLessMin [].
  • Recall the meanings of leftLessMin [] and rightLessMin []. leftLessMin [i] represents the first subscript on the left that is lower than the current column, as shown in the following figure. When the orange column is the current traversed column, rightLessMin [] is the first on the right:

  • Left and right are symmetrical relations. Only the solution of left is considered below. As shown in the following figure, if all the newly added layers are 1, of course, this is the most perfect case, then leftLessMin [] does not need to be changed at all:

  • However, the fact is cruel. There must be 0:

  • Considering the update of the last column, leftLessMin = 1 on the upper floor, that is, the position of blue 0 is the first column lower than it. However, in the current layer, 0 appears in the middle. Therefore, it is no longer the previous leftLessMin, but compared with the position where 0 appeared last time (because 0 must be smaller than the current column). Whoever has a larger subscript and is closer to the current column will be selected. The position of 0 in the above figure is 2, and the previous leftLessMin is 1. Choose a larger one, that is 2.
  • Java example:
public int maximalRectangle4(char[][] matrix) {
    if (matrix.length == 0) {
        return 0;
    }
    int maxArea = 0;
    int cols = matrix[0].length;
    int[] leftLessMin = new int[cols];
    int[] rightLessMin = new int[cols];
    // Initialize to - 1, that is, the leftmost
    Arrays.fill(leftLessMin, -1); 
    // Initialize to cols, that is, the rightmost
    Arrays.fill(rightLessMin, cols); 
    int[] heights = new int[cols];
    for (int row = 0; row < matrix.length; row++) {
        // Update all heights
        for (int col = 0; col < cols; col++) {
            if (matrix[row][col] == '1') {
                heights[col] += 1;
            } else {
                heights[col] = 0;
            }
        }
		// Update all leftLessMin
        int boundary = -1; // Record the last occurrence of 0
        for (int col = 0; col < cols; col++) {
            if (matrix[row][col] == '1') {
                // Compare with the last occurrence of 0
                leftLessMin[col] = Math.max(leftLessMin[col], boundary);
            } else {
                // Currently 0 means the current height is 0, so it is initialized to - 1 to prevent the impact on the next cycle
                leftLessMin[col] = -1; 
                // Update the location of 0
                boundary = col;
            }
        }
        // The same is true on the right
        boundary = cols;
        for (int col = cols - 1; col >= 0; col--) {
            if (matrix[row][col] == '1') {
                rightLessMin[col] = Math.min(rightLessMin[col], boundary);
            } else {
                rightLessMin[col] = cols;
                boundary = col;
            }
        }
		
        // Update all areas
        for (int col = cols - 1; col >= 0; col--) {
            int area = (rightLessMin[col] - leftLessMin[col] - 1) * heights[col];
            maxArea = Math.max(area, maxArea);
        }
    }
    return maxArea;
}

Topics: data structure leetcode Dynamic Programming stack