简单题——力扣387.字符串中的第一个唯一字符
力扣387.字符串中的第一个唯一字符
【LeetCode 387】字符串中的第一个唯一字符
一、题目描述
给定一个字符串 s
,找到它的第一个不重复的字符,并返回它的下标。
如果不存在这样的字符,则返回 -1
。
示例
示例 1:
输入:s = "leetcode"
输出:0
解释:字符 'l' 在字符串中只出现一次,且下标为 0。
示例 2:
输入:s = "loveleetcode"
输出:2
解释:第一个只出现一次的字符是 'v',下标为 2。
示例 3:
输入:s = "aabb"
输出:-1
解释:字符串中没有不重复的字符。
提示
- 1 <= s.length <= 10⁵
- s 只包含小写字母
二、思路分析
这道题的核心在于:
- 找出哪些字符出现了 一次;
- 按字符串的 顺序 找到第一个这样的字符。
由于字符串只包含小写字母,我们可以用数组或哈希表高效统计。
三、方法一:使用哈希表统计频率(推荐写法)
思路步骤
- 第一次遍历字符串,用哈希表统计每个字符的出现次数。
- 第二次遍历字符串,找到第一个出现次数为 1 的字符,返回其索引。
- 如果不存在,返回 -1。
时间复杂度:O(n)
空间复杂度:O(1)(因为只有 26 个字母)
代码实现
class Solution {public int firstUniqChar(String s) {int[] count = new int[26];for (char c : s.toCharArray()) {count[c - 'a']++;}for (int i = 0; i < s.length(); i++) {if (count[s.charAt(i) - 'a'] == 1) {return i;}}return -1;}
}
代码解析
count[c - 'a']
用于记录字母c
出现的次数;- 第二次遍历时,找到第一个出现次数为 1 的字符;
- 由于是顺序遍历,保证返回的是“第一个唯一字符”。
四、方法二:使用 HashMap(适合扩展到任意字符)
如果字符串不止包含小写字母,可以使用 HashMap<Character, Integer>
。
实现代码
import java.util.HashMap;
import java.util.Map;class Solution {public int firstUniqChar(String s) {Map<Character, Integer> map = new HashMap<>();for (char c : s.toCharArray()) {map.put(c, map.getOrDefault(c, 0) + 1);}for (int i = 0; i < s.length(); i++) {if (map.get(s.charAt(i)) == 1) {return i;}}return -1;}
}
优点
- 更通用,可扩展到任意 Unicode 字符;
- 思路清晰,适合初学者理解。
缺点
- 相比数组,
HashMap
占用的空间和时间开销略高。
五、方法三:利用字符 API 优化写法(简洁但效率略低)
Java 的 String.indexOf()
和 String.lastIndexOf()
方法可以用来判断字符是否唯一。
代码实现
class Solution {public int firstUniqChar(String s) {for (int i = 0; i < s.length(); i++) {if (s.indexOf(s.charAt(i)) == s.lastIndexOf(s.charAt(i))) {return i;}}return -1;}
}
复杂度分析
- 每次
indexOf
与lastIndexOf
都是 O(n),整体 O(n²)。 - 对于 1e5 级别的字符串会超时,因此只推荐在字符串很短时使用。
六、方法对比总结
方法 | 思路 | 时间复杂度 | 空间复杂度 | 适用场景 |
---|---|---|---|---|
方法一 | 数组计数 | O(n) | O(1) | 最推荐(仅小写字母) |
方法二 | HashMap 计数 | O(n) | O(n) | 任意字符情况 |
方法三 | 字符 API 判断 | O(n²) | O(1) | 简短字符串或快速实现 |
七、结论
最优解为方法一:使用长度为 26 的数组统计字符频率。
时间复杂度 O(n),空间复杂度 O(1),实现简单高效。
最终推荐代码:
class Solution {public int firstUniqChar(String s) {int[] count = new int[26];for (char c : s.toCharArray()) {count[c - 'a']++;}for (int i = 0; i < s.length(); i++) {if (count[s.charAt(i) - 'a'] == 1) {return i;}}return -1;}
}