算法数据结构面试分享(八)寻找第一个不重复的字符

寻找第一个不重复的字符

   今天我们一起来看这道题:寻找第一个不重复的字符。原题的描述是: Write an efficient function to find the first nonrepeated character in a string. For instance, the first nonrepeated character in “total” is ‘o’ and the first nonrepeated character in “teeter” is ‘r’. Discuss the efficiency of your algorithm. 

一、确保我们理解了问题,并且尝试一个例子,确认理解无误。 
    题目当中有两个核心要点, 1. Efficient function 可以理解为最高效的算法。2. total中我们的目标是找出‘o', teeter 是 'r'。 先忽略什么是最高效的,先满足功能需求。total中第一个字符是t, 这个字符串中t在后面出现过,所以t要忽略掉,第二个字符是o, 它在后面没有出现过。那就是我们需要找的目标字符啦。再看第二个例子, teeter,t在后面也出现过,e出现过,之后的t和e也都出现过,而r只有一个。
    好了,我们应该理解这个问题了。不过大家还有其他问题需要和面试官确认的吗?
    1. 空字符串怎么办? (能提出这个问题的同学应该知道字符串是引用类型,给自己鼓个掌哈。)
    2. 如果所有的字符都重复过怎么办?(确实有这样的场景哈,如Woow,我们该返回什么呢?我觉得返回什么字符都不是最好的解决方法,那我们也抛异常吧,你怎么看?)
    3. 大小写字符算是相同的还是不同的 (不同)


二、想想你可以用什么方法解决问题,你会选择哪一种,为什么?
    上面的解释中,我们发现有一个方法是工作的。 就是拿到当前的字符串,从头到尾再扫描一遍,如果有多余一个字符和当前字符相等,说明重复了,我们继续。如果只有一个,说明也就只有它本身了,那么扫描结束的时候我们就可以返回该字符了。如果最后一个字符都已经判断完了,还没有返回,说明我们没有找到这样的字符存在,抛出异常。
    大家能够发现,这样的做法,其实有两个for循环。第一个for是依次拿出当前的字符,第二层的for是从头到尾扫描,和当前的字符比较。所以我们的复杂度是O(n*n)对吧?
    有没有更好的办法呢?大家还记得之前的文章里我们使用过计数排序对吧?我们设想一下,如果我们能够给没有字符计数,之后再扫描一遍,依次判断它出现的次数,如果次数是1我们是不是就找到呢?如何计数呢? 大家想一下,字符可能有多少种可能呢?这里不能只考虑a-z, A-Z哦,因为题中提到的是字符串,万一他包含一些特殊字符呢?字典可以解决这个问题。我们想找到一种简单的方法,而不是库函数。
  
三、 解释你的算法和实现的方法
 我们现在就看这二种算法,复杂度为O(n)的。我们想一下char是两个字节的对吧,那么它能表示的范围是0-255,至于char和int之间的关系,大家应该还记得,ASCII码对吧。(不清楚的同学翻翻书,也可以加我微信交流,后期我打算录一些视频教程)。
所以核心的代码有:统计计数
             int[] counter = new int[256];
                foreach(var c in input)
                {
                    counter[c]++;
                }

还有一块是,扫描字符串,取当前的字符判断个数:
            foreach(var c in input)
                {
                    if(counter[c] == 1)
                    {
                        return c;
                    }
                }

四、写代码的时候,记住,一定要解释你现在在干什么 
    直接上代码,什么数组,扫描计数,扫描取出计数做判断。
  public static char FindFirstNoRepeat(string input)
        {
            if(input !=null)
            {
                int[] counter = new int[256];

                foreach(var c in input)
                {
                    counter[c]++;
                }

                foreach(var c in input)
                {
                    if(counter[c] == 1)
                    {
                        return c;
                    }
                }

                throw new Exception("没有找到只出现一次的字符");
            }

            throw new ArgumentNullException("字符串不能为空");
        }

五、 Workthrough
六、 修复缺陷
我们来测试一下这个方法:
 static void Main(string[] args)
        {
            string input = "total";
            var r = FindFirstNoRepeat(input);
            Console.WriteLine(r);
            Debug.Assert(r == 'o', "total 中第一个不重复的字符是 o");

            try
            {
                r = FindFirstNoRepeat("woow");
                Debug.Assert(false, "字符串 woow 中没有不重复的字符");
            }
            catch (Exception e)
            {
                Debug.Assert(true);
            }
        }

欢迎大家关注我的公众号,还有我的系列视频教程, 数据结构与算法 微软经典算法面试题辅导。大家有什么更好的解法,也欢迎讨论哈。

算法数据结构面试分享(八)寻找第一个不重复的字符_第1张图片

你可能感兴趣的:(经典面试-算法数据结构)