【问题标题】:Arranging the elements of a list(with repeating elements) according to the frequency of occurrence根据出现的频率排列列表的元素(带有重复元素)
【发布时间】:2011-08-24 18:56:14
【问题描述】:

根据它们在列表中出现的频率来排列列表中的元素(带有重复元素)的好方法是什么。

我需要使用列表中最常出现的 5 个项目。

我正在考虑使用 HashMap 来计算元素的频率,方法是在每次元素出现时增加相应的计数器,然后进行 HashMap 迭代 5 次以找到最高频率。每次迭代的元素。

【问题讨论】:

    标签: java collections


    【解决方案1】:

    这种方法怎么样?

    维护一个包含计数的地图

    public static Map  <Foo,Integer>;
    

    class Foo implements Comparator<Foo>{  
          private Bar element;
    
    
          public int compare(Foo f1, Foo f2){
           return SomeClass.map.get(f1) - SomeClass.map.get(f2);
          }
    
        }
    

    只需在list 中更新地图即可。

    addFooToList()removeFooFromList()强制封装对List的访问,并在那里封装地图更新逻辑。

    【讨论】:

    • a) 这仅在多个对象“相同”而不是“相等”时才有效。 b)它将计数保持在被计数的对象内。我会称之为糟糕的设计。一根香蕉不需要知道我冰箱里有多少根香蕉
    • @Jigar 好的,那么每当您放入或取出一个对象时,您都必须更新所有相等对象中的频率,这会大大增加复杂性
    • @Sean no 如果我们使用地图(第二种方法),我们将不必这样做。我们也可以将List 包装到某个具有add/removeFooTo/FromList() 的类中,该类封装了逻辑
    • @Jigar 是的,如果你删除第一种方法,我会支持你的答案:-)
    • @Jigar 好的,你抓住了我 :-) +1
    【解决方案2】:

    您可以使用Guava Multisetorder it by frequency


    关于性能。当然,这取决于你有多少不同的值,但是这个测试代码在我的机器上花了大约一秒钟。我会说这对于 1000 万件商品来说已经足够合理了:

    Multiset<Integer> set = HashMultiset.create();
    int amount = 10000000;
    Random random = new Random();
    for (int i = 0; i < amount; i++) {
        set.add(Integer.valueOf(random.nextInt(255)));
    }
    TreeSet<Entry<Integer>> sortedEntries = Sets.newTreeSet(
            new Comparator<Entry<Integer>>() {
        public int compare(Entry<Integer> a, Entry<Integer> b) {
            return Ints.compare(a.getCount(), b.getCount());
        }
    });
    Iterables.addAll(sortedEntries, set.entrySet());
    for (Entry<Integer> entry : Iterables.limit(sortedEntries, 5)) {
        System.out.println(entry.getElement());
    }
    

    【讨论】:

      【解决方案3】:

      任何基于比较的排序都会产生O(N log N) 或更糟的时间复杂度,因此(渐近地)这些不是好的建议。

      您的方法具有O(N) 时间复杂度,这是您能得到的最好的。您可以尝试降低常量(目前您大致对列表元素进行6*N 访问)。

      我会像这样在两次迭代中做到这一点:首先使用 HashMap 计算频率。接下来,遍历映射中的条目,并保持一个有序的 5 元素数组,其中包含迄今为止看到的 5 个最常见的值。对于每个新元素,检查该值是否比迄今为止最常见的第 5 个值更常见,并在必要时更新“前 5 个”。


      更新一个更简单的解决方案,具有相同的时间复杂度。首先,使用HashMap 计算频率。接下来,将所有条目放入PriorityQueue 并弹出五个值。条目应该是值-频率对,按频率进行比较(如@Jigar 的解决方案)。这样的排序不会“与 equals 一致”(请参阅​​ Comparable 以获得解释),但这没关系。

      【讨论】:

      • 对我来说听起来非常复杂
      • @Sean 如果您正在处理小列表,那么编写复杂的解决方案是不值得的。但是,当您使用大型列表(例如,10M 元素)时,您会注意到性能上的差异。
      • 是的,但我建议的 Guava Multisets 是高度优化的数据结构,而且速度足够快(请参阅我添加的代码)
      • @Sean 好的,他们可能已经足够快了(我赞成你的答案),但作为一名兼职理论计算机科学家,我不得不挑剔;)
      • 谢谢,但你必须意识到,Guava 家伙也是铁杆 CS 极客 :-)
      【解决方案4】:

      我也会使用 HashMap。我在其中找到了一些代码:

      HashMap<String, Integer> counts = new HashMap<String, Integer>();
      
      void increment(String s) {
          Integer oldCount = counts.get(s);
          if (oldCount == null) {
              counts.put(s, 1);
          } else {
              counts.put(s, oldCount + 1);
          }
      }
      

      列出元素:

      Map.Entry<String, Integer>[] array = new Map.Entry[counts.size()];
      counts.entrySet().toArray(array);
      Arrays.sort(array, new Comparator<Map.Entry<String, Integer>>() {
          public int compare(Map.Entry<String, Integer> a, Map.Entry<String, Integer> b) {
              return b.getValue() - a.getValue();
          }
      });
      int x = 0, min = 0;
      for (Map.Entry<String, Integer> el : array) {
          String k = el.getKey();
          println("Count: " + el.getValue() + "\n" + k + "\n\n");
      }
      

      【讨论】:

        猜你喜欢
        • 2013-11-21
        • 1970-01-01
        • 2014-02-17
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多