【发布时间】:2011-08-24 18:56:14
【问题描述】:
根据它们在列表中出现的频率来排列列表中的元素(带有重复元素)的好方法是什么。
我需要使用列表中最常出现的 5 个项目。
我正在考虑使用 HashMap 来计算元素的频率,方法是在每次元素出现时增加相应的计数器,然后进行 HashMap 迭代 5 次以找到最高频率。每次迭代的元素。
【问题讨论】:
标签: java collections
根据它们在列表中出现的频率来排列列表中的元素(带有重复元素)的好方法是什么。
我需要使用列表中最常出现的 5 个项目。
我正在考虑使用 HashMap 来计算元素的频率,方法是在每次元素出现时增加相应的计数器,然后进行 HashMap 迭代 5 次以找到最高频率。每次迭代的元素。
【问题讨论】:
标签: java collections
这种方法怎么样?
维护一个包含计数的地图
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的访问,并在那里封装地图更新逻辑。
【讨论】:
List 包装到某个具有add/removeFooTo/FromList() 的类中,该类封装了逻辑
您可以使用Guava Multiset 和order 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());
}
【讨论】:
任何基于比较的排序都会产生O(N log N) 或更糟的时间复杂度,因此(渐近地)这些不是好的建议。
您的方法具有O(N) 时间复杂度,这是您能得到的最好的。您可以尝试降低常量(目前您大致对列表元素进行6*N 访问)。
我会像这样在两次迭代中做到这一点:首先使用 HashMap 计算频率。接下来,遍历映射中的条目,并保持一个有序的 5 元素数组,其中包含迄今为止看到的 5 个最常见的值。对于每个新元素,检查该值是否比迄今为止最常见的第 5 个值更常见,并在必要时更新“前 5 个”。
更新一个更简单的解决方案,具有相同的时间复杂度。首先,使用HashMap 计算频率。接下来,将所有条目放入PriorityQueue 并弹出五个值。条目应该是值-频率对,按频率进行比较(如@Jigar 的解决方案)。这样的排序不会“与 equals 一致”(请参阅 Comparable 以获得解释),但这没关系。
【讨论】:
我也会使用 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");
}
【讨论】: