第一个只出现一次的字符(字符流中第一个只出现一次的字符)

题目

  在一个字符串(1<=字符串长度<=10000,全部由字母组成)中找到第一个只出现一次的字符,并返回它的位置

思路

  从头开始扫描这个字符串中的每个字符。当访问到某字符时拿这个字符和后面的每个字符相比较,如果在后面没有发现重复的字符,则该字符就是只出现一次的字符。如果字符串有 n 个字符,每个字符可能与后面的 O(n)个字符相比较,因此这种思路的时间复杂度是 O(n^2)。

二,记录法

  由于题目与字符出现的次数相关, 我们是不是可以统计每个字符在该字符串中出现的次数?要达到这个目的,我们需要一个数据容器来存放每个字符的出现次数。在这个数据容器中可以根据字符来查找它出现的次数,也就是说这个容器的作用是把一个字符映射成二个数字。在常用的数据容器中, 哈希表正是这个用途。

  为了解决这个问题,我们可以定义哈希表的键(Key)是字符, 而值(Value )是该字符出现的次数。同时我们还需要从头开始扫描字符串两次。第一次扫描字符串时,每扫描到一个字符就在哈希表的对应项中把次数加 1 。接下来第二次扫描时, 每扫描到一个字符就能从哈希表中得到该字符出现的次数。这样第一个只出现一次的字符就是符合要求的输出。

  第一次扫描时,在哈希表中更新一个字符出现的次数的时间是 O(n) 。如果字符串长度为 n, 那么第一次扫描的时间复杂度是 O(n)。第二次扫描时,同样 0(1)能读出一个字符出现的次数,所以时间复杂度仍然是 O(n)。这样算起来,总的时间复杂度是 O(n)。

#include <iostream>
#include <map>
#include <algorithm>
using namespace std;

class Solution
{
    public:
        void get_norepeat_char(const string &s);    
};
void Solution::get_norepeat_char(const string &s)
{
    if(s.empty()||s.length()<=0)
        return ;
        
    map<char,int> mp;
    for(int i=0;i<s.length();++i)
        mp[s[i]]++;

    //也可用函数对象方式根据value找key,
    //对迭代器进行解引用时,将获得一个引用,
    //指向容器中一个value_type类型的值。对于map容器,其value_type是pair类型
    auto it=find_if(mp.begin(),mp.end(),[](const map<char,int>::value_type item)
                    {
                        return item.second==1;
                    });
    if(it==mp.end())
        return ;
    cout<<it->first<<endl;
}
    
int main()
{
    string str;
    cin>>str;
    
    Solution s;
    s.get_norepeat_char(str);
    return 0;
}

拓展 

关于map 

 1.map是<键—值>对的集合 如:map< string ,int> word_count。map类型通常可理解为关联数组:可使用键作为下标来获取一个值,正如内置数组类型一样。而关联的本质在于元素的值与某个特定的键相关联,而并非通过元素在数组中的位置来获取。

  2.对于键类型,唯一得约束就是必须支持 < 操作符,其他不作要求。所以,不能拿(list 类型的迭代器)做键类型,因为list 的迭代器不支持算术运算,也不支持关系运算。它只支持前置和后置的自增、自减运算以及相等(不相等)运算。

  3.value_type该类型比前面介绍的容器所使用的元素类型要复杂得多,value_type(是使用泛型时所加的类型) 是存储元素的键以及值的pair类型,而且键为const。所以键初始化之后是不能再被赋值的

  4.需要谨记value_type 是pair类型的,它的值成员可以修改,但键成员不能修改!!!!。

  5.使用下标访问map与使用下标访问数组或vector的行为截然不同:用下标访问不存在的元素将导致在map容器中添加一个新的元素,它的键即为该下标值
  6.有别于vector或string类型,map下标操作符返回的类型与对map迭代器进行解引用获得的类型不相同。

  7.map迭代器返回value_type类型(pair类型)的值—–包括const key_type 和mapped_type类型成员的pair对象;下标操作符返回的是存储在map里面的值,是一个mapped_type类型的值。
  8.map类额外定义了两种类型:key_type、mapped_type,以获得键或值的类

思维拓展

  如果需要判断多个字符是不是在某个字符串里出现过或者统计多个字符在某个字符串出现的次数,那么可以考虑基于数组创建一个简单的哈希表,这样可以用小空间换来时间效率的提升。

相关题目

  英语中,如果两个单词出现的字母相同,并且每个字母出现的次数也相同,就称这两个单词为变位词,如:silent与listen,evil和live,写一个函数,判断两个词是不是变位词。

思路  

  用map容器扫描第一个单词,扫描到每个字符时,用字符做key,相应的value+1,之后在扫描第二个单词,扫描到相应的字符时,对应得value-1,如果第二次扫描完成后,哈希表中的value都是0,则两个字符互为变位词。

 

题目

  请实现一个函数,用来找出字符流中第一个只出现一次的字符。例如,当从字符流中只读出前两个字符“go”时,第一个只出现一次的字符是‘g’;当从该字符流中读出前6个字符“google”时,第一个只出现一次的字符是’l’。

思路

  256个字符的hash表,与上面不同的是这是一个流的问题,所以我们分别需要实现插入操作和判断当前元素中只第一个出现一次的字符

class Solution
{
public:
  //Insert one char from stringstream
    void Insert(char ch)
    {
        s+=ch;
        ++hash[ch];
        return;
    }
  //return the first appearence once char in current stringstream
    char FirstAppearingOnce()
    {
        for(int i=0;i<s.size();++i)
            if(hash[s[i]]==1)
                return s[i];
        return '#';
    }
private:
    int hash[256];
    string s;
};