题目描述:
在字符串 s 中找出第一个只出现一次的字符。如果没有,返回一个单空格。 s 只包含小写字母。
示例:
s = "abaccdeff"
返回 "b"s = ""
返回 " "
限制:0 <= s 的长度 <= 50000
我的解题思路一:两次遍历字符串,第一次用Map记录每个字符出现的次数,再次遍历时当第一次出现哈希表中字符值为1时则返回该字符,否则返回空
public char firstUniqChar(String s) {
Map<Character,Integer> map = new HashMap<>();
for (int i = 0; i < s.length(); i++) {
if (map.containsKey(s.charAt(i))) {
map.put(s.charAt(i),map.get(s.charAt(i)) + 1);
} else {
map.put(s.charAt(i), 1);
}
}
for (int j = 0; j < s.length(); j++) {
if (map.get(s.charAt(j)) == 1) {
return s.charAt(j);
}
}
return ' ';
}
复杂度分析
时间复杂度:O(n),其中 n 是字符串 s 的长度。我们需要进行两次遍历。
空间复杂度:O(∣Σ∣),其中 Σ 是字符集,在本题中s 只包含小写字母,因此 ∣Σ∣≤26。我们需要 O(∣Σ∣) 的空间存储哈希映射。
参考解题思路二:两次遍历,一次遍历字符串记录字符出现一次的索引位置或字符多次出现索引记录为-1,优化哈希表,可减少第二次遍历的次数
public char firstUniqChar(String s) {
Map<Character,Integer> map = new HashMap<>();
// 遍历第一次字符串找到所有字符出现次数或者只出现一次的索引位置
for (int i = 0; i < s.length(); i++) {
if (map.containsKey(s.charAt(i))) {
map.put(s.charAt(i), -1);
} else {
map.put(s.charAt(i), i);
}
}
int index = s.length();
// 遍历哈希表找到最先出现的且只出现一次的字符串
for (Map.Entry<Character,Integer> m : map.entrySet()) {
if (m.getValue() != -1 && m.getValue() < index) {
// 找到出现索引更小的字符替换索引位置
index = m.getValue();
}
}
// 是否存在满足条件的字符,无则返回空,有则返回该索引字符
return index == s.length() ? ' ' : s.charAt(index);
}
复杂度分析
时间复杂度:O(n),其中 n 是字符串s 的长度。第一次遍历字符串的时间复杂度为 O(n),第二次遍历哈希映射的时间复杂度为 O(∣Σ∣),由于 s 包含的字符种类数一定小于 s 的长度,因此 O(∣Σ∣) 在渐进意义下小于 O(n),可以忽略。
空间复杂度:O(∣Σ∣),其中Σ 是字符集,在本题中s 只包含小写字母,因此 ∣Σ∣≤26。我们需要 O(∣Σ∣) 的空间存储哈希映射。
参考解题思路三:队列+哈希表,哈希表记录同方法二一致,当字符出现一次则追加到队列尾部,当字符出现多次则判断队列中队首元素是否为重复出现字符,弹出重复字符,直至队首为首次出现字符为止,最后返回非空的队列首位元素即可
public char firstUniqChar(String s) {
Map<Character,Integer> map = new HashMap<>();
Queue<por> queue = new LinkedList();
// 遍历第一次字符串找到所有字符出现次数或者只出现一次的索引位置
for (int i = 0; i < s.length(); i++) {
if (map.containsKey(s.charAt(i))) {
map.put(s.charAt(i), -1);
// 删除队列中重复元素
while (!queue.isEmpty() && map.get(queue.peek().ch) == -1) {
queue.poll(); // 弹出队首重复的元素
}
} else {
map.put(s.charAt(i), i);
queue.offer(new por(s.charAt(i), i)); // 追加进队列
}
}
// 队列中是否存在满足条件的字符,无则返回空,有则返回该字符
return queue.isEmpty() ? ' ' : queue.peek().ch;
}
class por{
char ch;
int index;
public por(char ch, int index) {
this.ch = ch;
this.index = index;
}
}
复杂度分析
时间复杂度:O(n),其中 n 是字符串 s 的长度。遍历字符串的时间复杂度为 O(n),而在遍历的过程中我们还维护了一个队列,由于每一个字符最多只会被放入和弹出队列最多各一次,因此维护队列的总时间复杂度为O(∣Σ∣),由于 s 包含的字符种类数一定小于 s 的长度,因此 O(∣Σ∣) 在渐进意义下小于 O(n),可以忽略。
空间复杂度:O(∣Σ∣),其中Σ 是字符集,在本题中 s 只包含小写字母,因此 ∣Σ∣≤26。我们需要 O(∣Σ∣) 的空间存储哈希映射以及队列。
作者:LeetCode-Solution
链接:https://leetcode-cn.com/problems/di-yi-ge-zhi-chu-xian-yi-ci-de-zi-fu-lcof/solution/di-yi-ge-zhi-chu-xian-yi-ci-de-zi-fu-by-3zqv5/