简体   繁体   English

如何打印数组中出现次数最多的前 5 个数字?

[英]How do I print the top 5 most occurring numbers in an array?

I'm supposed to "Write a method that prints out the top 5 most commonly occurring numbers in an array along with the count of occurrences for each" I have a method that prints out the occurrence of each entry in an array and I created another method to print the top 5 occurrences in the array.我应该“编写一个方法,打印出数组中最常出现的 5 个数字以及每个数字的出现次数”我有一个方法可以打印出数组中每个条目的出现,我创建了另一个方法打印数组中的前 5 个匹配项。 I'm able to get the values to display correctly in descending order but I'm having trouble getting the keys to do the same.我能够让值按降序正确显示,但我无法让键做同样的事情。 For Example, my output is "2 occurs: 5 times 3 occurs: 4 times 4 occurs: 3 times 5 occurs: 3 times 6 occurs: 2 times" when it should be "3 occurs: 5 times 9 occurs: 4 times 6 occurs: 3 times 7 occurs: 3 times 5 occurs: 2 times" How can I fix my method to get the expected output?比如我的output是“2次出现:5次3次出现:4次4次出现:3次5次出现:3次6次出现:2次”当它应该是“3次出现:5次9次出现:4次6次出现” : 3 次 7 发生:3 次 5 发生:2 次”如何修复我的方法以获得预期的 output?

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.Random;

public class HW4 {

    public static void main(String[] args) {
        int[] a = getRandom(20, 1, 10);
        System.out.println(Arrays.toString(a));
        System.out.println();
        occurrence(a);
        System.out.println();
        top5(a);

    }

    public static int[] getRandom(int n, int low, int high) {

        long seed = 0;
        Random random = new Random(seed);
        random.setSeed(seed);

        int[] result = new int[n];

        for (int i = 0; i < n; i++)

            result[i] = random.nextInt(high - low) + low;

        return result;

    }

    public static void occurrence(int[] x) {
        HashMap<Integer, Integer> occurrences = new HashMap<>();
        for (int key : x) {
            if (occurrences.containsKey(key)) {
                occurrences.put(key, occurrences.get(key) + 1);
            } else {
                occurrences.put(key, 1);
            }
        }
        for (int key : occurrences.keySet()) {
            System.out.println(key + " occurs: " + occurrences.get(key) + " times");
        }
    }

    public static void top5(int[] arr) {
        HashMap<Integer, Integer> lookup = new HashMap<Integer, Integer>();
        for (int key : arr) {
            if (lookup.containsKey(key)) {
                lookup.put(key, lookup.get(key) + 1);
            } else {
                lookup.put(key, 1);
            }
        }
        ArrayList<Integer> keys = new ArrayList<>(lookup.keySet());
        ArrayList<Integer> values = new ArrayList<>(lookup.values());
        for (int i = 0; i < 5; i++) {
            Collections.sort(values, Collections.reverseOrder());
            System.out.println(keys.get(i) + " occurs: " + values.get(i) + " times");
        }
    }
}

List of Entries + Sorting条目列表+排序

The simplest approach would to calculate the frequencies of each element using a HashMap , as you've done.正如您所做的那样,最简单的方法是使用HashMap计算每个元素的频率。

Then dump all the map-entries into a list and sort the list by value .然后将所有地图条目转储到一个列表中,并按列表进行排序。 We need a Comparator to perform this sorting.我们需要一个Comparator器来执行这种排序。 And for that, we can use Java 8 method Map.Entry.comparingByValue() to obtain a comparator .为此,我们可以使用 Java 8 方法Map.Entry.comparingByValue()来获得一个比较器

And as a final step, print the first 5 elements.最后一步,打印前5元素。

public static void top5(int[] arr) {
    Map<Integer, Integer> lookup = new HashMap<>();
    for (int key : arr) {
        int value = lookup.get(key); // or lookup.merge(key, 1, Integer::sum); instead of these two lines
        lookup.put(key, value + 1);
    }
    
    List<Map.Entry<Integer, Integer>> sortedEntries = new ArrayList<>(lookup.entrySet());
    
    sortedEntries.sort(Map.Entry.<Integer, Integer>comparingByValue().reversed());
    
    for (int i = 0; i < 5; i++) {
        System.out.println(
            sortedEntries.get(i).getKey() + " occurs: " + 
            sortedEntries.get(i).getValue() + " times"
        );
    }
}

PriorityQueue优先队列

Instead of sorting the whole data, a more performant approach would be to use a PriorityQueue .而不是对整个数据进行排序,一种更高效的方法是使用PriorityQueue

The first step remains the same - generate a map of frequencies .第一步保持不变——生成频率的 map

The next step is to create a PriorityQueue which would store map-entries .下一步是创建一个PriorityQueue来存储map-entries For that, we would also need a comparator .为此,我们还需要一个比较器

Then iterate over the entry-set , trying to offer each entry to a queue .然后遍历entry-set ,尝试将每个条目提供给queue If the size of the queue is equal to 5 and the value of the next entry is greater then the value of the lowest value in the queue, the root element of the queue needs to be removed.如果队列的大小等于5并且下一个条目的值大于队列中最小值的值,则需要删除队列的根元素。

public static void top5(int[] arr) {
    Map<Integer, Integer> lookup = new HashMap<>();
    for (int key : arr) {
        int value = lookup.get(key); // or lookup.merge(key, 1, Integer::sum); instead of these two lines
        lookup.put(key, value + 1);
    }
    
    Queue<Map.Entry<Integer, Integer>> queue = new PriorityQueue<>(
        Map.Entry.comparingByValue()
    );
    
    for (Map.Entry<Integer, Integer> entry: lookup.entrySet()) {
        if (queue.size() == 5 && queue.peek().getValue() < entry.getValue()) queue.poll();
        if (queue.size() < 5) queue.offer(entry)
    }
    
    while (!queue.isEmpty()) {
        Map.Entry<Integer, Integer> next = queue.poll();
        
        System.out.println(
            next.getKey() + " occurs: " +
            next.getValue() + " times"
        );
    }
}

If you're looking for a method using Hashmap, you could do that如果您正在寻找使用 Hashmap 的方法,您可以这样做

public static void top5(int[] arr) {
        LinkedHashMap<Integer, Integer> lookup = new LinkedHashMap<>();
        for (int key : arr) {
            if (lookup.containsKey(key)) {
                lookup.put(key, lookup.get(key) + 1);
            } else {
                lookup.put(key, 1);
            }
        }
        Map<Integer, Integer> result = lookup.entrySet() /* sorts the linked_map by value */
                .stream()
                .sorted(Map.Entry.comparingByValue())
                .collect(Collectors.toMap(
                        Map.Entry::getKey,
                        Map.Entry::getValue,
                        (oldValue, newValue) -> oldValue, LinkedHashMap::new));

        List<Integer> key_in_order
                = new ArrayList<Integer>(result.keySet());  // convert to list

        /* reverse the key because it was backward compared to what you want */
        Collections.reverse(key_in_order);

        for (int i = 0; i < 5; i++) {
            System.out.println("key : "+String.valueOf(key_in_order.get(i)) + " value : " + result.get(key_in_order.get(i)) );
        }
    }

As for often with java, the result requires very big function lol There's probably a more efficient way but that's what I've come up with personally至于经常使用 java,结果需要非常大的 function 大声笑这可能是一种更有效的方法,但这就是我个人想出的

Once you have created occurrences map,创建事件 map 后,

  1. you can create one max heap of the keys.您可以创建一个最大的密钥堆。 You can pass comparator in max heap to compare based on values from occurrences map.您可以在最大堆中传递比较器,以根据出现次数 map 中的值进行比较。
PriorityQueue<Integer> maxHeap = new PriorityQueue<>((a,b)->occurrences.get(b)-occurrences.get(a))
  1. after this you can just poll from maxHeap for 5 times在此之后,您可以从 maxHeap 轮询 5 次
for(int i=0;i<5;i++){
  if(!maxHap.isEmpty()){
    System.out.println(maxHeap.poll());
  }
}

You can update your top5 function as below:您可以按如下方式更新您的 top5 function:

public static void top5(Map<Integer, Integer> occurrences) {
        PriorityQueue<Integer> maxHeap = new PriorityQueue<>((a, b) -> occurrences.get(b) - occurrences.get(a));
        maxHeap.addAll(occurrences.keySet());
        System.out.println("top 5");
        for (int i = 0; i < 5; i++) {
            if (!maxHeap.isEmpty()) {
                System.out.println(maxHeap.peek() + "  occurs: " + occurrences.get(maxHeap.poll()) + " times");
            }
        }
    }
public static void top5(int[] arr) {
    IntStream.of(arr).boxed()
            .collect(Collectors.groupingBy(Function.identity(), Collectors.counting()))
            .entrySet().stream()
            .sorted((e1, e2) -> Long.compare(e2.getValue() , e1.getValue()))
            .limit(5)
            .forEach(e -> System.out.printf("%d occurs %d times%n", e.getKey(), e.getValue()));
}

The first two lines create the histogram, that is a Map which maps each value to its number of occurrences.前两行创建直方图,即 Map,它将每个值映射到其出现次数。 The next line gets the stream of entries which will be sorted in the following line by their value (that is the number of occurences) in reverse order.下一行获取 stream 条目,这些条目将在下一行按其值(即出现次数)以相反的顺序排序。 You want only 5 elements, so the result is limited to 5 and in the last line the results are output nicely formatted.您只需要 5 个元素,因此结果限制为 5 个,最后一行的结果是 output 格式很好。

In this answer I will assume that you have at least five numbers:在这个答案中,我假设您至少有五个数字:

Integer top5[] = new Integer[5];
int length = 0;
int minimumIndex = 0;
for (Integer key : occurrences.keySet) {
    if (length < 5) { //we take the first five items whatever they are
        top5[length++] = key; //we assign key to top5[length] and increment length
        if (length === 5) { //The elements are filled, getting the index of the minimum
            minimumIndex = 0;
            for (int index = 1; index < 5; index++) {
                if (occurrences.get(top5[minimumIndex]).intValue() > occurrences.get(top5[index]).intValue()) minimumIndex = index;
            }
        }
    }
    } else { //buildup completed, need to compare all elements
        if (occurrences.get(key).intValue() > occurrences.get(top5[minimumIndex]).intValue()) {
            //We found a larger item than the fifth largest so far
            top5[minimumIndex] = key; //we replace it
            //we find the smallest number now
            minimumIndex = 0;
            for (int index = 1; index < 5; index++) {
                if (occurrences.get(top5[minimumIndex]).intValue() > occurrences.get(top5[index]).intValue()) minimumIndex = index;
            }
        }
    }
}
top5 = Arrays.sort(top5, Collections.reverseOrder());

If this answer seems to be too complicated, then you can separate the computation of minimumIndex into a separate method and use it twice.如果这个答案似乎太复杂,那么您可以将minimumIndex的计算分离到一个单独的方法中,并使用它两次。 Otherwise, it should be easy to apply, especially since there are comments added as well.否则,它应该很容易应用,特别是因为还添加了评论。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM