316. 去除重复字母-贪心+单调栈

1.题目

2.思路

思考去重,一开始直接就想到可用哈希表。

然后发现要字典序最小,不知道应该怎么选择???卡住。。。。。翻题解去了

发现用到了单调栈,果然思路不对,想法都是白搭,所以算法和思路非常重要,10分钟一点思路都没有,直接去看题解,看看是数据结构的问题还是算法的问题。

问题一这道题是如何有贪心的思想的?

贪心就代表着每一步的选择都是最优的,不会存在现在的选择需要依靠后面未知的数。

这里我们要得到最小的字典序,假如不考虑重复以及不考虑保留一位原有的字符的话,直接对于当前元素判断:

  • 如果这个元素的字典序比上一个字典序小,那么需要把上一个元素给删掉。(这里是一个递归循环,可以一直删掉)
  • 如果这个元素的字典序比上一个字典序大,那么直接不管这个元素,遍历下一个元素即可

这样就是一直都是最优选择,可以保证这样的字符是字典序最小的,至少不满足题目中的重复字符以及保留一位原有的字符的条件。

进一步思考,如何满足不要重复字符的条件呢?

  • 使用一个boolean[]vis = new boolean[26]布尔数组记录是否被访问过,就可以达到目的了。

那么再进一步,如何满足保留一位字符呢?

  • 使用一个int[]num=new inr[26]整形数组记录每个元素的频次,就可以达到目的了。

问题二单调栈是拿来干嘛的?

在考虑删掉当前遍历字符的时候,可能需要一直删掉前一个字符,所以这个先进后出的特点,选择了栈。

这个栈记录了从栈底到栈顶,字典序是递增的,并且可以是保证题目限定的条件。所以叫做单调栈。

只要字典序关系不满足,可以一直弹出栈顶元素。

问题三时间复杂度为什么是O(n)?

代码中虽然有双重循环,但是每个字符至多只会入栈、出栈各一次。

参考大佬题解:力扣

class Solution {
    public String removeDuplicateLetters(String s) {
        // 思路不对,所以白搭。
        // 贪心 + 单调栈。
        //贪心 + 单调栈
        int n = s.length();
        boolean[]vis = new boolean[26]; // 记录字符是否被访问
        int[]num = new int[26];//记录每个字符出现的次数
        // 赋值
        for(int i = 0 ; i < n ; i++)
            num[s.charAt(i) - 'a']++;


        Stack<Character>stack = new Stack();
        
        //贪心,就是对每个字符都有一个最优的操作办法,所以开始遍历
        for(int i = 0 ; i < n; i++){
            char c = s.charAt(i);
            num[c - 'a']--;
            if(vis[c - 'a'])continue; // 如果已经访问过这个元素,直接下一个元素
            
            while(!stack.isEmpty() && stack.peek() > c){
                //栈顶元素字典序 比 当前元素大,那么就有可能需要弹出这个元素
                if(num[stack.peek() - 'a'] == 0)break; // 这是最后一个元素,后面没有了,所以直接结束
                char cur = stack.peek();
                stack.pop();
                vis[cur - 'a'] = false;//因为弹出元素了,所以后续还可以继续访问这个元素
            }
            //把该弹的元素都弹出了。那么该入栈了。
            stack.push(c);
            vis[c - 'a'] = true;
        }
        //遍历栈
        StringBuffer sb = new StringBuffer();
        while(!stack.isEmpty())
            sb.append(stack.pop());
        //最后需要反转
        return sb.reverse().toString();
    }
}

3.结果

 4.感想

  • 贪心的思想很重要,一步一步来推导。
  • 单调栈之前没遇到过,学一下如何使用
  • StringBuffer sb = new StringBuffer();
  • sb.append();
  • sb.reverse().toString(); // 反转函数
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值