大家好,这里是白十七,今天给大家带来单调栈集锦,本集锦参考
大家一起进步!!有什么好的资源都可以发在评论区嗷!
402. 移掉 K 位数字 - 力扣(LeetCode)
这道题参考leetcode 402 Remove K Digits_哔哩哔哩_bilibili
大体上就是,先理解什么事单调栈。
他维持一个单调递增或单调递减的栈。
看这道题,移掉k位数字,返回最小的。
那我们就可以整一个单调递增的栈,这里要注意一个问题,就是什么时候添加,什么删除。
什么时候添加呢?当num[i]>num[i+1]的时候,这个时候,num[i+1]<num[i],维持不了单调递增的条件,就需要把num[i]删掉。
注意:这里删除还有一个条件限制,你总不能删的比移掉k位数字还多吧。就是stack.size()+nums.length()-1-index>=nums.length()-k
这句话什么意思呢,就是,当前的栈的大小+我们后面剩的数,是否大于我们要保留的位数。
比如
1432219 当我们遍历到第二位的时候,也就是4,添加。
遍历到第三位的时候,发现他不是单调递增的了,要不要删除呢?看看我们后面的位数+我们栈里面有的位数,是否大于我们,也就是stack.size()+nums.length()-index-1 (index是我们当前遍历到的,因为是从0开始算的,所以在-1) >=nums.length()-k (也就是我们要保存的位数)
也即stack.size()-1+k>=index
再然后就是考虑去0,因为你不能返回0200把,肯定都是200.
去0的话,就把头的0去掉,如果都是0,最后在给队列里加个0就ok。
class Solution {
public String removeKdigits(String num, int k) {
Deque<Character> deque = new ArrayDeque<>();
int len = num.length();
int index = 0;
int count = len-k;
if (count==0) {
return "0";
}
while (index < len) {
//单调递增 添加
while (index<len && (deque.isEmpty() || deque.peekLast()<=num.charAt(index))){
deque.addLast(num.charAt(index++));
}
if (index==len){break;}
//删除
while(!deque.isEmpty() &&deque.peekLast()>num.charAt(index) &&
deque.size()+len-index-1>=count){
deque.removeLast();
}
deque.addLast(num.charAt(index++));
}
//针对10001做的处理
while(deque.size()>count){
deque.removeLast();
}
//去0
while (!deque.isEmpty()&& deque.peekFirst()=='0'){
deque.removeFirst();
}if (deque.isEmpty()) {
deque.add('0');
}
StringBuilder builder = new StringBuilder();
index=0;
while(!deque.isEmpty()&& index<count){
builder.append(deque.removeFirst());
index++;
}
return builder.toString();
}
}
316. 去除重复字母 - 力扣(LeetCode)
首先是这道题的描述,他要求返回结果的字典序最小(要求不能打乱其他字符的相对位置)。
那这个怎么解决呢?
输入:"bcabc"
返回 "abc"
首先,我们得知道后面有没有这个字母,也就是字母最后一次出现的位置,如果有,我们就可以替换掉,如果没有,那么我们就不能动。
这个字母最后一次出现的位置怎么保存呢?用一个int[26],里面保存26个小字母,正好。
然后就是单调栈,如果遍历的字母正好比原先的大,那就添加进去,维持一个单调递增的数组,如果小呢,就去比较他们的位置,如果在后面还有出现,那我们就可以直接扔掉,然后在看前面的,如果还是小,接着这一操作,也就是一个while循环。
然后,我们还需要整一个set数组用来保存当前的字母,看有没有重复,如果重复了,就直接跳过。
大体思路搞明白之后,就可以写代码。
public String removeDuplicateLetters(String s){
//首先保存一下这个字符串最后出现的位置
int[] index=new int[26];
for(int i = 0;i<s.length();i++){
index[s.charAt(i)-'a']=i;
}
//然后一个queue用来保存单调递增的序列
Deque<Character>queue = new ArrayDeque<>();
//一个set用来保存出现的字母
HashSet<Character> set = new HashSet<Character>();
//整个哨兵节点 a,用来比大小
queue.addLast('a');
for(int i =0;i<s.length();i++){
char curChar = s.charAt(i);
//如果包含,直接下一次
if(set.contains(curChar)){
continue;
}
//不满足条件,删除
while(curChar<queue.peekLast() && index[queue.peekLast()-'a']>i){ // index[queue.peekLast()-'a']>i
//把set和queue中都删掉
set.remove(queue.peekLast());
queue.pollLast();
}
//删除完之后,再把后面的加进来
queue.addLast(curChar);
set.add(curChar);
}
//删除哨兵节点
queue.removeFirst();
StringBuilder sb= new StringBuilder();
while(!queue.isEmpty()){
sb.append(queue.pollFirst());
}
return sb.toString();
}
321. 拼接最大数 - 力扣(LeetCode)
参考:leetcode 321 Create Maximum Number_哔哩哔哩_bilibili
这个题目的描述就是,从两个数组中,取到k位的最大值。
也就是从num1中选取i位,从num2中选取k-i位,然后混合,取最大值。
思路有了之后,开始写代码。
首先是从从nums1和num2中取出最大的k位数,这个就可以用单调栈
首先大体框架
public int[] maxNumber(int[] nums1, int[] nums2, int k) {
int len1 = nums1.length;
int len2 = nums2.length;
int[] res= null;
//从数组中取k个数
for(int i = 0;i<=k;i++){
if (i>nums1.length|| k-i>nums2.length) {
continue;
}
//从nums1和nums2中取值
int[] max1 = getMax(nums1,i);
int[] max2 = getMax(nums2,k-i);
//混合两个数组,返回两个数组混合的最大值
int[] tmp=merge(max1,max2);
if(greater(tmp,0,res,0)){
res=tmp;
}
}
return res;
}
public int[] getMax(int[]nums,int num){
int[] res = new int[num];
//取nums中最大的num位数,用单调栈
Deque<Integer> queue = new ArrayDeque<>();
int index = 0;
int len = nums.length;
while(index<len){
//单调栈添加 这是一个单调递减的单调栈
while(index<len && (queue.isEmpty() || queue.peekLast() > nums[index])){
queue.addLast(nums[index++]);
}
//单调栈删除
if (index==nums.length) {
break;
}
while(!queue.isEmpty() && queue.peekLast()<nums[index] && queue.size()+len-index-1>=num){
queue.removeLast();
}
//最后元素的添加
queue.addLast(nums[index++]);
}
//最后元素的返回
for(int i = 0;i<num;i++){
if (!queue.isEmpty()){
res[i]=queue.pollFirst();
}
}
return res;
}
取得最大值之后,就是混合,然后在比较
混合的话就是看取出的max1和max2里面谁的数大就取谁,最后返回一个数组
public int[] merge(int[] nums1,int[] nums2){
int len1 = nums1.length;
int len2 = nums2.length;
int len = len1 + len2;
int[] nums = new int[len];
int index1 = 0;
int index2 = 0;
int index = 0;
while(index1<len1 || index2<len2){
if(greater(nums1,index1,nums2,index2)){
nums[index++]=nums1[index1++];
}else{
nums[index++]=nums2[index2++];
}
}
return nums;
}
最后就是比较
public boolean greater(int[] nums1, int index1, int[] nums2, int index2){
//因为nums2 是k-i,所以上来会是null,为了防止空指针异常,我们直接返回一个true
if(nums2==null) {
return true;
}
int len1 = nums1.length;
int len2 = nums2.length;
//这个是如果两个个数相等的情况
while(index1<len1 && index2<len2 && nums1[index1]==nums2[index2]){
index1++;
index2++;
}
//index2==nums2.length是为了防止数组下标越界的情况 index1!=nums1.length&&(nums1[index1]>nums2[index2])比较大小 index1!=nums1.length是为了防止第一次传的nums1空数组异常
return index2==nums2.length ||(index1!=nums1.length&&(nums1[index1]>nums2[index2]));
}