leetcode字符串中的第一个唯一字符(每日计划)

121 阅读1分钟

给定一个字符串,找到它的第一个不重复的字符,并返回它的索引。如果不存在,则返回 -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)