LeetCode 378字符串中的第一个唯一字符
题目描述
给定一个字符串,找到它的第一个不重复的字符,并返回它的索引。如果不存在,则返回 -1。
提示:你可以假定该字符串只包含小写字母。
示例
输入:s = “leetcode”
输出:0
输入:s = “loveleetcode”
输出:2
题目解析
题目的意思是要我们找到第一个没有重复出现的字符,例如示例1中,‘l’, ‘t’, ‘c’, ‘o’, 'd’都只出现一次,但是‘l’排在前面出现,因此返回‘l’所在的索引,即0;示例而同理。
根据示例可以看出,我们应该找到什么办法,在遍历字符传递的时候同时记录每个字符出现的次数。之后只要重新遍历得到第一个出现次数为1的字符其索引即为我们要的答案。下面有两种方式来达到这个目的。
-
使用集合框架
由于我是用java提交的代码,所以在用的时候使用的Map集合来做的。选择map集合的原因是因为map集合不允许出现重复键,因此我们可以将要统计的字符当做map集合中的键,出现的次数当做其值。
实现代码如下:
class Solution { public int firstUniqChar(String s) { Map<Character, Integer> map = new HashMap<>(26); //创建一个map集合,并指定初始化大小为26,小写字母只有26个,不指定也行 //第一趟遍历 -- 按照顺序统计每个字符出现的次数 for (int i = 0; i < s.length(); i++) { if (map.get(s.charAt(i)) == null) { //如果map集合中还没有该字符,则添加上,其值设为1 map.put(s.charAt(i), 1); } else { map.put(s.charAt(i), map.get(s.charAt(i)) + 1); //有该字符,值增加 } } int result = -1; //第二趟遍历 -- 找到第一个值为1的键,返回该索引 for (int i = 0; i < s.length(); i++) { if (map.get(s.charAt(i)) == 1) { result = i; break; } } return result; } }
程序执行结果
- 使用数组
可以看到上面那种使用集合方法的速度太慢了。上面之所以要使用map集合就是因为能够将键值对给同时记录起来,但是由于该题中字符只包含小写字母,最多也只有26个字符。因此可以使用数组来隐性表示这26个字符,即数组下标索引即对应着26个字母,这可以使用ASCII码来实现,用ch - 'a’即可得到ch字符在数组中的位置了,数组中每一项的值保留对应字母最后一次出现的在字符串中的索引。即数组索引对应键,最后一次出现的索引对应值。这样就不用使用集合来操作了,用起来也会更简便。过程如下:
-
创建一个大小为26的int类型的数组,数组索引对应字符,数组值保留该字符在字符串s中最后一次出现的索引。
-
对字符串s一趟遍历,记录每个字符最后一次出现的索引
-
对字符串s二趟遍历,判断遍历到的索引是否和数组中对应字符记录的对应索引的大小相等,相等的话直接返回该索引。不相等的话,将数组中该字符对应的值设置为-1,避免后面出现重复的字符时造成错误。
-
如果二趟遍历中没有返回,那么直接在最后返回-1即可。
程序实现:
class Solution {
public int firstUniqChar(String s) {
int[] nums = new int[26]; //创建一个大小为26的数组
for (int i = 0; i < s.length(); i++) {
char ch = s.charAt(i);
nums[ch - 'a'] = i; // ch - 'a'即可得到对应字符在数组中的索引位置,这个运算时通过ASCII码进行运算的
}
for (int i = 0; i < s.length(); i++) {
int index = s.charAt(i) - 'a';
if (nums[index] == i) { //数组中的值和遍历到的索引相等则表示该字符第一次出现,直接将这个索引返回
return i;
} else {
nums[index] = -1; //该字符遍历过设置为-1,后面不会再被判断到了
}
}
return -1;
}
}
程序执行结果:
可见与使用集合的方式相比快了很多。