目录
- 零. 单调栈
- 一. Next Greater Element(单调栈问题模板)
- 题目简述:
- 思路和代码:
- I. 思路
- II. 代码
- 二. Next Warmer Weather
- 题目简述:
- 思路和代码:
- I. 思路
- II. 代码
- 三. Next Greater Elements(循环数组)
- 题目简述:
- 思路和代码:
- I. 思路
- II. 代码
- 四. 不同字符的最小子序列
- 思路和代码:
- I. 博主的做法
- II. 东哥的做法
- 五. 去除重复字母
- 思路和代码:
零. 单调栈
- 单调栈实际上就是栈,只是利用了一些巧妙的逻辑,使得每次新元素入栈后,栈内的元素都保持有序(单调递增或单调递减)。
- 它不同于堆,只处理一种典型的问题,叫做 Next Greater Element。
一. Next Greater Element(单调栈问题模板)
题目简述:
给你一个数组,返回一个等长的数组,对应索引存储着下一个更大元素,如果没有更大的元素,就存 -1。
eg:给你一个数组 [2,1,2,4,3],你返回数组 [4,2,4,-1,-1]。
解释:第一个 2 后面比 2 大的数是 4;1 后面比 1 大的数是 2;第二个 2 后面比 2 大的数是 4; 4 后面没有比 4 大的数,填 -1;3 后面没有比 3 大的数,填 -1。
- 函数名:public static int[] nextGreaterElement(int[] nums){ }
思路和代码:
I. 思路
-
先看东哥举的例子,类似于站队的问题,每个人都向后看,看到
第一个比他高的
就输出。
-
从后面向前将给的数组入栈。
-
如果后面没有人比他高,或者后面根本没有人,那么直接返回-1。(将后面比他低的人弹出栈,一个一个的向后面比,直到后面没有人)
-
将这个人加入栈,作为前一个人的比较标准。重复上面的步骤。
II. 代码
public static int[] nextGreaterElement(int[] nums){
int[] res = new int[nums.length];
Stack<Integer> stack = new Stack<>();
for(int i = nums.length-1; i >= 0; i--){
while(!stack.isEmpty() && stack.peek() <= nums[i] ){
stack.pop();
}
res[i] = stack.isEmpty() ? -1 : stack.peek();
stack.push(nums[i]);
}
return res;
}
二. Next Warmer Weather
题目简述:
给你一个数组 T,这个数组存放的是近几天的天气气温。你返回一个数组,计算:对于每一天,你还要至少等多少天才能等到一个更暖和的气温;如果等不到那一天,填 0 。
eg:给你 T = [73, 74, 75, 71, 69, 72, 76, 73],你返回 [1, 1, 4, 2, 1, 1, 0, 0]。
解释:第一天 73 华氏度,第二天 74 华氏度,比 73 大,所以对于第一天,只要等一天就能等到一个更暖和的气温。后面的同理。
- 函数名:public static int[] nextWarmerWeather(int[] weather){ }
思路和代码:
I. 思路
- 和上道题很像,就是入栈的时候,入的是索引而不是数组值。
II. 代码
public static int[] nextWarmerWeather(int[] weather){
int[] res = new int[weather.length];
Stack<Integer> stack = new Stack<>();
for(int i = weather.length-1; i >= 0; i--){
while(!stack.isEmpty() && weather[stack.peek()] <= weather[i]){
stack.pop();
}
res[i] = stack.isEmpty() ? 0 : stack.peek() - i;
stack.push(i);
}
return res;
}
三. Next Greater Elements(循环数组)
题目简述:
同样是 Next Greater Number,现在假设给你的数组是个环形的
eg:给你一个数组 [2,1,2,4,3],你返回数组 [4,2,4,-1,4]。拥有了环形属性,最后一个元素 3 绕了一圈后找到了比自己大的元素 4
- 函数名:
思路和代码:
I. 思路
- 和第一道题很类似。既然是循环数组,最坏的情况就是,一个比它大的数就在它的最前面,也就是题目中例子给的这样,3 要再绕整个数组一圈才能找到 4。
- 也就是说最多需要遍历两遍数组,而数据结构在内存中存储都是以线性的形式的,我们通过修改循环条件(一般是通过 % 运算符求模(余数))来改变数组的遍历次数。
- 如果想要遍历 n 遍数组,那么把 2 改为遍历的次数就可以了。
II. 代码
public static int[] nextGreaterElement_circle(int[] nums){
int[] res = new int[nums.length];
Stack<Integer> stack = new Stack<>();
for(int i = 2 * nums.length-1; i >= 0; i--){
while(!stack.isEmpty() && stack.peek() <= nums[i % nums.length] ){
stack.pop();
}
res[i % nums.length] = stack.isEmpty() ? -1 : stack.peek();
stack.push(nums[i % nums.length]);
}
return res;
}
四. 不同字符的最小子序列
- 题目链接:https://leetcode.cn/problems/smallest-subsequence-of-distinct-characters/
思路和代码:
I. 博主的做法
- 看都没看懂,什么叫返回结果的字典序最小。。。
II. 东哥的做法
- 题目的要求:
- 去重
- 不能打乱原来的顺序
- 保证字典序最小(类似单调栈问题)
class Solution {
public String removeDuplicateLetters(String s) {
Stack<Character> stack = new Stack<>();
//a从97开始,足够塞下26个字母了
int[] count = new int[150];
for(int i = 0; i < s.length(); i++)
count[s.charAt(i)]++;
//用桶排序的方法统计每一个字母的个数
char[] num = s.toCharArray();
for(char c : num){
count[c]--;
//如果栈中有,就不加了,去除重复元素
if(stack.contains(c))
continue;
//最大限度的处理排序问题,尽可能的使最后的顺序为字典序
//eg:bcabc,前面的bc都会在这里弹出去
while(!stack.empty() && stack.peek() > c){
if(count[stack.peek()] == 0)
break;
stack.pop();
}
stack.push(c);
}
StringBuilder stb = new StringBuilder();
while(!stack.empty())
stb.append(stack.pop());
return stb.reverse().toString();
}
}
五. 去除重复字母
- 题目链接:https://leetcode.cn/problems/remove-duplicate-letters/
思路和代码:
- 和上道题一模一样
参考:
https://labuladong.github.io/algo/di-yi-zhan-da78c/shou-ba-sh-48c1d/yi-dao-shu-ed782/
https://mp.weixin.qq.com/s__biz=MzAxODQxMDM0Mw==&mid=2247484525&idx=1&sn=3d2e63694607fec72455a52d9b15d4e5&chksm=9bd7fa65aca073734df90b45054448e09c14e6e35ad7b778bff62f9bd6c2b4f6e1ca7bc4f844&scene=21#wechat_redirect