面试题35:第一个只出现一次的字符
1.题目描述
题目:字符串中第一个只出现一次的字符。在字符串中找出第一个只出现一次的字符。
例如,如输入"abaccdeff",则输出b。
2.题目分析
由于题目与字符出现的次数相关,那么我们是不是可以统计每个字符在该字符串中出现的次数?要达到这个目的,我们需要一个数据容器来存放每个字符的出现次数。在这个数据容器中,可以根据字符来查找它出现的次数,也就是说这个容器的作用是把一个字符映射成一个数字。在常用的数据容器中,哈希表正是这个用途。
为了解决这个问题,我们可以定义哈希表的键值(Key)是字符,而值( Value)是该字符出现的次数。同时我们还需要从头开始扫描字符串两次。第一次扫描字符串时,每扫描到一个字符,就在哈希表的对应项中把次数加1。接下来第二次扫描时,每扫描到一个字符,就能从哈希表中得到该字符出现的次数。这样,第一个只出现一次的字符就是符合要求的输出。
哈希表是一种比较复杂的数据结构,C++标准模板库中的map和unordered map实现了哈希表的功能,我们可以直接拿过来用。由于本题的特殊性,我们其实只需要一个非常简单的哈希表就能满足要求,因此我们可以考虑实现一个简单的哈希表。字符(char)是一个长度为8的数据类型因此总共有256种可能。于是我们创建一个长度为256的数组,每个字母根据其ASCI码值作为数组的下标对应数组的一个数字,而数组中存储的是每个字符出现的次数。这样我们就创建了一个大小为256、以字符ASCI码为键值的哈希表。
第一次扫描时,在哈希表中更新一个字符出现的次数的时间是O(1).如果字符串长度为n,那么第一次扫描的时间复杂度是O(n).第二次扫描时,同样在O(1)时间内能读出一个字符出现的次数,所以时间复杂度仍然是O(n).这样算起来,总的时间复杂度是O(n).同时,我们需要一个包含256个字符的辅助数组,它的大小是1KB.由于这个数组的大小是一个常数,因此可以认为这种算法的空间复杂度是O(1).
3.code
#include <cstdio>
#include <string>
char FirstNotRepeatingChar(const char* pString)
{
if(pString == nullptr)
return '\0';
const int tableSize = 256;
unsigned int hashTable[tableSize];
for(unsigned int i = 0; i < tableSize; ++i)
hashTable[i] = 0;
const char* pHashKey = pString;
while(*(pHashKey) != '\0')
hashTable[*(pHashKey++)] ++;
pHashKey = pString;
while(*pHashKey != '\0')
{
if(hashTable[*pHashKey] == 1)
return *pHashKey;
pHashKey++;
}
return '\0';
}
// ====================测试代码====================
void Test(const char* pString, char expected)
{
if(FirstNotRepeatingChar(pString) == expected)
printf("Test passed.\n");
else
printf("Test failed.\n");
}
int main(int argc, char* argv[])
{
// 常规输入测试,存在只出现一次的字符
Test("google", 'l');
// 常规输入测试,不存在只出现一次的字符
Test("aabccdbd", '\0');
// 常规输入测试,所有字符都只出现一次
Test("abcdefg", 'a');
// 鲁棒性测试,输入nullptr
Test(nullptr, '\0');
return 0;
}