代码随想录-刷题第五十六天
单调栈理论基础
先介绍单调栈类型的题目,通常是一维数组,要寻找任一个元素的右边或者左边第一个比自己大或者小的元素的位置,此时就要想到可以用单调栈。时间复杂度为O(n)。
单调栈的本质是空间换时间,因为在遍历的过程中需要用一个栈来记录右边第一个比当前元素高的元素,优点是只需要遍历一次。
分析单调栈的时候,我们需要考虑以下几点。
① 单调栈里存放的元素是什么?
单调栈里只需要存放元素的下标i就可以了,如果需要使用对应的元素,直接T[i]就可以获取。
② 单调栈里元素是递增呢?还是递减呢?
这里要根据具体的题目来分析。
使用单调栈主要有三个判断条件。
- 当前遍历的元素T[i]小于栈顶元素T[st.top()]的情况
- 当前遍历的元素T[i]等于栈顶元素T[st.top()]的情况
- 当前遍历的元素T[i]大于栈顶元素T[st.top()]的情况
把这三种情况分析清楚了,也就理解透彻了。
739. 每日温度
题目链接:739. 每日温度
思路:借助单调栈来依次遍历数组,具体执行过程可以看代码随想录的分析。
自己再模拟一遍,就会发现:只有单调栈递增(从栈口到栈底顺序),就是求右边第一个比自己大的,单调栈递减的话,就是求右边第一个比自己小的。
class Solution {public int[] dailyTemperatures(int[] temperatures) { // 注意,单调栈里加入的元素是下标。int[] res = new int[temperatures.length];Deque<Integer> stack = new LinkedList<>();stack.push(0);for (int i = 1; i < temperatures.length; i++) {if (temperatures[i] < temperatures[stack.peek()]) {// 情况一stack.push(i);} else 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;}
}
496. 下一个更大元素 I
题目链接:496. 下一个更大元素 I
思路:本题相对于上一题增加了条件,难度也有所增加。
注意题目中说是两个没有重复元素 的数组 nums1 和 nums2。可以用map做映射,根据数值快速找到下标,还可以判断nums2[i]是否在nums1中出现过。
遍历nums2,做单调栈处理,找到每一个元素右边最大元素,然后判断是否在nums1中存在,存在的话就要更新结果。
nums2的遍历过程与上一题相同。
class Solution {public int[] nextGreaterElement(int[] nums1, int[] nums2) {int[] res = new int[nums1.length];// 题目说如果不存在对应位置就输出 -1Arrays.fill(res, -1);// map数组映射nums1,key对应值,val对应下标。Map<Integer, Integer> map = new HashMap<>();for (int i = 0; i < nums1.length; i++) {map.put(nums1[i], i);}Deque<Integer> stack = new LinkedList<>();stack.push(0);for (int i = 1; i < nums2.length; i++) {if (nums2[i] < nums2[stack.peek()]) {// 情况一stack.push(i);} else if(nums2[i] == nums2[stack.peek()]){// 情况二stack.push(i);} else {// 情况三while (!stack.isEmpty() && nums2[i] > nums2[stack.peek()]) {if (map.containsKey(nums2[stack.peek()])) {// 该数在nums1中存在,需要更新结果。// 根据map找到nums2[stack.peek()]在nums1中的下标int index = map.get(nums2[stack.peek()]);res[index] = nums2[i];}stack.pop();}stack.push(i);}}return res;}
}
503. 下一个更大元素II
题目链接:503. 下一个更大元素II
思路:本题仍然是利用单调栈来寻找下一个更大数值的问题,只不过数组可以循环来寻找。解决方法是,将两个相同的数组拼接,然后利用单调栈来寻找就好了(寻找过程与每日温度类似)。
class Solution {public int[] nextGreaterElements(int[] nums) {int[] res = new int[nums.length];Arrays.fill(res, -1);Deque<Integer> stack = new LinkedList<>();stack.push(0);// 拼接一个新的numsint[] nums2 = new int[nums.length + nums.length];for (int i = 0; i < nums.length; i++) {nums2[i] = nums[i];nums2[i + nums.length] = nums[i];}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()]) {if (stack.peek() < nums.length) {res[stack.peek()] = nums2[i];} else {res[stack.peek() - nums.length] = nums2[i];}stack.pop();}stack.push(i);}}return res;}
}
其实也可以不扩充nums,而是在遍历的过程中模拟走了两遍nums。
class Solution {public int[] nextGreaterElements(int[] nums) {int[] res = new int[nums.length];Arrays.fill(res, -1);Deque<Integer> stack = new LinkedList<>();stack.push(0);for (int i = 1; i < nums.length * 2; i++) {// 模拟遍历两遍nums,注意一下都是用i % nums.length来操作if (nums[i % nums.length] <= nums[stack.peek()]) {stack.push(i % nums.length);} else {while (!stack.isEmpty() && nums[i % nums.length] > nums[stack.peek()]) {res[stack.peek()] = nums[i % nums.length];stack.pop();}stack.push(i % nums.length);}}return res;}
}