數組牆 最詳細的解題報告

題目

隨機給定一個整型數組,每一個數組中的數字表明數組所在位置牆的高度,問這個數組所能拼湊的最大矩形牆的面積爲多少。java

示例

  • 輸入:{2, 1, 6, 5, 4, 7, 2}
  • 輸出:16

提示

數組{2, 1, 6, 5, 4, 7, 2}能夠描述爲:算法

2 1 6 5 4 7 2
\(\color{#000000}{*}\)
\(\color{#000000}{*}\) \(\color{#000000}{*}\)
\(\color{#000000}{*}\) \(\color{#000000}{*}\) \(\color{#000000}{*}\)
\(\color{#FF3030}{*}\) \(\color{#FF3030}{*}\) \(\color{#FF3030}{*}\) \(\color{#FF3030}{*}\)
\(\color{#FF3030}{*}\) \(\color{#FF3030}{*}\) \(\color{#FF3030}{*}\) \(\color{#FF3030}{*}\)
\(\color{#000000}{*}\) \(\color{#FF3030}{*}\) \(\color{#FF3030}{*}\) \(\color{#FF3030}{*}\) \(\color{#FF3030}{*}\) \(\color{#000000}{*}\)
\(\color{#000000}{*}\) \(\color{#000000}{*}\) \(\color{#FF3030}{*}\) \(\color{#FF3030}{*}\) \(\color{#FF3030}{*}\) \(\color{#FF3030}{*}\) \(\color{#000000}{*}\)

其中,數組

  • 第一行的數字表示數組中對應的值
  • 每一列中\(\color{#000000}{*}\)的個數加上\(\color{#FF3030}{*}\)的個數之和等於第一行中數組的值
  • \(\color{#FF3030}{*}\)表示最大的矩形牆面積

解題思路

  1. 將原數組array複製一份到數組copy
  2. 新建一個數組area用來存儲包含當前列的最大面積,初始值爲0
  3. 將數組copy0元素切割成多個子數組用Coordinate對象來表示,其中Coordinate.startIndex表示子數組的起始下標,Coordinate.endIndex表示子數組的結束下標(但不包含),Coordinate.minValue表示子數組的最小值
  4. 將數組copy[i]中的值減去Coordinate.minValue,其中Coordinate.startIndex <= i < Coordinate.endIndex,根據數組area的值area[i] = Math.max(area[i], (array[i] - copy[i]) * (Coordinate.endIndex - Coordinate.startIndex))
  5. 重複3)4)遍歷全部的Coordinate對象,直到按0元素切割不能得到有效Coordinate

具體算法(Java版)

public class FindMaxRectangle {

    public static void main(String[] args) {
        // 隨機生成數組
        int[] array = generateArray(20);
        // 將數組打印出來
        System.out.println("當前數組:" + arrayToString(array));
        // 打印數組牆
        printArray(array);
        // 輸出最大矩形牆的面積
        System.out.println("數組最大矩形牆的面積爲:" + findMax(array));
    }

    /**
     * 查找最大矩形牆的面積
     */
    private static int findMax(int[] array) {
        int[] copy = new int[array.length]; // 記錄運行時數組中的值
        int[] area = new int[array.length]; // 記錄最大矩形牆的面積
        Queue<List<Coordinate>> queue = new LinkedList<>();
        for (int i = 0; i < array.length; i++) {
            copy[i] = array[i];
            area[i] = 0;
        }
        queue.offer(divideArray(copy));
        while (!queue.isEmpty()) {
            List<Coordinate> coordinates = queue.poll();
            // 沒有任何有效的子數組
            if (coordinates.size() == 0)
                break;
            for (Coordinate coordinate : coordinates) {
                for (int i = coordinate.getStartIndex(); i < coordinate.getEndIndex(); i++) {
                    if (copy[i] > 0) {
                        // 減去最小值minValue
                        copy[i] -= coordinate.getMinValue();
                    }
                    // 計算子數組對應的最大矩陣牆面積
                    int value = (array[i] - copy[i]) *
                            (coordinate.getEndIndex() - coordinate.getStartIndex());
                    // 更新最大矩陣牆的面積
                    area[i] = Math.max(value, area[i]);
                }
            }
            queue.offer(divideArray(copy));
        }
        // 查找最大的矩陣牆的面積
        int maxArea = 0;
        for (int i = 0; i < area.length; i++) {
            if (maxArea < area[i]) {
                maxArea = area[i];
            }
        }
        return maxArea;
    }

    /**
     * 將數組按照0元素切分紅多個子數組,子數組用Coordinate對象來記錄
     */
    private static List<Coordinate> divideArray(int[] array) {
        List<Coordinate> coordinates = new ArrayList<>();
        int startIndex = -1, endIndex = -1, minValue = Integer.MAX_VALUE;
        for (int i = 0; i < array.length; i++) {
            if (array[i] != 0) {
                if (startIndex == -1) {
                    startIndex = i;
                }
                if (array[i] < minValue) {
                    minValue = array[i];
                }
            } else {
                if (startIndex != -1) {
                    endIndex = i;
                    coordinates.add(new Coordinate(startIndex, endIndex, minValue));
                    startIndex = -1;
                    minValue = Integer.MAX_VALUE;
                }
            }
        }
        if (startIndex != -1) {
            coordinates.add(new Coordinate(startIndex, array.length, minValue));
        }
        return coordinates;
    }

    /**
     * 隨機生成指定長度的數組
     */
    private static int[] generateArray(int length) {
        int[] array = new int[length];
        Random random = new Random();
        for (int i = 0; i < array.length; i++) {
            int value = random.nextInt(10);
            if (value < 0) {
                value = 0;
            }
            array[i] = value;
        }
        return array;
    }

    /**
     * 打印成數組牆
     */
    private static void printArray(int[] array) {
        int max = 0;
        for (int i = 0; i < array.length; i++) {
            if (max < array[i]) {
                max = array[i];
            }
        }
        for (int i = max; i > 0; i--) {
            for (int j = 0; j < array.length; j++) {
                if (array[j] >= i) {
                    System.out.print("* ");
                } else {
                    System.out.print("  ");
                }
            }
            System.out.println();
        }
    }

    /**
     * 將數組轉換成字符串
     */
    private static String arrayToString(int[] array) {
        StringBuilder stringBuilder = new StringBuilder();
        stringBuilder.append("[");
        for (int i = 0; i < array.length; i++) {
            stringBuilder.append(array[i]);
            if (i != array.length - 1) {
                stringBuilder.append(", ");
            }
        }
        stringBuilder.append("]");
        return stringBuilder.toString();
    }

    /**
     * 用來記錄子數組信息
     */
    static class Coordinate {
        /**
         * @param startIndex 數組的起始下標
         * @param endIndex 數組的結束下標(不包含)
         * @param minValue 數組中的最小值
         */
        public Coordinate(int startIndex, int endIndex, int minValue) {
            this.startIndex = startIndex;
            this.endIndex = endIndex;
            this.minValue = minValue;
        }

        private int startIndex; // 數組的起始下標
        private int endIndex; // 數組的結束下標(不包含)
        private int minValue; // 數組中的最小值

        public int getStartIndex() {
            return startIndex;
        }

        public int getEndIndex() {
            return endIndex;
        }

        public int getMinValue() {
            return minValue;
        }
    }
}

輸出結果

運行結果

若是你們有什麼更好的方法或者發現代碼中存在bug但願能夠一塊兒交流討論!app

相關文章
相關標籤/搜索