“我报名参加金石计划1期挑战——瓜分10万奖池,这是我的第1篇文章,点击查看活动详情”
题目
给定一个字符串 s ,找到 它的第一个不重复的字符,并返回它的索引。如果不存在,则返回 -1。
示例 1:
- 输入:
s = "leetcode"- 输出:
0
示例 2:
- 输入:
s = "loveleetcode"- 输出:
2
示例 3:
- 输入:
s = "aabb"- 输出:
-1
方法一:使用哈希表存储频数
思路及解法
我们可以对字符串进行两次遍历。
在第一次遍历时,我们使用哈希映射统计出字符串中每个字符出现的次数。在第二次遍历时,我们只要遍历到了一个只出现一次的字符,那么就返回它的索引,否则在遍历结束后返回 −1。
代码
class Solution {
func firstUniqChar(_ s: String) -> Int {
var frequency: [Character : Int] = [:]
for ch: Character in s {
if nil != frequency[ch] {
frequency[ch] = frequency[ch]! + 1
} else {
frequency[ch] = 1
}
}
var index: Int = 0
for ch: Character in s {
if nil != frequency[ch] && frequency[ch]! == 1{
return index
}
index += 1
}
return -1
}
}
复杂度分析
-
时间复杂度:,其中 是字符串 的长度。我们需要进行两次遍历。
-
空间复杂度:,其中 是字符集,在本题中 只包含小写字母,因此 。我们需要 的空间存储哈希映射。
方法二:使用哈希表存储索引
思路及解法
我们可以对方法一进行修改,使得第二次遍历的对象从字符串变为哈希映射。
具体地,对于哈希映射中的每一个键值对,键表示一个字符,值表示它的首次出现的索引(如果该字符只出现一次)或者 −1(如果该字符出现多次)。当我们第一次遍历字符串时,设当前遍历到的字符为 ,如果 不在哈希映射中,我们就将 与它的索引作为一个键值对加入哈希映射中,否则我们将 在哈希映射中对应的值修改为 −1。
在第一次遍历结束后,我们只需要再遍历一次哈希映射中的所有值,找出其中不为 −1 的最小值,即为第一个不重复字符的索引。如果哈希映射中的所有值均为 −1,我们就返回 −1。
代码
class Solution {
func firstUniqChar(_ s: String) -> Int {
var position: [Character : Int] = [:]
var i: Int = 0
for ch: Character in s {
if nil != position[ch] {
position[ch] = -1
} else {
position[ch] = i
}
i += 1
}
var first: Int = s.count
for (_, pos) in position {
if pos != -1 && pos < first {
first = pos
}
}
if first == s.count {
first = -1
}
return first
}
}
复杂度分析
-
时间复杂度:,其中 是字符串 的长度。第一次遍历字符串的时间复杂度为 ,第二次遍历哈希映射的时间复杂度为 ,由于 包含的字符种类数一定小于 的长度,因此 在渐进意义下小于 ,可以忽略。
-
空间复杂度:,其中 是字符集,在本题中 只包含小写字母,因此 。我们需要 的空间存储哈希映射。