1 理论基础
- 单调栈适用于求解数组中某个元素左边或者右边比它小或者大的的第一个元素的位置
- 单调栈的含义就是一个栈里面元素是单调递增或者单调递减的栈,一般存放数组的下标
2. 每日温度
求解数组中第一个大于target的元素距离target的距离
//单调栈
class Solution {
public int[] dailyTemperatures(int[] temperatures) {
int[] res = new int[temperatures.length];
Stack<Integer> stack = new Stack<>();
stack.push(0);
for(int i = 1; i < temperatures.length; i++) {
//栈顶下标对应的数组元素小于等于当前下标的数组元素,元素下标入栈
if(temperatures[stack.peek()] >= temperatures[i]) {
stack.push(i);
}
//大于的时候就获取栈顶当前数组下标和栈顶元素下标的差值
else{
while(!stack.isEmpty() && temperatures[stack.peek()] < temperatures[i]) {
res[stack.peek()] = i - stack.peek();
stack.pop();
}
stack.push(i); //这里将元素拿出来了之后需要加入下一个元素
}
}
return res;
}
}
3. 下一个更大元素
class Solution {
public int[] nextGreaterElement(int[] nums1, int[] nums2) {
//先做一个map映射,以便于在nums2中寻找nums1
HashMap<Integer, Integer> map = new HashMap<>();
for(int i = 0; i < nums1.length; i++) {
map.put(nums1[i], i);
}
int len = nums1.length;
int[] res = new int[len]; //用于存储最终结果
Arrays.fill(res, -1); //初始化为-1
Stack<Integer> stack = new Stack<>();
stack.push(0);
/****************
先入栈,下一个比栈顶下标对应的nums2元素小,就入栈,
比栈顶小标对应的元素大就找到栈顶下标对应的nums1的
索引,将当前大的元素赋值给找到的索引下标数组
*****************/
for(int i = 1; i < nums2.length; i++) {
if(nums2[i] <= nums2[stack.peek()]) {
stack.push(i);
}
else{
while(!stack.isEmpty() && nums2[i] > nums2[stack.peek()]) {
//map.get():根据key访问value
if(map.containsKey(nums2[stack.peek()])) {
int key = map.get(nums2[stack.peek()]);
res[key] = nums2[i];
}
stack.pop();
}
stack.push(i);
}
}
return res;
}
}
4. 求解循环数组的下一个最大元素
//先将数组复制一遍,组成一个为原来两倍的新数组
//然后利用这个新数组,使用单调栈寻找后面比它大
//的元素,不过时间复杂度明显较高
class Solution {
public int[] nextGreaterElements(int[] nums) {
int len = nums.length;
int[] copyNums = new int[2 * nums.length];
for(int i = 0; i < copyNums.length; i++) {
if(i < len) {
copyNums[i] = nums[i];
}
else{
copyNums[i] = nums[i - len];
}
}
int[] temp = new int[2*len];
Arrays.fill(temp, -1);
int[] res = new int[len];
Arrays.fill(res, -1);
Stack<Integer> stack = new Stack<>();
stack.push(0);
for(int i = 1; i < copyNums.length; i++) {
if(copyNums[i] <= copyNums[stack.peek()]) {
stack.push(i);
}
else{
while(!stack.isEmpty() && copyNums[i] > copyNums[stack.peek()]) {
temp[stack.peek()] = copyNums[i];
stack.pop();
}
stack.push(i);
}
}
for(int i = 0; i < len ; i++) {
res[i] = temp[i];
}
return res;
}
}
//简化方法
class Solution {
public int[] nextGreaterElements(int[] nums) {
if(nums == null || nums.length <= 1) {
return new int[]{-1};
}
Stack<Integer> stack = new Stack<>();
int len = nums.length;
int[] res = new int[len];
Arrays.fill(res, -1);
for(int i = 0; i < 2 * len; i++) {
while(!stack.isEmpty() && nums[i % len] > nums[stack.peek()]) {
res[stack.peek()] = nums[i % len]; //处理环的问题
stack.pop();
}
stack.push(i % len);
}
return res;
}
}
5. 接雨水 :面试常考
//解题关键在于栈顶元素下一个是它左边比它大的一个元素
//当前遍历到的比栈顶元素大的话它就是其右边比它大的第一个元素
class Solution {
public int trap(int[] height) {
if(height == null || height.length == 0) {
return 0;
}
int waterSquare = 0;
Stack<Integer> stack = new Stack<>();
stack.push(0);
for(int i = 1; i < height.length; i++) {
if(height[stack.peek()] >= height[i]) {
stack.push(i);
}
else{
while(!stack.isEmpty() && height[stack.peek()] < height[i]) {
int mid = stack.pop();
if(!stack.isEmpty()) {
int h = Math.min(height[i], height[stack.peek()]) - height[mid];
int w = i - stack.peek() - 1;
waterSquare += h * w;
}
}
stack.push(i);
}
}
return waterSquare;
}
}
- 寻找柱状图中构成的最大矩形
//暴力解法: 超时
class Solution {
public int largestRectangleArea(int[] heights) {
int sum = 0;
for(int i = 0; i < heights.length; i++) {
int left = i;
int right = i;
//往左往右比较找到比当前大的值
for( ;left >= 0; left--) {
if(heights[left] < heights[i]) break;
}
for( ; right < heights.length; right++) {
if(heights[right] < heights[i]) break;
}
int w = right - left - 1;
int h = heights[i];
sum = Math.max(sum, w * h);
}
return sum;
}
}
//单调栈
```java
//单调栈
class Solution {
public int largestRectangleArea(int[] heights) {
if(heights == null && heights.length == 0) {
return 0;
}
//给数组尾加一个0,防止单调递减的情况永远不弹出
//给数组头加一个0,防止只有两个元素的时候不够处理
int len = heights.length;
int[] newHeights = new int[len + 2];
newHeights[0] = 0;
newHeights[len + 1] = 0;
for(int i = 1; i < len + 1; i++) {
newHeights[i] = heights[i - 1];
}
Stack<Integer> stack = new Stack<>();
stack.push(0);
int maxSquare = 0;
for(int i = 1; i < newHeights.length; i++) {
if(newHeights[i] >= newHeights[stack.peek()]) {
stack.push(i);
}
else{
while(!stack.isEmpty() && newHeights[i] < newHeights[stack.peek()]) {
int mid = stack.pop();
if(!stack.isEmpty()) {
int h = newHeights[mid];
int w = i - stack.peek() - 1;
maxSquare = Math.max(maxSquare, h * w);
}
}
stack.push(i);
}
}
return maxSquare;
}
}