链接:387. 字符串中的第一个唯一字符 - 力扣(LeetCode)
题目描述
给定一个字符串 s
,找到 它的第一个不重复的字符,并返回它的索引 。如果不存在,则返回 -1
。
示例 1:
输入: s = "leetcode" 输出: 0
示例 2:
输入: s = "loveleetcode" 输出: 2
示例 3:
输入: s = "aabb" 输出: -1
解题思路
方法一:数组存计数
第一遍先统计每个字符出现的次数
,第二遍再次从前往后遍历字符串s中的每个字符,如果某个字符出现一次直接返回。
实现
采用一个用于计数的数组vec大小为英文字母个数26,将所有元素都初始化为0。和一个将字符串转为char类型的指针数组data用于遍历,统计字符时用data[i] - 'a'获取字母所在位置,然后将计数数组的对应位置加一。第二次遍历字符串,判断出现个数是否为1,是则返回下标 i ,否则继续遍历,若最后跳出循环也未找到唯一字符则返回0。
代码
class Solution {
public:
int firstUniqChar(string s) {
int vec[26]={0};
// memset(vec,0,sizeof(26));
char* data = s.data();//和const char* data = s.c_str效果一样
// 先统计每个字符出现的次数
for(int i=0;i<s.size();++i){
vec[data[i]-'a']++;
}
// 然后在遍历字符串s中的字符,如果出现次数是1就直接返回
for(int i=0;i<s.size();++i){
if(vec[data[i]-'a']==1){
return i;
}
}
return -1;
}
};
结果
注意
在C/C++中,字符串是以null终止的字符数组。使用下标访问字符串s[i]是合法的,可以获取到第i个字符。然而,直接使用下标访问字符串可能会导致以下问题:
-
不可变性:字符串是不可变的,也就是说,不能直接通过s[i]的方式来修改字符串中的某个字符。
-
内存越界
当需要对字符串进行增删改查、赋值等操作时,将字符串转换为char型数组可以更方便地进行操作。可以直接访问和修改特定位置上的字符,而不受字符串不可变性的限制。此外,字符数组不带有终止符(‘\0’),允许在数组中存储任意字符,包括特殊字符,这在某些场景下是有用的。
方法二:哈希表存计数
和上面的一样,先统计每个字符的数量,然后再查找。
实现
用哈希表代替上述的数组实现同样的计数,哈希表的第一维存储字母,第二维存储计数
代码
class Solution {
public:
int firstUniqChar(string s) {
unordered_map<int,int> hash;//哈希表
for(char ch:s){
hash[ch]++;//计数加一
}
for(int i=0;i<s.size();++i){
if(hash[s[i]]==1){//寻找只出现一次的字母
return i;
}
}
return -1;
}
};
结果
用时要比方法一多。
方法三:哈希表存索引
与方法二不同,本方法第一次遍历用哈希表存索引,使得第二次遍历不是查找字母本身而是哈希映射。
实现
具体地,利用哈希表的查询键的函数count判断所查字母是否第一次出现,若返回次数大于0则说明该字母不是第一次出现,则将对应值设为-1,若返回0,说明该字母是第一次出现,则将相应值设为所在字符串的索引。
第二次再遍历哈希表的所有值,查找不为-1且最小的值,这个值即为索引。若最后遍历完哈希表还是没有找到,说明不存在唯一字母,返回-1 。要找最小索引,则初始设为最大索引。
代码
class Solution {
public:
int firstUniqChar(string s) {
unordered_map<int,int> hash;//哈希表
for(int i=0;i<s.size();++i){
if(hash.count(s[i])){ //count函数用于统计某个键的出现次数
hash[s[i]]=-1; //哈希表已经出现过相同字母,不是第一次出现
}else{ //未找到相同字母,则将该字母作为键,索引作为值存入哈希表
hash[s[i]]=i;
}
}
int first=s.size();
for(auto [_,pos]:hash){ //[_,pos]对哈希表的索引,只查值
if(pos!=-1 && pos<first){ //找到只出现一次的字母且保证是第一次出现
first=pos;
}
}
if(first==s.size()){
first=-1; //没有找到只出现一次的字母
}
return first;
}
};
结果
时间更长了
心得
由于这里只需要对26个字母赋值,小数据量直接对数组赋初值比memset函数所用时间更短;如果数据量很大用memset函数比直接赋值要快一些。
字符串转字符数组的两种方法:const char* data = s.data();和const char* data = s.c_str
哈希表是键值对存储的,声明时用unordered_map<int,int> hash;其中键的类型可变,本题中将int改为char,运行时间变长了。
哈希表的count函数用于统计某个键的出现次数,找不到则返回0
哈希表遍历时可以只遍历值 for(auto [_,pos]:hash)
找最小(大)值时,变量初始设为最大(小)值