题目
随机给定一个整型数组,每个数组中的数字代表数组所在位置墙的高度,问这个数组所能拼凑的最大矩形墙的面积为多少。
示例
- 输入:{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}{*}\)表示最大的矩形墙面积
解题思路
- 将原数组
array
复制一份到数组copy
- 新建一个数组
area
用来存储包含当前列的最大面积,初始值为0
- 将数组
copy
按0
元素切割成多个子数组用Coordinate
对象来表示,其中Coordinate.startIndex
表示子数组的起始下标,Coordinate.endIndex
表示子数组的结束下标(但不包含),Coordinate.minValue
表示子数组的最小值 - 将数组
copy[i]
中的值减去Coordinate.minValue
,其中Coordinate.startIndex <= i < Coordinate.endIndex
,根据数组area
的值area[i] = Math.max(area[i], (array[i] - copy[i]) * (Coordinate.endIndex - Coordinate.startIndex))
- 重复
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> 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 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 divideArray(int[] array) {
List 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希望可以一起交流讨论!