乍一看这题,非常简单,直接从头开始暴力搜索,双重循环搞定。1min就敲出了代码:
(错误示范)
#include<string.h>
int firstUniqChar(char * s){
int len=strlen(s);
int i,j;
int flag;
for (i=0; i<=len-1; i++)
{
flag=0;
for (j=0; j<=len-1; j++)
{
if (s[i]!=s[j] && i!=j) flag++;
}
if (flag==len-1) return i;
}
return -1;
}
但是执行以后就gg:
这很显然是因为双重for循环时间复杂度为O(N^2),对于很长的字符串,耗费的时间就会相当多,必然是超限。
那么,我们就不得不想个办法降低复杂度O(N)了。
之前,在初步学习C语言时,我有讲过所谓的“计数器judge”(我自己起的名字)的方法。
看“计数器judge”点这里!
这道题其实可以用计数器,降低复杂度。最后实现的代码是:
int firstUniqChar(char * s){
int len=strlen(s);
int i;
int judge[1000]={0};
for(i=0; i<=len-1; i++)
{
judge[s[i]]++;
}
for (i=0; i<=len-1; i++)
{
if (judge[s[i]]==1) return i;
}
return -1;
}
虽然使用了两次for循环,但是复杂度压低到了O(N),自然也就通过了:
当然,这个计数器还可以做优化处理,让他的运行速率更快一些。
之前的代码judge开辟了1000个空间去存储,而其实大可不必,我们可以这样来做:
int firstUniqChar(char * s){
int len=strlen(s);
int i;
int judge[26]={0};
for(i=0; i<=len-1; i++)
{
judge[s[i]-'a']++;
}
for (i=0; i<=len-1; i++)
{
if (judge[s[i]-'a']==1) return i;
}
return -1;
}
唯一的区别在于judge只开放了26个空间,计数器的下标是“该元素ASCII-字符a的ASCII”的值,这样就可以防止给judge分配多余的无用的空间。这样做,效率更高了:
在笔者的学习中,我了解到这种所谓的“计数器judge”,其本质是“哈希表”(Hash Table)。
judge数组是一个简单的哈希表,judge数组的下标是字符对应的ASCII码,也就是该哈希表的关键词(key)。在一些统计、查找重复的问题中,哈希表是首选的数据结构。
在日后的学习中,我会对哈希表做更深入的学习,现在就姑且理解到这一层吧。