【剑指offer】面试题35-第一个只出现一次的字符

问题描述:

在字符串中找出第一个只出现1次的字符。如输入“abaccdeff”,输出b。


问题分析:

方法1:最直白最粗暴的方法。
具体实现方法:对于字符串中的每一个字符,都去遍历字符串,查找是否是只出现了一次。这样一来,时间复杂度是O(N*N),N是字符串中字符的个数。这样的办法往往不是最好的办法。
方法2:哈希法。
既然上边的方法时间复杂度太高,我们可以采用空间换取时间的哈希法去解决问题。
具体实现方法:定义一个256的整形数组(为什么是256?因为一个字符是1个字节,8个比特位,可以表示256个字符),统计字符串中字符出现的次数,统计完成之后,再去找第一个出现1次的字符。这样一来,时间复杂度就是O(N)(统计整个字符串中字符出现的次数时遍历一次),空间复杂度是O(1)(无论字符串中有多少个字符,只需要开辟256个整形即可)。当然了,我们也可以使用map这个关联容器去统计字符串中字符出现的次数,然后找出第一个只出现1次的字符。


代码实现:

方法1的代码不予实现,因为在面试中一般不会得到面试官的喜爱。
方法2:

//利用数组存储统计结果:
char GetFirstNotRepeating(const string& s)
{
    if(s.size() == 0)
       return '\0';
    int HashArray[256] = {0};
    //统计字符出现的次数
    for(size_t i = 0; i < s.size(); ++i)
    {
       HashArray[s[i]]++;
    }
    for(size_t i = 0; i < s.size(); i++)
    {
       if(HashArray[s[i]] == 1)
           return s[i];
    }
    return '\0';
}
int main()
{
    string s;
    getline(cin,s);
    char ret = GetFirstNotRepeating(s);
    cout<<ret<<endl;
    system("pause");
    return 0;
}
//利用map去存储统计结果:
char GetFirstNotRepeating(const string& s)
{
    if(s.size() == 0)
       return '\0';
    map<char,int> m;
    map<char,int>::iterator it = m.begin();
    for(int i = 0; i < s.size(); ++i)
    {
       m[s[i]]++;//operator[],没有则进行插入,有则返回value的引用
    }
    for(int i = 0; i < s.size(); ++i)
    {
       it = m.find(s[i]);//如果找到,返回当前位置的迭代器。否则返回map::end
       if(it->second == 1)
           return s[i];
    }
    return '\0';
}
int main()
{
    string s;
    getline(cin,s);
    char ret = GetFirstNotRepeating(s);
    cout<<ret<<endl;
    system("pause");
    return 0;
}

相关题目:

1.从第一个字符串中删除第2个字符串中出现过的所有字符。比如输入第一个字符串”you are good”,第二个字符串”root”,输出”yu ae gd”.
思路分析:创建一个用数组实现的简单哈希表来存储第二个字符串中字符出现的情况,然后在第一个字符串中删除对应的字符。
代码实现:

void Erase(string s1,const string& s2)
{
    if(s1.size() == 0 || s2.size() == 0)
       return;
    string::iterator it = s1.begin();
    bool hash[256]={false};
    for(int i = 0; i < s2.size(); ++i)
    {
       hash[s2[i]] = true;
    }
    for(int i = 0; i < s1.size();)
    {
       if(hash[s1[i]] == true)
           s1.erase(it + i);
       else
           ++i;
    }
    cout<<s1<<endl;
}
int main()
{
    string s1;
    string s2;
    /*cin>>s1;
    cin>>s2;*///尽量不要这样写
    getline(cin,s1);
    getline(cin,s2);
    Erase(s1,s2);
    system("pause");
    return 0;
}

2.删除字符串中所有重复出现的字符。比如输入”google”,输出”gole”。
思路分析:用哈希表存储字符串中字符是否出现。
代码实现:

void EraseRepeating(const string& s)
{
    if(s.size() == 0)
       return;
    bool hash[256] = {false};
    for(int i = 0; i < s.size(); ++i)
    {
       hash[s[i]] = true;
    }
    string tmp;
    for(int i = 0; i < s.size(); ++i)
    {
       if(hash[s[i]] == true)
           tmp.push_back(s[i]);
       hash[s[i]] = false;
    }
    cout<<tmp<<endl;
}
int main()
{
    string s;
    getline(cin,s);
    EraseRepeating(s);
    system("pause");
    return 0;
}

3.判断两个单词是否互为变位词。变位词就是如果两个单词出现的字母相同并且字母出现的次数也相同,那么这两个单词互为变位词。
思路分析:将一个单词中的字母及出现的次数都使用一个简单的哈希数组来存储,另一个单词直接去数组进行比对,出现一个字母,将对应的位置的次数减1,如果数组最后变为0,说明这两个单词互为变位词。
代码实现:

bool IsAnagram(const string& s1,const string& s2)
{
    if(s1.size() == 0 || s2.size() == 0)
       return true;
    int hash[256] = {0};
    for(int i = 0; i < s1.size(); ++i)
    {
       hash[s1[i]]++;
    }
    for(int i = 0; i < s2.size(); ++i)
    {
       hash[s2[i]]--;
    }
    for(int i = 0; i < 256; ++i)
    {
       if(hash[i] != 0)
           return false;
    }
    return true;
}
int main()
{
    string s1;
    string s2;
    getline(cin,s1);
    getline(cin,s2);
    bool ret = IsAnagram(s1,s2);
    cout<<ret<<endl;
    system("pause");
    return 0;
}

总结:

1.输入一个字符串的时候,尽量不要使用cin,因为cin输入字符串的时候,会忽略字符串中的空格符,制表符等等。而是采用getline()函数,getline()函数的使用方法:
string s;
getline(cin,s);
2.使用string::erase()函数时候注意:erase()之后会返回删除元素的下一个位置,所以删除成功时迭代器不需要向后移动。
3.map的operator[],当前的key值map中不存在,则插入;否则返回当前元素对应的value值的引用。


关于查找第一个只出现1次的字符,原本以为是非常好写的题目,没想到简单的题目却引发了很多问题,很有收获~~如果有问题,请留言~

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值