一道算法题

今天面试的一道算法题,当时解得不太好,各种被面试官问住,回家在地铁上想到了如下的解法。

问题:请设计算法查找出一个字符串中重复出现最多的字符以及次数

一开始我是这么写的,也是最普通的写法,两个循环,设两个临时变量就输出结果

static private void method_derectLoop(String s)
        {
            Char[] c_s = s.ToCharArray();
            Int32 i_Count = 0;
            Char c_Char = ' ';
            //i_hasCounted是数过的相同字符数的总的个数,
            for (Int32 i = 0; i < c_s.Length; i ++)
            {
                //i_tempCount是一个临时的变量,包含了c_s[i]在字符串中出现的个数
                Int32 i_tempCount = 0;
                for (Int32 j = i; j < c_s.Length; j++)
                {
                    if (c_s[i] == c_s[j])
                    {
                        i_tempCount++;
                    }
                }
                if (i_tempCount > i_Count)
                {
                    i_Count = i_tempCount;
                    c_Char = c_s[i];
                }
            }
           Console.WriteLine("Count="+i_Count);
           Console.WriteLine("Char=" + c_Char);
           //Console.WriteLine(new String(c_s));
        }

这个解法面试官不是很满意,因为已经计算过次数的元素还是需要继续遍历一遍,这个时候我说那建立一个数组去存储已经计算过次数的元素,但是这种方法会多一个循环,在元素过多的情况下效率也不高,当时确实没有什么好的解法,就只好作罢,此时镜头一转,来到了地铁10号线上一个低头思考的同学身上,我终于想到了一个办法可以跳过已经计算的元素,那就是每计算一次交换一下元素的位置,计算完一次之后将所有相同的元素放在数组的开头,然后跳过这些元素从下一个开始,代码如下

static private void method_skipLoop(String s)
        {
            Char[] c_s = s.ToCharArray();
            Int32 i_Count = 0;
            Int32 i_hasCounted = 0;
            Int32 i_loopCount = 0;
            Char c_Char = ' ';
            //i_hasCounted是数过的字符数的总的个数,这些字符已经在数组的开头,可以直接跳过
            for (Int32 i = 0; i < c_s.Length; i = i_hasCounted)
            {
                //i_tempCount是一个临时的变量,包含了c_s[i]在字符串中出现的个数
                Int32 i_tempCount = 0;
                for (Int32 j = i; j < c_s.Length; j++)
                {
                    i_loopCount++;
                    if (c_s[i] == c_s[j])
                    {
                        //如果出现相同,不是同一个元素或者不是相邻元素,交换位置
                        i_tempCount++;
                        if (i != j && j != i + i_tempCount - 1)
                        {
                            Char c_tempChar = c_s[i + i_tempCount - 1];
                            c_s[i + i_tempCount - 1] = c_s[j];
                            c_s[j] = c_tempChar;
                        }
                    }
                }
                if (i_tempCount > i_Count)
                {
                    i_Count = i_tempCount;
                    c_Char = c_s[i];
                }
                i_hasCounted += i_tempCount;
            }
            Console.WriteLine("Count="+i_Count);
            Console.WriteLine("Char=" + c_Char);
            //Console.WriteLine(new String(c_s));
        }

第二种方法在s的比较短的时候执行的时间是会慢的,以为这个时候方法一和方法二的循环次数差不多,而方法二还需要多执行其他的操作。

不过当s的长度大于1000时区别就会出现(我的机器比较挫),当s十分长的时候方法二的优势就会显现出来了。

我们科以简单来分析一下方法二循环执行的次数,假设每个字母在s中出现的概率是相同的,有k个不同字母,s的长度为n

那么第一次循环遍历的元素为:n

第二次为:(1-1/k)n

第三次为:(1-2/k)n

...

最后一次为n/k

然后将这些加起来有

遍历次数=(((1+k-1)(k-1))2))*n=((k(k-1))/2)*n

而k<=26,所以((k(k-1))/2)可以近似为一个常数c,

所以方法二的遍历次数=cn

时间复杂度为O(n)

而方法一的时间复杂度是n^2。

后来也上网查了一下其他的解法,正在研究中...

原文地址:https://www.cnblogs.com/iiaijimaai/p/3299771.html