【问题标题】:Counting occurrences of an item计算项目的出现次数
【发布时间】:2011-07-02 06:56:39
【问题描述】:

我正在尝试找出解决以下问题的最佳解决方案(在 Java 中):

在第一次传递一些数据时,我计算了一个项目的出现次数。基本上,我创建一个从项目 ID 到整数的 HashMap,并在每次看到项目出现时递增整数。所以基本上,我有一个从 itemID 到计数的Map<Long,Integer>

现在,我需要从这张地图中得到按计数排序的前 n 个项目 id。

显然 HashMap 并不是这里的最优数据结构。有什么想法吗?

这是我在工作中做的一些数据挖掘工作,所以不是硬件问题...

【问题讨论】:

    标签: java algorithm data-mining


    【解决方案1】:

    一个明显的答案是为此使用 SortedMap。确保新创建的地图的可比较属性使顶部项目成为第一位,并且您可以从中获取第一个元素。

    【讨论】:

    • 他需要按值排序,而不是按键。
    【解决方案2】:

    实际上,HashMap 在这里是一个合理的解决方案,因为您必须累积总数。在您知道所有项目的计数之前,您无法快捷地找到前 N 个项目。

    有了 HashMap 之后,有几种方法可以做事。如果数据比较少,创建itemId和count对的数组,按count降序排序。然后选择前 N 项。

    如果您有很多项目(数十万个),在获得计数后使用最小堆可能会更快,其想法是您将前 N 个项目放入最小堆,然后只插入一个项,如果它的计数大于最小堆中的最小项。

    在进行添加时,您可以通过计数来保持事物的顺序,但是每次增加计数器时,您都必须从集合中删除该事物并重新插入它。您最好在 HashMap 中累积内容,以便通过 ID 查找内容,然后进行后处理以按计数应用排序。

    【讨论】:

    • 我喜欢最小堆的想法。这就是我一直在寻找的效率。
    • @ChrisS:请注意,尽管最小堆和排序都具有理论上的 O(n log n) 复杂度,但实际上,排序后选择方法对于小值会更快一些N 因为快速排序比最小堆具有更好的实际运行时性能。只有当 N 变大并且您不必实际将东西插入堆中时,这种技术才会变得更有效。
    【解决方案3】:

    我会在计数后对结果进行排序。

    Map<Item,Integer> map = new HashMap<Item, Integer>();
    
    ... (fill the map, counting the occurences)
    
    List<Map.Entry<Item, Integer>> list = new ArrayList<Map.Entry<Item, Integer>>(map.size());
    list.addAll(map.entrySet());
    Collections.sort(list, new Comparator<Map.Entry<Item, Integer>>() {
        public int compare(Map.Entry<Item, Integer>> left, Map.Entry<Item, Integer>> right) {
           // "-" to invert the order
           return - left.getValue().compareTo(right.getValue());
        }
    });
    

    现在list 是一个列表,其中的项目按计数排序(降序),.subList(0, n) 将为您提供前 n 个。

    如果您的 n 远小于项目总数,那么这不是最优的 - 我认为有一个更好(但更复杂)的算法可以只取无序列表中最好的部分。

    【讨论】:

    • 这看起来很合理。我正在排序到另一个地图,所以我必须遍历 keySet 才能获得前 1000 个。这更好,因为您可以调整列表的大小。
    【解决方案4】:

    我认为,如果您希望能够获取 id、计数并仍然维护 Map 结构,您将需要创建一个 Class 来封装您的数据。

    public class DataPair implements Comparable<DataPair> {
        private long id;
        private Integer count;
    
        //Getters and setters
    
        public void increaseCount() {
            count++;
        }
    
        public int compareTo(DataPair dp) {
             return this.count.compareTo(dp.count);
        }
    
    }
    

    然后有一个地图,就像你一直在使用 where:

    Map<long, DataPair> m = new HashMap<long, DataPair>()
    

    然后,当您需要按计数排序时,您只需取出值并对其进行排序,同时保持按 id 获取当前计数的能力。

    List<DataPair> list = new ArrayListM<DataPair>(m.values());
    Collections.sort(list);
    

    然后您将获得排序后的计数,并且仍然能够获取 id。

    【讨论】:

      【解决方案5】:

      您可以有一个排序的地图[按值排序],如下所示:

      创建一个类Profile 来保存您的数据和计数[用于临时目的]。

      您的个人资料类将如下所示:

      class Profile
      {
          public String data;
          public Integer value;
      
          public int getValue()
          {
              return value;
          }
      }
      

      按值排序的方法如下:

      public Map<String, Integer> sortMapByValues(final Map<String, Integer> passedMap)
          {
              List<Profile> tuples = new LinkedList<Profile>();
      
              Iterator<String> it = passedMap.keySet().iterator();
      
              while (it.hasNext())
              {
                  String key = it.next();
                  Integer val = passedMap.get(key);
      
                  tuples.add(new Profile(key, val));
              }
      
              Collections.sort(tuples, new ProfileComparator());
      
              Map<String, Integer> sortedMap = new LinkedHashMap<String, Integer>();
      
              for (int i = 0; i < tuples.size(); i++)
              {
                  sortedMap.put(tuples.get(i).getKey(), tuples.get(i).getValue());
              }
      
              return sortedMap;
          }
      

      现在您只需要一个 Comparator 实现。

      您的 ProfileComparator 类将如下所示:

      public final class ProfileComparator implements Comparator<Profile>
      {
          public int compare(final Profile n1, final Profile n2)
          {
              if (n1.getValue() > n2.getValue())
              {
                  return -1;
              }
      
              if (n2.getValue() > n1.getValue())
              {
                  return 1;
              }
      
              return 0;
          }
      }
      

      【讨论】:

        【解决方案6】:

        也许 TreeMap 是更多可选的解决方案。

        http://docs.oracle.com/javase/6/docs/api/java/util/TreeMap.html

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2011-02-08
          • 1970-01-01
          • 2014-03-18
          • 2014-10-16
          • 1970-01-01
          • 1970-01-01
          • 2011-12-27
          • 2021-06-11
          相关资源
          最近更新 更多