单调栈结构
一种特别设计的栈结构,为了解决如下的问题:
给定一个可能含有重复值的数组arr,i位置的数一定存在如下两个信息
1)arr[i]的左侧离i最近并且小于(或者大于)arr[i]的数在哪?
2)arr[i]的右侧离i最近并且小于(或者大于)arr[i]的数在哪?
如果想得到arr中所有位置的两个信息,怎么能让得到信息的过程尽量快。
单调栈实现
public static int[][] getNearLessNoRepeat(int[] arr) {
int[][] ans = new int[arr.length][2];
Stack<Integer> stack = new Stack<>();
for (int i = 0; i < arr.length; i++) {
while (!stack.empty() && arr[i] < arr[stack.peek()]) {
int index = stack.pop();
ans[index][0] = stack.empty() ? -1 : stack.peek();
ans[index][1] = i;
}
stack.push(i);
}
while (!stack.empty()) {
int index = stack.pop();
ans[index][0] = stack.empty() ? -1 : stack.peek();
ans[index][1] = -1;
}
return ans;
}
public static int[][] getNearLess(int[] arr) {
int[][] ans = new int[arr.length][2];
Stack<LinkedList<Integer>> stack = new Stack<>();
for (int i = 0; i < arr.length; i++) {
while (!stack.empty() && arr[i] < arr[stack.peek().peekLast()]) {
LinkedList<Integer> list = stack.pop();
int leftIndex = stack.empty() ? -1 : stack.peek().peekLast();
for (int index : list) {
ans[index][0] = leftIndex;
ans[index][1] = i;
}
}
if (!stack.empty() && arr[i] == arr[stack.peek().peekLast()]) {
stack.peek().offerLast(i);
} else {
LinkedList<Integer> list = new LinkedList<>();
list.offerLast(i);
stack.push(list);
}
}
while (!stack.empty()) {
LinkedList<Integer> list = stack.pop();
int leftIndex = stack.empty() ? -1 : stack.peek().peekLast();
for (int index : list) {
ans[index][0] = leftIndex;
ans[index][1] = -1;
}
}
return ans;
}
单调栈题目
题目1:满足要求的子数组的最大值
给定一个只包含正数的数组arr,arr中任何一个子数组sub,一定都可以算出(sub累加和 )* (sub中的最小值)是什么,那么所有子数组中,这个值最大是多少?
public static int maxValue2(int[] arr) {
if (arr == null || arr.length == 0) {
return Integer.MIN_VALUE;
}
int ans = Integer.MIN_VALUE;
for (int i = 0; i < arr.length; i++) {
for (int j = i; j < arr.length; j++) {
int min = Integer.MAX_VALUE;
int sum = 0;
for (int k = i; k <= j; k++) {
min = Math.min(min, arr[k]);
sum += arr[k];
}
ans = Math.max(ans, sum * min);
}
}
return ans;
}
public static int maxValue(int[] arr) {
if (arr == null || arr.length == 0) {
return Integer.MIN_VALUE;
}
int n = arr.length;
int[] preSum = new int[n];
preSum[0] = arr[0];
for (int i = 1; i < n; i++) {
preSum[i] = preSum[i - 1] + arr[i];
}
return process(arr, preSum);
}
private static int process(int[] arr, int[] preSum) {
int ans = 0;
int[] stack = new int[arr.length];
int cursor = -1;
for (int i = 0; i < arr.length; i++) {
while (cursor != -1 && arr[stack[cursor]] >= arr[i]) {
int index = stack[cursor--];
int left = cursor == -1 ? -1 : stack[cursor];
int right = i;
ans = left == -1 ? Math.max(ans, arr[index] * preSum[right - 1]) : Math.max(ans, arr[index] * (preSum[right - 1] - preSum[left]));
}
stack[++cursor] = i;
}
while (cursor != -1) {
int index = stack[cursor--];
int left = cursor == -1 ? -1 : stack[cursor];
int right = arr.length;
ans = left == -1 ? Math.max(ans, arr[index] * preSum[right - 1]) : Math.max(ans, arr[index] * (preSum[right - 1] - preSum[left]));
}
return ans;
}
题目2:返回直方图的最大长方形面积
给定一个非负数组arr,代表直方图,返回直方图的最大长方形面积
https://leetcode.com/problems/largest-rectangle-in-histogram
public static int largestRectangleArea(int[] heights) {
if (heights == null || heights.length == 0) {
return 0;
}
int n = heights.length;
int[] stack = new int[n];
int cursor = -1;
int area = 0;
for (int i = 0; i < n; i++) {
while (cursor != -1 && heights[stack[cursor]] >= heights[i]) {
int index = stack[cursor--];
int left = cursor == -1 ? -1 : stack[cursor];
int right = i;
area = left == -1 ? Math.max(area, heights[index] * right) : Math.max(area, heights[index] * (right - 1 - left));
}
stack[++cursor] = i;
}
while (cursor != -1) {
int index = stack[cursor--];
int left = cursor == -1 ? -1 : stack[cursor];
int right = n;
area = left == -1 ? Math.max(area, heights[index] * right) : Math.max(area, heights[index] * (right - 1 - left));
}
return area;
}
题目3:全部由1组成的最大子矩形,内部有多少个1
给定一个二维数组matrix,其中的值不是0就是1,返回全部由1组成的最大子矩形,内部有多少个1
https://leetcode.com/problems/maximal-rectangle/submissions/
public static int maximalRectangle(char[][] matrix) {
if (matrix == null || matrix.length == 0 || matrix[0] == null || matrix[0].length == 0) {
return 0;
}
int n = matrix.length;
int m = matrix[0].length;
int[] height = new int[m];
int area = 0;
for (char[] row : matrix) {
for (int i = 0; i < m; i++) {
height[i] = row[i] == '0' ? 0 : height[i] + 1;
}
area = Math.max(area, largestRectangleArea(height));
}
return area;
}
private static int largestRectangleArea(int[] heights) {
int n = heights.length;
int[] stack = new int[n];
int cursor = -1;
int area = 0;
for (int i = 0; i < n; i++) {
while (cursor != -1 && heights[stack[cursor]] >= heights[i]) {
int index = stack[cursor--];
int left = cursor == -1 ? -1 : stack[cursor];
int right = i;
area = Math.max(area, heights[index] * (right - 1 - left));
}
stack[++cursor] = i;
}
while (cursor != -1) {
int index = stack[cursor--];
int left = cursor == -1 ? -1 : stack[cursor];
int right = n;
area = Math.max(area, heights[index] * (right - 1 - left));
}
return area;
}
题目4:矩形数量
给定一个二维数组matrix,其中的值不是0就是1,返回全部由1组成的子矩形数量
https://leetcode.com/problems/count-submatrices-with-all-ones/submissions/
public static int numSubmat(int[][] mat) {
if (mat == null || mat.length == 0 || mat[0] == null || mat.length == 0) {
return 0;
}
int m = mat[0].length;
int[] heights = new int[m];
int num = 0;
for (int[] rows : mat) {
for (int i = 0; i < m; i++) {
heights[i] = rows[i] == 0 ? 0 : heights[i] + 1;
}
num += rectNum(heights);
}
return num;
}
private static int rectNum(int[] height) {
if (height == null || height.length == 0) {
return 0;
}
int n = height.length;
int[] stack = new int[n];
int num = 0;
int cursor = -1;
for (int i = 0; i < n; i++) {
while (cursor != -1 && height[stack[cursor]] >= height[i]) {
int index = stack[cursor--];
int left = cursor == -1 ? -1 : stack[cursor];
int right = i;
int maxMinIndex = left == -1 ? right : height[left] > height[right] ? left : right;
num += (height[index] - height[maxMinIndex]) * num(right - left - 1);
}
stack[++cursor] = i;
}
while (cursor != -1) {
int index = stack[cursor--];
int left = cursor == -1 ? -1 : stack[cursor];
int right = n;
int maxMinIndex = left == -1 ? -1 : left;
int down = maxMinIndex == -1 ? 0 : height[left];
num += (height[index] - down) * num(right - left - 1);
}
return num;
}
private static int num(int n) {
return (n * (n + 1)) >> 1;
}
题目5:所有子数组最小值的累加和
给定一个数组arr,返回所有子数组最小值的累加和
https://leetcode.com/problems/sum-of-subarray-minimums/submissions/
public static int sumSubarrayMins(int[] arr) {
int n = arr.length;
int[] stack = new int[n];
int cursor = -1;
long sum = 0L;
for (int i = 0; i < n; i++) {
while (cursor != -1 && arr[stack[cursor]] >= arr[i]) {
int index = stack[cursor--];
int left = cursor == -1 ? -1 : stack[cursor];
int right = i;
int num = (index - left) * (right - index);
sum += (num * (long) arr[index]);
sum %= 1000000007;
}
stack[++cursor] = i;
}
while (cursor != -1) {
int index = stack[cursor--];
int left = cursor == -1 ? -1 : stack[cursor];
int right = n;
int num = (index - left) * (right - index);
sum += (num * (long) arr[index]);
sum %= 1000000007;
}
return (int) sum;
}