返回

查找字符串中首个不重复字符:两种高效方法详解

javascript

寻找字符串中首个不重复字符

编程中,经常遇到需要在字符串中寻找特定字符的情况。一个常见的挑战是:给定一个字符串,找出其中第一个不重复出现的字符。这类问题看起来简单,但解决它需要仔细考虑各种效率问题。

方法一:利用哈希表计数

一个直接的方案是使用哈希表(在 JavaScript 中表现为普通对象或 Map)来记录每个字符出现的次数。遍历一遍字符串,将每个字符的出现次数存储在哈希表中。完成计数后,再次遍历字符串,检查每个字符的计数是否为1,若是,则该字符为首个不重复的字符。

此方法的关键在于用哈希表快速统计字符频率,避免重复计算,实现了较优的时间效率。

实现步骤

  1. 创建一个空对象,用于存储字符计数。
  2. 遍历输入字符串。对于每个字符,检查对象中是否已存在对应键,若存在则计数加一,否则将该键初始化为1。
  3. 再次遍历输入字符串。查找对象中值等于1的键对应的字符,找到后直接返回该字符。
  4. 如果完成遍历,未找到不重复的字符,返回 null 或一个指定的默认值。

代码示例

function findFirstNonRepeatingChar(str) {
    const charCounts = {};

    for (const char of str) {
        charCounts[char] = (charCounts[char] || 0) + 1;
    }

    for (const char of str) {
      if(charCounts[char] === 1) {
        return char;
      }
    }
    return null;
}

// 使用示例
const testString1 = 'aba';
const result1 = findFirstNonRepeatingChar(testString1);
console.log(`字符串 "${testString1}" 的第一个不重复字符是: ${result1}`); // 输出: b

const testString2 = 'aabcbd';
const result2 = findFirstNonRepeatingChar(testString2);
console.log(`字符串 "${testString2}" 的第一个不重复字符是: ${result2}`); // 输出: c

const testString3 = 'aabbcc';
const result3 = findFirstNonRepeatingChar(testString3);
console.log(`字符串 "${testString3}" 的第一个不重复字符是: ${result3}`); // 输出: null

代码解析

代码首先创建charCounts 对象来记录每个字符出现的次数。随后,代码进行两次遍历,第一次遍历计算字符频率,第二次遍历检查并返回首个出现一次的字符。如找不到,返回 null。此方法简单有效。

方法二:利用 indexOflastIndexOf

也可以使用 JavaScript 字符串对象的内置方法 indexOflastIndexOf。如果一个字符在这两个方法返回的索引相同,则意味着该字符仅出现一次。该方法的好处是不需要额外的数据结构,如哈希表,但可能带来较高的复杂度。

实现步骤

  1. 遍历字符串。
  2. 对于每个字符,使用 indexOf 查找字符首次出现的索引,使用 lastIndexOf 查找字符最后一次出现的索引。
  3. 若两个索引相等,返回当前字符。
  4. 完成遍历后仍未找到则返回 null或一个默认值。

代码示例

function findFirstNonRepeatingChar_indexOf(str) {
    for(let i = 0; i < str.length; i++){
        const char = str[i];
        if(str.indexOf(char) === str.lastIndexOf(char)){
            return char
        }
    }
    return null
}

// 使用示例
const testString1_index = 'aba';
const result1_index = findFirstNonRepeatingChar_indexOf(testString1_index);
console.log(`字符串 "${testString1_index}" 的第一个不重复字符是: ${result1_index}`);

const testString2_index = 'aabcbd';
const result2_index = findFirstNonRepeatingChar_indexOf(testString2_index);
console.log(`字符串 "${testString2_index}" 的第一个不重复字符是: ${result2_index}`);

const testString3_index = 'aabbcc';
const result3_index = findFirstNonRepeatingChar_indexOf(testString3_index);
console.log(`字符串 "${testString3_index}" 的第一个不重复字符是: ${result3_index}`);

代码解析

这段代码直接遍历字符串。 对每一个字符,调用 indexOflastIndexOf 来查找第一次出现和最后一次出现的位置,如果索引相等就返回该字符,找不到则返回 null 。 简洁易懂,依赖于字符串内建方法。

选择策略

通常情况下,第一种方法——哈希表计数——拥有更好的时间效率。因为它只需要两次 O(n) 的遍历(其中 n 是字符串的长度),整体的时间复杂度是O(n)。第二种方法 indexOflastIndexOf 方法本身的时间复杂度通常为 O(n), 而我们在循环内部执行这两个方法, 会产生 O(n^2) 级别的时间复杂度,更适用于字符串比较短,对效率要求不高的场景。 选择哪种方法需要权衡执行效率和代码简洁性。

无论选择哪种方法,在实际应用中都应根据具体的使用场景来选择最合适的方案。 对一些特殊情况,例如超长的字符串或者字符集非常大的情况,也要留意可能引发的问题。 优先保证代码运行的正确性,而后可以考虑代码的优化方向。