题目描述
请实现一个函数用来找出字符流中第一个只出现一次的字符。例如,当从字符流中只读出前两个字符"go"时,第一个只出现一次的字符是"g"。当从该字符流中读出前六个字符“google"时,第一个只出现一次的字符是"l"。
思路
【注意】
- 这里都要考虑到字符串为空的情况,如果为空,返回’\0’;
- 如果字符串中不存在只出现一次的字符,也返回’\0’;
方法一 字符串扫描(笨方法)
第一种方法相对比较笨,但是也最容易被想到和实现。我们需要两个for循环来实现,外层循环从头开始遍历字符串,其指向的是当前要判断的字符的下标。内层循环从头开始遍历字符串,如果遇到和外层循环相同的字符并且它们的下标不同,那么该字符就不是出现一次的字符,外层循环++,继续判断下一个字符,这是内层循环又要从头开始扫描字符串。
//查找第一个只出现一次的字符
char findFirstChar(char *pString)
{
if(pString == "") return '\0'; //如果字符串为空,返回'\0';
char *temp = pString;
temp = pString;
for(int i = 0; temp[i] != '\0'; i++)
{
int j;
for(j = 0; temp[j] != '\0'; j++)
{
if(temp[i] == temp[j] && i != j) //如果遇到和外层循环相同的字符并且它们的下标不同,直接退出循环,继续判断下一个元素
break;
}
if(temp[j] == '\0') //如果内存循环已经扫描到字符串结尾,说明下标为i的元素只出现一次,可以直接返回
{
return pString[i];
}
}
return '\0'; //字符串中不存在只出现一次的字符,也返回'\0';
}
【分析】上面的代码时间复杂度为O(n),性能很低,每判断一个字符,都需要从头开始扫描字符串
下面,让我介绍利用哈希表实现查找的方法
方法二 哈希表
我们可以定义哈希表的键值(Key)是字符的ASCII值,而值(Value)是该字符出现的次数【hashTable[‘s’] = 1 (s就是哈希表的键值,而1就是哈希表的value)】。同时我们需要扫描两次字符串,第一次扫描字符串时,每扫描到一个字符就在哈希表的对应项中把次数加1。接下来第二次扫描的时候,没扫描到一个字符就能在哈希表中得到该字符出现的次数。找出第一个Value为1的那个key就是我们需要找到那个字符
char findFirstChar2(char *pString)
{
//空字符串
if(pString == "")
return '\0';
// 1、初始化
/*
定义一个哈希表;为什么是256, 因为字符的ASCLL表表示256个字符;
我们是以字符的ASCLL值为数组下标,而字符的出现次数为数组内容的;
*/
int hashTable[256]; //定义一个哈希表
for( int i = 0; i < 256; ++ i)
hashTable[i] = 0;
//2、以字符作为数组下标,求每个字符出现的次数
char* pHashKey = pString;
while(*pHashKey != '\0')
{
hashTable[*pHashKey] ++;
pHashKey++;
}
// 3、找到第一次出现一次的字符,就是在数组中第一次存储为1所对应的字符
pHashKey = pString;
while(*pHashKey != '\0')
{
if(hashTable[*pHashKey] == 1)
return *pHashKey;
pHashKey++;
}
return '\0';
}
【分析】哈希表对于查找时十分方便,当我们遇到查找问题时,可以优先考虑一下哈希表
方法三 map容器
其实用map和哈希表的实现原理类似,其都是通过将键与其值相映射
static char findFirstChar3(char *pString)
{
string str = ""; //用字符串保存读取的字符流,,方便后面求字符串大小
map<char, int> hashTable; //声明map实现哈希表,存放对应字符出现的个数( 字符:出现次数)
int ch;
while(*pString != '\0')
{
str += *pString;
hashTable[*pString++]++; // hashTable['c'] = 1;
}
for(int i = 0; i < str.size(); i++)
{
if(hashTable[str[i]] == 1)
{
return str[i];
}
}
return '\0';
}