leetcode316. 去除重复字母,字符串去重,最小字典序

leetcode316. 去除重复字母,字符串去重,最小字典序

题目描述:
给你一个仅包含小写字母的字符串,请你去除字符串中重复的字母,使得每个字母只出现一次。需保证返回结果的字典序最小(要求不能打乱其他字符的相对位置)。
示例 1:
输入: “bcabc”
输出: “abc”
示例 2:
输入: “cbacdcbc”
输出: “acdb”

1.知识点:数据结构:重复与去重就应该想到hashset,和hashmap如果不能用额外空间应该想到双指针
顺序相关的应该想到stack栈
这种题如果想要遍历和枚举应该是不现实的,太过于繁杂,简单点先用贪心算法试试
贪心算法:贪心算法(又称贪婪算法)是指,在对问题求解时,总是做出在当前看来是最好的选择。
也就是说,不从整体最优上加以考虑,他所做出的是在某种意义上的局部最优解。
2.基本贪心算法思想:遍历所有字符,1.判定这个字符是否出现过,若没有先放入stack第一个字符,然后遍历从后面开始的字符
2.如果这个字符比前一个字符要小,则检查前一个字符,若前一个字符在后面还会出现,则将前一个字符pop出栈
再放入现在的字符,并且只要栈还有值,则一直重复这个流程检查,直到不符合要求
3.放入现在的字符
3.代码实现

public String removeDuplicateLetters(String s) {
     
        //用到的数据结构:
        // 栈用来存放最后的结果,
        // hashmap用来存放每个字母出现的次数,
        // set用来存放不重复的字母
        Stack<Character> stack=new Stack<>();
        Map<Character,Integer> map=new HashMap<>();
        Set<Character> set=new HashSet<>();
        for(Character c : s.toCharArray()) {
     
            map.put(c, map.getOrDefault(c, 0) + 1);
           // System.out.println(c+":"+map.get(c));

        }
        for(Character c : s.toCharArray() )
        //for (int i=0 ; i
        {
     
           // Character c = s.charAt(i);
            if (!set.contains(c))
            {
     
                //这里的判定是一个难点,并且判断是否为空要在peek之前
                //并且我们判定的是stack栈中前一个字符与现在所遍历到的字符的关系,并且判定前一个字符是否重复
                //并且这个判定一直持续到否为止,这时贪心算法的关键
                while ( !stack.isEmpty() && stack.peek() > c && map.get(stack.peek()) >= 1)
                {
     
                //这里是删除和出栈前一个字符
                    set.remove(stack.peek());
                    stack.pop();
                }
                stack.push(c);
                set.add(c);
            }
            //在这里,会一直把当前遍历的字符在map中的个数减去1操作
            map.put(c,map.get(c)-1);
            
        }
        //这里说一下stringbuilder和stringbuffer的区别
        //和 String 类不同的是,StringBuffer 和 StringBuilder 类的对象能够被多次的修改,并且不产生新的未使用对象。
         // StringBuilder 类在 Java 5 中被提出,
        //它和 StringBuffer 之间的最大不同在于 StringBuilder 的方法不是线程安全的(不能同步访问)。
//由于 StringBuilder 相较于 StringBuffer 有速度优势,所以多数情况下建议使用 StringBuilder 类。
//然而在应用程序要求线程安全的情况下,则必须使用 StringBuffer 类。
        StringBuilder stringBuilder=new StringBuilder();
        while ( !stack.isEmpty() ) stringBuilder.append(stack.pop());
        stringBuilder.reverse();
        return stringBuilder.toString();
    }

你可能感兴趣的:(算法学习,学习,算法,数据结构,leetcode)