【问题标题】:Find frequency of a character in an array of Strings在字符串数组中查找字符的频率
【发布时间】:2013-10-16 21:28:19
【问题描述】:

给定一个字符串数组,找出特定字符出现的频率。

例如。给定数组 {"hon","bhig","zzz","hello"} 和字符 'h',输出为 3。

我是这样解决的: 方法 1:遍历数组中的每个字符串,每次在当前字符串中出现该字符时递增一个计数器。运行时间为 O(n),其中 n 是数组中所有字符串的累积长度。

方法 2:这可以使用 HashMap 进行优化;如果字符串在数组中重复,这将特别有用。这就是我所做的:获取一个 HashMap,其中键 = 字符串,值 = 字符串在数组中出现的次数。将给定数组中的所有字符串连同它们的计数一起放入 HashMap。然后遍历 HashMap 中的每个键值对,计算给定字符在键(字符串)中出现的次数,并将其在 HashMap 中的对应值递增。

我的问题是:有没有更好的方法来做到这一点?

代码如下:

注意:请阅读完整的已接受答案。

public static int findFreq(String[] arr,char c) {
    Map<String,Integer> map  = new HashMap<String,Integer>();
    for(int i=0;i<arr.length;i++) {
        if(map.containsKey(arr[i])) 
            map.put(arr[i],map.get(arr[i])+1);
        else
            map.put(arr[i], 1);
    }
    int freq=0;
    for(Entry<String,Integer> entr:map.entrySet()) {
        String s = entr.getKey();
        for(int i=0;i<s.length();i++) {
            if(s.charAt(i)==c)
                freq += entr.getValue();
        }
    }
    return freq;
}

【问题讨论】:

  • 看到你将不得不查看数组中的每个单独的字符来解决这个问题,你永远不会比 O(n) 做得更好。我看不出用字符串做地图有什么帮助(事实上,如果你再也不想看arr,你就不需要地图了)。如果你想保留它,我会从字母表中的每个字母映射到它出现的次数(即h --&gt; 3)。
  • 计算字符串的哈希码需要查看每个字母。假设哈希码可能已经计算过一次(并因此被缓存),第二种方法可能需要更多的工作,并且(平均而言)不能减少工作。仅当字符串计数显着大于 1 时,才能节省成本。

标签: java arrays string algorithm


【解决方案1】:

抱歉,我认为方法 2 会减慢速度。为了将每个字符串添加到HashMap,该方法计算哈希码,它查看字符串中的每个字符。因此,设置HashMap 已经查看了每个字符串中的每个字符,这与您对方法 1 所做的工作一样长,而且您必须再次通过地图。

【讨论】:

  • 另外,如果字符串在数组中重复,地图只会提供节省。发布的示例数组 OP 根本没有节省。
【解决方案2】:

方法 2 不是很优化,您真正应该做的是创建一个 Map&lt;Character,Integer&gt; 然后您不需要计算第二个循环,但您需要循环每个字符串中的每个字符。

方法 1,取决于您的实现,也只计算字符串中出现的每个字符,是否考虑该字符是否出现两次,例如 "hash"

任何一种方法都需要比较EACH字符串中的EACH字符然后计数

方法 2 应该是这样的

public static int findFreq(String[] arr,char c) {
    Map<Character,Integer> map  = new HashMap<Character,Integer>();
    for(int i=0;i<arr.length;i++) {
        for(Character ch : arr[i].toCharArray()){
            if(map.containsKey(ch)) 
                map.put(ch,map.get(ch)+1);
            else
                map.put(ch, 1);
        }
    }
    return map.get(Character.valueOf(c));
 }

无论哪种方式,这两种方法都是 O(n),来自 docs for HashMap

此实现为基本操作(get 和 put)提供恒定时间性能

但这就是说,即使使用我上面提供的方法,在填充地图时也需要额外的 get

因此,如果用于单个搜索,方法 1 更好,如果重复使用,则方法 2 是要走的路(但在方法之外填充地图)

为您提供的一些指标:

Number of Words  |    Array (approach 1)   |   Map (My approach 2)  |  Map (your approach 2)
                 |       (time in ms)      |     (time in ms)       |      (time in ms) 
                 |     (groovy)/(java)     |     (groovy)/(java)    |     (groovy)/(java)     
-------------------------------------------------------------------------------------------
      43303      |         118 /  5        |         229 / 34       |             / 16     
     417221      |         852 / 10        |        1088 / 120      |             / 49
    2086705      |        2929 / 45        |        5064 / 731      |             / 219

我撤回了我的方法,看来您的 Map 方法更快!

这是我的数组方法(以防你的不同)

private static int findFreqArray(String[] arr, char c){
    int count = 0;
    for(int i=0;i<arr.length;i++) {
        for(char ch : arr[i].toCharArray()){
            if(ch == c)
                count++;
        }
    }
    return count;  
}

【讨论】:

  • 非常感谢您提供的指标,这确实消除了我的一些疑问。是的,正如许多人所建议的那样,方法 1 似乎是最快的。
  • 是的,为什么,我很惊讶你的地图方法在 2 个循环中更快,但现在我看了一下,在我的方法中调用 arr[i].toCharArray() 可能会减慢它的速度
  • 可能更像是映射每个字符,而数组方法(和您的映射)仅在字符匹配时才映射。
【解决方案3】:

方法 1 在这里更可取。在最坏的情况下,他们中的任何一个的成本是O(N)。使用HashMap&lt;String&gt; 记住旧访问字符串(具有固有的散列成本)的第二种方法不会带来值得一提的性能改进。我们应该避免过早的优化,因为approach 1更简单

【讨论】:

    【解决方案4】:

    不一定。 另一种可能性是将您的数组“展平”为单个字符串并在其中搜索单个字符(与您的变体 1 相同)。这可能会稍微加快速度,但不一定会使代码“更好”。在此SO answer 中可以找到字符串中字符搜索的示例。

    【讨论】:

      【解决方案5】:

      不,你永远不会只做一次搜索就比 O(n) 做得更好。但是,如果您要针对同一个数组多次搜索不同的字符,您可以从遍历数组开始并构建一个从每个字符到其出现次数的哈希映射。然后,对于每次搜索,您只需进行简单的常数时间查找,而不是 O(n) 搜索。

      【讨论】:

        【解决方案6】:

        Hashmap 比第一个更慢。两种算法都需要从每个字符传递一次,因此都需要 O(n) 时间。但第一个更简单,执行的代码行更少。

        不错的尝试:)

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2011-05-07
          • 1970-01-01
          • 1970-01-01
          • 2020-06-19
          • 1970-01-01
          相关资源
          最近更新 更多