387. 字符串中的第一个唯一字符

题目

给定一个字符串s,找到 它的第一个不重复的字符,并返回它的索引 。如果不存在,则返回 -1

示例 1:
输入: s = “leetcode”
输出: 0
示例 2:

输入: s = “loveleetcode”
输出: 2
示例 3:

输入: s = “aabb”
输出: -1

方法一:使用哈希表存储频数

思路与算法
我们可以对字符串进行两次遍历
在第一次遍历时,通过hash映射统计每个字符出现的频数,如果只出现一次,设置他的值为1,如果出现多次就设置值为0或其他。

代码:

public int firstUniqChar(String s) {
    int len = s.length();
    Map<Character,Integer> map=new HashMap<>();
    for (int i = 0; i < len; i++) {
        map.put(s.charAt(i),map.getOrDefault(s.charAt(i),0)+1);
    }
    for (int i = 0; i < len; i++) {
        if(map.get(s.charAt(i))==1){
            return i;
        }
    }
    return -1;
}

其中的map.getOrDefault()是map自带的方法
其作用是:
当Map集合中有这个key时,就用这个key值,没有时就给他设置默认的值。
例如:

HashMap<String, String> map = new HashMap<>();
	map.put("name", "cookie");
	map.put("age", "18");
	map.put("sex", "女");
	String name = map.getOrDefault("name", "random");
	System.out.println(name);// cookie,map中存在name,获得name对应的value
	int score = map.getOrDefault("score", 80);
	System.out.println(score);// 80,map中不存在score,使用默认值80

方法二:使用哈希表存储索引

感觉hash是真的方便,同样也是两次遍历。这次我们是直接把字符串中的字符作为存储的key,索引下标作为存储的value。如果遍历时key已经存在,设置其key的value值为-1。如果key值不存在就设置key为当前的索引i
第二次遍历map找出出了-1之外最小的i就可以了。
最后就是i遍历到最后是n的话,同样也是返回-1。

class Solution {
    public int firstUniqChar(String s) {
        Map<Character, Integer> position = new HashMap<Character, Integer>();
        int n = s.length();
        for (int i = 0; i < n; ++i) {
            char ch = s.charAt(i);
            if (position.containsKey(ch)) {
                position.put(ch, -1);
            } else {
                position.put(ch, i);
            }
        }
        int first = n;
        for (Map.Entry<Character, Integer> entry : position.entrySet()) {
            int pos = entry.getValue();
            if (pos != -1 && pos < first) {
                first = pos;
            }
        }
        if (first == n) {
            first = -1;
        }
        return first;
    }
}

方法三:队列

思路与算法
我们也可以借助队列找到第一个不重复的字符。队列具有「先进先出」的性质,因此很适合用来找出第一个满足某个条件的元素。
具体地,我们使用与方法二相同的哈希映射,并且使用一个额外的队列,按照顺序存储每一个字符以及它们第一次出现的位置。当我们对字符串进行遍历时,设当前遍历到的字符为 cc,如果 cc 不在哈希映射中,我们就将 cc 与它的索引作为一个二元组放入队尾,否则我们就需要检查队列中的元素是否都满足「只出现一次」的要求,即我们不断地根据哈希映射中存储的值(是否为 -1−1)选择弹出队首的元素,直到队首元素「真的」只出现了一次或者队列为空。
在遍历完成后,如果队列为空,说明没有不重复的字符,返回 -1−1,否则队首的元素即为第一个不重复的字符以及其索引的二元组。

在维护队列时,我们使用了「延迟删除」这一技巧。也就是说,即使队列中有一些字符出现了超过一次,但它只要不位于队首,那么就不会对答案造成影响,我们也就可以不用去删除它。只有当它前面的所有字符被移出队列,它成为队首时,我们才需要将它移除。

代码:

class Solution {
    public int firstUniqChar(String s) {
        Map<Character, Integer> position = new HashMap<Character, Integer>();
        Queue<Pair> queue = new LinkedList<Pair>();
        int n = s.length();
        for (int i = 0; i < n; ++i) {
            char ch = s.charAt(i);
            if (!position.containsKey(ch)) {
                position.put(ch, i);
                queue.offer(new Pair(ch, i));
            } else {
                position.put(ch, -1);
                while (!queue.isEmpty() && position.get(queue.peek().ch) == -1) {
                    queue.poll();
                }
            }
        }
        return queue.isEmpty() ? -1 : queue.poll().pos;
    }

    class Pair {
        char ch;
        int pos;

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

最后再介绍一道解法

public int firstUniqChar(String s) {
    int[] arr = new int[26];
    int n = s.length();
    for (int i = 0; i < n; i++) {
        arr[s.charAt(i)-'a']++ ;
    }
    for (int i = 0; i < n; i++) {
        if (arr[s.charAt(i)-'a'] == 1) {
            return i;
        }
    }
    return -1;
}

是记录26个字母每个字母的数量的,两次循环,第一次找出每个字母的数量,第二次找出为值1的下标返回。

哈哈 算法是不是很有意思。。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值