给定一个字符串,找到它的第一个不重复的字符,并返回它的索引。如果不存在,则返回 -1。
示例:
s = "leetcode"
返回 0
s = "loveleetcode"
返回 2
提示:你可以假定该字符串只包含小写字母。
我的算法实现:
/**
* @param {string} s
* @return {number}
*/
var firstUniqChar = function (s) {
let len = s.length
let obj = {};
for (let i = 0; i < len; i++) {
let c = s[i]
if (obj[c] === undefined) { // 如果没有出现过就保存到obj中
obj[c] = i;
} else { // 如果出现过,那么就将值设置成一个很大的值,而最大的值就是字符串的长度
obj[c] = len
}
}
let arr = Object.values(obj); // 将得到的对象转换成数组
let minIndex = Math.min(...arr) // 得到 arr 这个数组中最小值
if (arr.length > 0 && minIndex !== len) return minIndex; // 如果最小值不是字符串的长度,那么得到的最小值就是我们想得到的值
else return -1;
};
我也看了别人的解法,看到了一个使用 java 写的,我感觉最好的:
// 解法4. 根据给定字符串长度分情况优化
public int firstUniqChar(String s) {
// 字符串长度不超过26,按照原先的方式遍历
if (s.length() <= 26) {
int[] chars = new int[26];
for (char c : s.toCharArray()) {
chars[c - 'a'] += 1;
}
for (int i = 0; i < s.length(); ++i) {
if (chars[s.charAt(i) - 'a'] == 1) {
return i;
}
}
}
//只遍历26个字母,使用indexOf函数检查字符索引
int result = -1;
for (char c = 'a'; c <= 'z'; ++c) {
int pre = s.indexOf(c);
// s包含该字符并且只出现一次
if (pre != -1 && pre == s.lastIndexOf(c)) {
// 取最前面的位置
result = (result == -1 || result > pre) ? pre : result;
}
}
}
也许,初次看到觉得这个代码量这么多怎么可能好,首先当小于 26 个字母的时候,遍历的次数只有这么多。而当长度比 26 要多的时候,由于字母只有 26 个,所以不管有多少的长度,只会遍历 26 次,这样说可能感觉只要后半段是不是就可以了,事实并不是这样,首先 indexOf 和 lastIndexOf 的过程也是耗时间的,这个主要取决于字符串的长度,如果都使用这种,那么就会浪费大部分没有必要的循环和比较。
当然除了上面这些,还有一个原因,提高效率的方式,所采取的方式往往都是“尽量不做无用功”,按照这个思路进行,而这段代码正好朝着这个方向靠,我特别欣赏。
来源:力扣(LeetCode)