LeetCode 387 字符串中的第一个唯一字符 Swift 题解:用哈希表快速定位不重复字符
文章目录
- 摘要
- 描述
- 题解答案
- 题解代码分析
- 代码拆解
- 示例测试及结果
- 时间复杂度
- 空间复杂度
- 总结
摘要
在日常开发中,处理字符串是最常见的任务之一。有时候我们需要从一段文字里找到某个“特殊”的字符,比如:
- 在用户输入的用户名里,找到第一个唯一字符来生成推荐 ID;
- 在聊天消息里快速定位第一个不重复的字母,做关键词标记;
- 在日志文件里扫描异常标识符时,优先取第一个独特的符号。
这道题就是一个简化版的需求:给定一个字符串,找到第一个不重复的字符并返回它的下标,如果没有就返回 -1。
描述
题目要求:
- 输入一个字符串
s
,只包含小写字母。 - 找到第一个 只出现一次的字符,返回它的索引。
- 如果不存在,返回
-1
。
举几个例子:
s = "leetcode"
→ 第一个不重复的字符是l
,下标0
。s = "loveleetcode"
→ 第一个不重复的字符是v
,下标2
。s = "aabb"
→ 没有唯一字符,返回-1
。
题解答案
最直观的想法:
- 我们需要知道每个字符出现了多少次。
- 再从左到右扫描一次,找到第一个只出现一次的字符。
这就很自然地联想到 哈希表(字典)来存储字符出现次数。
题解代码分析
下面是 Swift 实现:
import Foundationclass Solution {func firstUniqChar(_ s: String) -> Int {var frequency: [Character: Int] = [:]// 第一次遍历:统计每个字符的出现次数for ch in s {frequency[ch, default: 0] += 1}// 第二次遍历:找到第一个出现次数为 1 的字符for (index, ch) in s.enumerated() {if frequency[ch] == 1 {return index}}return -1}
}
代码拆解
-
统计字符出现次数
- 用一个字典
frequency
存储每个字符出现的次数。 - 遍历字符串时,
frequency[ch, default: 0] += 1
是 Swift 的简洁写法,如果字典里没有该字符,就先设为 0。
- 用一个字典
-
寻找第一个唯一字符
- 再次遍历字符串。
- 如果某个字符在字典里的次数是 1,就直接返回它的索引。
-
不存在时返回 -1
- 如果整串字符串都找不到唯一字符,就返回
-1
。
- 如果整串字符串都找不到唯一字符,就返回
这个思路简单高效,只需要两次遍历。
示例测试及结果
我们写一个小 demo 来验证:
let solution = Solution()let ex1 = "leetcode"
print("输入: \(ex1)")
print("输出: \(solution.firstUniqChar(ex1))\n")let ex2 = "loveleetcode"
print("输入: \(ex2)")
print("输出: \(solution.firstUniqChar(ex2))\n")let ex3 = "aabb"
print("输入: \(ex3)")
print("输出: \(solution.firstUniqChar(ex3))\n")
运行结果:
输入: leetcode
输出: 0输入: loveleetcode
输出: 2输入: aabb
输出: -1
完全符合题意。
时间复杂度
- 第一次遍历统计次数 O(n)。
- 第二次遍历查找唯一字符 O(n)。
- 总体 O(n),满足要求。
空间复杂度
- 额外使用一个字典存储字符次数。
- 因为输入只包含 26 个小写字母,所以字典最多存 26 个键值对。
- 空间复杂度是 O(1)(常量级,不随字符串长度变化)。
总结
这道题是典型的 哈希计数 + 遍历 问题,属于“快手题”,但在实际开发中也挺有用:
- 处理用户输入时,快速找到独特标识;
- 日志分析时定位第一个独特的关键字;
- 文件名或标签扫描时,获取第一个唯一项。
它的思路非常清晰:
- 先统计,再查找,一步步缩小问题。
- 算法复杂度最优,额外空间需求也很低。
如果把题目升级,比如要求 返回所有唯一字符,思路也可以沿用,只是改成收集所有出现一次的字符。