代码随想录算法训练营Day52|单调栈part01

64 阅读5分钟

LeetCode 739 每日温度

题目链接:leetcode.cn/problems/da…

文档讲解:programmercarl.com/0739.每日温度.h…

视频讲解:www.bilibili.com/video/BV1my…

思路

给定一个整数数组 temperatures ,表示每天的温度,返回一个数组 answer ,其中 answer[i] 是指对于第 i 天,下一个更高温度出现在几天后。如果气温在这之后都不会升高,请在该位置用 0 来代替。

暴力解法

对于每天的问题向后搜索更高的温度。

  • 时间复杂度:O(n2)O(n^2)

单调栈解法

为了找到一个元素右边第一个比自己大的元素,就想到使用单调栈。单调栈的本质是用空间换时间,用一个栈记录我们遍历到的所有元素,就不需要重复去遍历,因此时间复杂度为O(n)O(n)

使用单调栈时需要明确:

  1. 单调栈中存放的元素是什么: 单调栈中存放元素的下标,如果需要使用对应的元素,根据下标获取
  2. 单调栈里元素是递增or递减(从栈顶到栈底) 为了把还没找到右边第一个更大元素的元素中,最先可能找到的元素置顶(栈只能读取栈顶),栈应该是递增的

使用单调栈时有三种情况:

  1. 遍历元素temp[i]小于栈顶元素temp[stack.top]i入栈
  2. 遍历元素temp[i]等于栈顶元素temp[stack.top]i入栈
  3. 遍历元素temp[i]大于栈顶元素temp[stack.top]:弹出栈顶直到temp[stack.top]大于等于temp[i]i入栈。把弹出的元素k的answer置为i-k

解法

class Solution {
	public int[] dailyTemperatures(int[] temperatures) {	
		int[] answer = new int[temperatures.length];		
		Stack<Integer> stack = new Stack<>();		
		stack.push(0);
		
		for (int i = 1; i < temperatures.length; i++) {		
			if (temperatures[i] <= temperatures[stack.peek()]) {			
				stack.push(i);			
			}			
			else {			
				while (!stack.isEmpty() && temperatures[i] > temperatures[stack.peek()]) {				
					int k = stack.pop();					
					answer[k] = i - k;				
				}				
				stack.push(i);			
			}		
		}		
		return answer;	
	}
}

LeetCode 496.下一个更大元素 I

题目链接:leetcode.cn/problems/ne…

文档讲解:programmercarl.com/0496.下一个更大元…

视频讲解:www.bilibili.com/video/BV1jA…

思路

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] 是如上所述的 下一个更大元素 。

本质上本题是要寻找nums2数组中部分元素的下一个更大元素。这一部份需要寻找的元素就是nums1中的元素,返回结果的顺序也要和nums1对应。为了成功对应,且nums1nums2都是无重复元素的,我们可以用一个Map去映射nums1的元素和下标。

本题单调栈中存储元素在nums2中的下标,递增。

考虑使用单调栈时情况:

  1. 如果nums2[i]小于等于栈顶元素nums2[stack.top] 或 栈为空:
    1. 如果nums2[i]在Map中,i入栈
    2. 否则,跳过
  2. 如果nums2[i]大于栈顶元素nums2[stack.top]
    1. 弹出栈顶元素直到nums2[i]小于栈顶元素nums2[stack.top]
    2. 对弹出的k,answer[nums2[k].value]置为nums2[i]
    3. 如果nums2[i]在Map中,i入栈
    4. 否则,跳过

解法

class Solution {
	public int[] nextGreaterElement(int[] nums1, int[] nums2) {	
		int[] answer = new int[nums1.length];
		for (int i = 0; i < answer.length; i++) {		
			answer[i] = -1;		
		}		
		Stack<Integer> stack = new Stack<>();		
		Map<Integer, Integer> map = new HashMap<>();		
		for (int i = 0; i < nums1.length; i++) {		
			map.put(nums1[i], i);		
		}		
		for (int i = 0; i < nums2.length; i++) {		
			if (stack.isEmpty() || nums2[i] <= nums2[stack.peek()]) {			
				if (map.containsKey(nums2[i])) {				
					stack.push(i);				
				}			
			}			
			else {			
				while (!stack.isEmpty() && nums2[i] > nums2[stack.peek()]) {				
					int k = stack.pop();					
					answer[map.get(nums2[k])] = nums2[i];				
				}				
				if (map.containsKey(nums2[i])) {				
					stack.push(i);				
				}			
			}		
		}		
		return answer;	
	}
}

LeetCode 503下一个更大元素II

题目链接:leetcode.cn/problems/ne…

文档讲解:programmercarl.com/0503.下一个更大元…

视频讲解:www.bilibili.com/video/BV15y…

思路

给定一个循环数组 nums ( nums[nums.length - 1] 的下一个元素是 nums[0] ),返回 nums 中每个元素的 下一个更大元素 。

数字 x 的 下一个更大的元素 是按数组遍历顺序,这个数字之后的第一个比它更大的数,这意味着你应该循环地搜索它的下一个更大的数。如果不存在,则输出 -1 。

在LeetCode 739 每日温度的基础上改成了环形数组。依旧使用单调栈存储下标,从栈顶到栈底递增。

区别仅在于对于数组的遍历上,LeetCode 739 每日温度只需要从头到尾遍历一边数组,但本题由于可以通过循环找到前面的元素,需要遍历两次。最后除了数组中最大的元素,其他元素都能找到更大的下一个元素

使用单调栈时有三种情况:

  1. 遍历元素nums[i]小于栈顶元素nums[stack.top]i入栈
  2. 遍历元素nums[i]等于栈顶元素nums[stack.top]i入栈
  3. 遍历元素nums[i]大于栈顶元素nums[stack.top]:弹出栈顶直到nums[stack.top]大于等于nums[i]i入栈。把弹出的元素k的answer置为nums[i]

解法

class Solution {
	public int[] nextGreaterElements(int[] nums) {	
		int[] answer = new int[nums.length];		
		for (int i = 0; i < answer.length; i++) {		
			answer[i] = -1;		
		}		
		Stack<Integer> stack = new Stack<>();		
		stack.push(0);		
		for (int i = 1; i < answer.length * 2; i++) {		
			int index = i % nums.length;			
			if (nums[index] <= nums[stack.peek()]) {			
				stack.push(index);			
			}			
			else {			
				while (!stack.isEmpty() && nums[index] > nums[stack.peek()]) {				
					int k = stack.pop();					
					answer[k] = nums[index];				
				}				
				stack.push(index);			
			}		
		}		
		return answer;	
	}
}

今日收获总结

今日学习时长3小时。