-
题目1:
输入一个字符串s,返回第一个出现次数为1的字符,若没有这样的字符,就返回一个空格;
字符仅有26个小写字母; -
思路:
1.哈希表 + 两次遍历:O(n),O(1)
class Solution {
public:
char firstUniqChar(string s) {
int v[26] = {
0};
for (auto c : s) {
//第一次遍历s,统计每个字符出现的次数
v[c - 'a']++;
}
for (auto c : s) {
//第二次遍历s
if (v[c- 'a'] == 1) return c;//找到第一个次数为1的字符
}
return ' ';
}
};
- 题目2:64.字符流中第一次只出现一次的字符
- 思路:
类似双顶堆求数据流的中位数那道题,本题也是一个字符流,随着不断insert输入,随时可能firstAppearingOnce查第一次只出现一次的字符;
这种动态的题,肯定不能通过一个变量维护,只能通过一个动态变化的数据结构,不断更新目标值;
随着insert输入,自然会出现重复字符,之前这些只出现一次的字符自然会慢慢消失,而第一次只出现一次的字符也一定是满足单调向后更新的,因为某字符x如果是目前第一个只出现一次的字符,那么它前面所有字符一定都是重复字符,随着不断输入,也可能输入x导致目标字符x也由于重复而消失,下一个目标字符一定出现在x的后面位置(也正因如此,y总的方法使用queue);
//自己写的
//劣势在于使用集合而不是映射,因此无法存对应字符出现的次数,因此当insert一个重复字符时,它可能是所求的目标字符,也可能是后面的,例如google,insert第二个o时,当前所求的目标字符(即第一次只出现一次的字符仍然是g),因此为了标注该字符重复了,要么删掉,要么做某些标记,但不论哪种,总需要遍历vector先找到该重复字符,才能删除或标记;而y总的方法,无论某个字符是第1次出现还是重复字符,操作都是++count[ch]
//至于查firstAppearingOnce时,采用队列的话,由于重复元素一旦出现就在insert中删掉了,队首(如果有的话)始终就是目标字符;而我的方法,每次都要遍历vector,去找到第一个false对应的字符,即便前面一段已经全部都是重复字符了,在每次查firstAppearingOnce时都会白白遍历1次;
class Solution{
public:
unordered_set<char> us;
vector<pair<char, bool>> q;//当出现重复字符时,可以选择直接从vector中erase掉,但效率低下,因此选择用一个bool表示该字符是重复字符
//Insert one char from stringstream
void insert(char ch){
if (us.find(ch) == us.end()) {
q.push_back({
ch, false});
us.insert(ch);
}
else {
for (auto& p : q)
if (p.first == ch) p.second = true;
}
}
//return the first appearence once char in current stringstream
char firstAppearingOnce(){
for (auto& x : q)
if (x.second == false) return x.first;
return '#';
}
};
//y总
class Solution{
public:
unordered_map<char, int> count;
queue<char> q;
//Insert one char from stringstream
void insert(char ch){
if (++count[ch] == 1) q.push(ch);//新来的字符放在队尾,留作后备军
while (!q.empty() && count[q.front()] > 1) q.pop();//(如果队列不为空的话)始终保持队首是第一个只出现一次的字符,一旦重复就丢弃,因为下一个只出现一次的字符一定在这个队首后边
}
//return the first appearence once char in current stringstream
char firstAppearingOnce() {
return q.empty() ? '#' : q.front();
}
};
- 总结:
y总NB!