剑指 offer 50 第一个只出现一次的字符

剑指 offer 50 第一个只出现一次的字符

在这里插入图片描述

哈希表存储频数

01

class Solution {
    public char firstUniqChar(String s) {
        if(s.isEmpty()){
            return ' ';
        }

        int len = s.length();
        Map<Character,Integer> map = new HashMap<>();
        for (int i = len-1; i >= 0; i--) {
            char c = s.charAt(i);

            if (map.containsKey(c)){
                map.replace(c,map.get(c)+1);
            }else{
                map.put(c,1);
            }
        }
        for (int i=0;i<len;i++){
            if (map.get(s.charAt(i))==1){
                return s.charAt(i);
            }
        }
        return ' ';
    }

}

在这里插入图片描述

02

map.getOrDefault(c,0)的使用!!!

class Solution {
    public char firstUniqChar(String s) {
        if(s.isEmpty()){
            return ' ';
        }

        int len = s.length();
        Map<Character,Integer> map = new HashMap<>();
        for (int i = len-1; i >= 0; i--) {
            char c = s.charAt(i);

            map.put(c,map.getOrDefault(c,0)+1);
        }
        for (int i=0;i<len;i++){
            if (map.get(s.charAt(i))==1){
                return s.charAt(i);
            }
        }
        return ' ';
    }

}

在这里插入图片描述

时间复杂度 O ( n ) O(n) O(n) n n n是字符串的长度,需要扫描两次字符串。
空间复杂度 O ( ∣ Σ ∣ ) O(|\Sigma |) O(Σ) Σ \Sigma Σ是字符集,在本题中 s s s只包含小写字母,因此 Σ < = 26 \Sigma<=26 Σ<=26,我们需要 O ( ∣ Σ ∣ ) O(|\Sigma|) O(Σ)的空间存储哈希映射。

使用哈希表存储索引

class Solution {
    public char firstUniqChar(String s) {
        if(s.isEmpty()){
            return ' ';
        }

        int len = s.length();
        Map<Character,Integer> map = new HashMap<>();
        for (int i = len-1; i >= 0; i--) {
            char c = s.charAt(i);

            if (map.containsKey(c)){
                map.replace(c,-1);
            }else{
                map.put(c,i);
            }
        }
        int first = len;
        for (Map.Entry<Character,Integer> entry:map.entrySet()){
                int pos = entry.getValue();
                if (pos!=-1 && pos<first){
                    first = pos;
                }
        }
        if (first==len){
            return ' ';
        }else {
            return s.charAt(first);
        }
    }

}

在这里插入图片描述
时间复杂度 O ( n ) O(n) O(n)
空间复杂度 O ( ∣ Σ ∣ ) O(|\Sigma|) O(Σ)

队列

也可以借助队列找到第一个不重复的字符。队列具有先进先出的性质,因此很适合用来找出第一个满足某个条件的元素。
具体地,使用与第二种方法相同的哈希映射,并且使用一个额外的队列,按照顺序存储每一个字符以及他们第一次出现的位置。当对字符串进行遍历时,设当前遍历到的字符为 c c c,如果 c c c不在哈希映射中,就将 c c c与它的索引作为一个二元组放入队尾,否则就需要检查队列中的元素是否都满足“只出现一次”的要求,即我们不断地根据哈希映射中存储的值是否为 − 1 -1 1选择弹出队首元素,直到队尾元素真的只出现一次或者队列为空。
在遍历完成后,如果队列为空,说明没有不重复的字符,返回空格,否则队首的元素就是第一个不重复的字符。
在维护队列时,使用了延迟删除的技巧。也就是说,队列中有些字符出现了超过一次,但它只要不位于队首(不是第一个,后面想怎么重复怎么重复,找到第一个不重复的就行),那么就不会对答案造成影响,我们也就可以不用去删除它。只有当它前面的所有字符被移出队列,它成为队首时,我们才需要将它移除。

class Solution {
    public char firstUniqChar(String s) {
         if(s.isEmpty()){
            return ' ';
        }

        int len = s.length();
        Map<Character,Integer> map = new HashMap<>();
        Queue<Pair> queue = new LinkedList<>();
        for (int i = 0; i <len; i++) {
            char c = s.charAt(i);

            if (map.containsKey(c)){
                map.replace(c,-1);
                while ((!queue.isEmpty())&&map.get(queue.peek().ch)==-1){
                    queue.poll();
                }
            }else{
                map.put(c,i);
                queue.add(new Pair(c,i));

            }
        }
        if (queue.isEmpty()){
            return ' ';
        }
        return queue.peek().ch;
    }

}
class Pair{
    char ch;
    int pos;

    public Pair(char ch, int pos) {
        this.ch = ch;
        this.pos = pos;
    }
}

在这里插入图片描述
时间复杂度 O ( n ) O(n) O(n),遍历字符串的时间复杂度是 O ( n ) O(n) O(n),在遍历的过程还维护了一个队列,由于每一个字符最多只会被放入和弹出队列最多各一次,因此维护队列的总时间复杂度是 O ( ∣ Σ ∣ ) O(|\Sigma|) O(Σ),由于 s s s包含的字符种类数一定小于 s s s的长度,因此 O ( ∣ Σ ∣ ) O(|\Sigma|) O(Σ)在渐进意义下小于 O ( n ) O(n) O(n),可以忽略。
空间复杂度 O ( ∣ Σ ∣ ) O(|\Sigma|) O(Σ)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值