单调栈
给定一个整数数组 temperatures ,表示每天的温度,返回一个数组 answer ,其中 answer[i] 是指对于第 i 天,下一个更高温度出现在几天后。如果气温在这之后都不会升高,请在该位置用 0 来代替。
思路:这题一开始思路就是两个for循环暴力解开,但时间超出了限制,那么使用单调栈。单调栈就是用于找出左边或右边第一个大或小的数值,以空间换时间,可以压缩到O(n)的时间复杂度。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| class Solution { public int[] dailyTemperatures(int[] temperatures) {
int lens=temperatures.length; int []res=new int[lens]; Deque<Integer> stack=new LinkedList<>(); stack.push(0); for(int i=1;i<lens;i++){
if(temperatures[i]<=temperatures[stack.peek()]){ stack.push(i); }else{ while(!stack.isEmpty()&&temperatures[i]>temperatures[stack.peek()]){ res[stack.peek()]=i-stack.peek(); stack.pop(); } stack.push(i); } }
return res; } }
|
nums1 中数字 x 的 下一个更大元素 是指 x 在 nums2 中对应位置 右侧 的 第一个 比 x 大的元素。
给你两个 没有重复元素 的数组 nums1 和 nums2 ,下标从 0 开始计数,其中nums1 是 nums2 的子集。
对于每个 0 <= i < nums1.length ,找出满足 nums1[i] == nums2[j] 的下标 j ,并且在 nums2 确定 nums2[j] 的 下一个更大元素 。如果不存在下一个更大元素,那么本次查询的答案是 -1 。
返回一个长度为 nums1.length 的数组 ans 作为答案,满足 ans[i] 是如上所述的 下一个更大元素 。
思路:这里的关键是如何联系起nums1和nums2,答案是使用hashmap,因为nums1和nums2都没有重复元素,因此可以建立hashmap,使用nums1[i]为key,i为value,建立起映射关系。这样就可以在两个数组上使用单调栈了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| class Solution { public int[] nextGreaterElement(int[] nums1, int[] nums2) { Stack<Integer> temp = new Stack<>(); int[] res = new int[nums1.length]; Arrays.fill(res,-1); HashMap<Integer, Integer> hashMap = new HashMap<>(); for (int i = 0 ; i< nums1.length ; i++){ hashMap.put(nums1[i],i); } temp.add(0); for (int i = 1; i < nums2.length; i++) { if (nums2[i] <= nums2[temp.peek()]) { temp.add(i); } else { while (!temp.isEmpty() && nums2[temp.peek()] < nums2[i]) { if (hashMap.containsKey(nums2[temp.peek()])){ Integer index = hashMap.get(nums2[temp.peek()]); res[index] = nums2[i]; } temp.pop(); } temp.add(i); } } return res; } }
|
给定一个循环数组 nums ( nums[nums.length - 1] 的下一个元素是 nums[0] ),返回 nums 中每个元素的 下一个更大元素 。
数字 x 的 下一个更大的元素 是按数组遍历顺序,这个数字之后的第一个比它更大的数,这意味着你应该循环地搜索它的下一个更大的数。如果不存在,则输出 -1 。
思路:这题就是典型的单调栈,唯一的难点是如何实现循环搜索。其实也很简单,只需要在第一个for循环后,如果还有剩余的stack,就进入第二个for循环,该循环的目的也是找出右边的下一个最大元素。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| class Solution { public int[] nextGreaterElements(int[] nums) { Stack<Integer> stack = new Stack<>(); int[] res = new int[nums.length]; Arrays.fill(res, -1); stack.add(0); for(int i = 1; i < nums.length; i++){ if(nums[i] <= nums[stack.peek()]){ stack.add(i); }else{ while(!stack.isEmpty() && nums[i] > nums[stack.peek()]){ res[stack.peek()] = nums[i]; stack.pop(); } stack.add(i); } } if(!stack.isEmpty()){ for(int i = 0; i < nums.length; i++){ while(!stack.isEmpty() && nums[i] > nums[stack.peek()]){ res[stack.peek()] = nums[i]; stack.pop(); } } } return res;
} }
|
给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。

思路:这题要学会化整为零,关键是找到每一个元素的高度能接多少雨水,而不是跳着接。直接的想法是双指针,该元素能接多少水,是由其左右两边第一个高过它的元素决定的,具体取决于哪个,就看哪个小了,可以用两个数组记录。第二种思路是单调栈,这种方法比较复杂,但思路也差不多,用一个递增的单调栈和一个判断当前元素是否大于栈顶元素的循环,来模拟左右两边第一个高过当前元素的元素。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71
| class Solution { public int trap(int[] height) { int length = height.length; if (length <= 2) return 0; int[] maxLeft = new int[length]; int[] maxRight = new int[length];
maxLeft[0] = height[0]; for (int i = 1; i< length; i++) maxLeft[i] = Math.max(height[i], maxLeft[i-1]);
maxRight[length - 1] = height[length - 1]; for(int i = length - 2; i >= 0; i--) maxRight[i] = Math.max(height[i], maxRight[i+1]);
int sum = 0; for (int i = 0; i < length; i++) { int count = Math.min(maxLeft[i], maxRight[i]) - height[i]; if (count > 0) sum += count; } return sum; } }
class Solution { public int trap(int[] height){ int size = height.length; if (size <= 2) return 0; Stack<Integer> stack = new Stack<Integer>(); stack.push(0); int sum = 0; for (int index = 1; index < size; index++){ int stackTop = stack.peek(); if (height[index] < height[stackTop]){ stack.push(index); }else if (height[index] == height[stackTop]){ stack.pop(); stack.push(index); }else{ int heightAtIdx = height[index]; while (!stack.isEmpty() && (heightAtIdx > height[stackTop])){ int mid = stack.pop();
if (!stack.isEmpty()){ int left = stack.peek(); int h = Math.min(height[left], height[index]) - height[mid]; int w = index - left - 1; int hold = h * w; if (hold > 0) sum += hold; stackTop = stack.peek(); } } stack.push(index); } }
return sum; } }
|
给定 n 个非负整数,用来表示柱状图中各个柱子的高度。每个柱子彼此相邻,且宽度为 1 。
求在该柱状图中,能够勾勒出来的矩形的最大面积。

思路:这题和接雨水非常像,唯一的区别在于接雨水是找左右两边离元素最近的第一个大于该元素的值,而该题是找左右两边离元素最远的小值。双指针解法比较通俗易懂,主要是注意while来不断寻找第一个小值,用result来保存最大的矩形值。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74
| class Solution { public int largestRectangleArea(int[] heights) { int length = heights.length; int[] minLeftIndex = new int [length]; int[] minRightIndex = new int [length]; minLeftIndex[0] = -1 ; for (int i = 1; i < length; i++) { int t = i - 1; while (t >= 0 && heights[t] >= heights[i]) t = minLeftIndex[t]; minLeftIndex[i] = t; } minRightIndex[length - 1] = length; for (int i = length - 2; i >= 0; i--) { int t = i + 1; while(t < length && heights[t] >= heights[i]) t = minRightIndex[t]; minRightIndex[i] = t; } int result = 0; for (int i = 0; i < length; i++) { int sum = heights[i] * (minRightIndex[i] - minLeftIndex[i] - 1); result = Math.max(sum, result); } return result; } }
class Solution { int largestRectangleArea(int[] heights) { Stack<Integer> st = new Stack<Integer>(); int [] newHeights = new int[heights.length + 2]; newHeights[0] = 0; newHeights[newHeights.length - 1] = 0; for (int index = 0; index < heights.length; index++){ newHeights[index + 1] = heights[index]; }
heights = newHeights; st.push(0); int result = 0; for (int i = 1; i < heights.length; i++) { if (heights[i] > heights[st.peek()]) { st.push(i); } else if (heights[i] == heights[st.peek()]) { st.pop(); st.push(i); } else { while (heights[i] < heights[st.peek()]) { int mid = st.peek(); st.pop(); int left = st.peek(); int right = i; int w = right - left - 1; int h = heights[mid]; result = Math.max(result, w * h); } st.push(i); } } return result; } }
|