查找具有最高频率的阵列中的所有数字?

时间:2017-06-04 09:33:35

标签: java arrays

我试图找出所有频率最高的数字。即如果最大频率为5,那么我需要在阵列中发生5次的所有数字。

让我们考虑以下数组示例:

1 8 7 8 9 2 1 9 6 4 3 5

在这里,最常见的数字是8,1,9,最高频率为2.我的预期输出是这样的:

    int n=100;
    int N=1000;

    int data[] = new int[N];
    Set<Integer> set = new HashSet<Integer>();

    Random random = new Random();

    for(int i=0;i<N;i++){
        int  number = random.nextInt(n);
        data[i] = number;
        set.add(number);
    }

    int frequency[] = new int[set.size()];
    Integer[] distinct = set.toArray(new Integer[set.size()]);

    for (int j=0;j<set.size();j++){
        int count=0;
        for(int k=0;k<N;k++){
            if(distinct[j]==data[k]){
                count = count+1;
            }
        }
        frequency[j] = count;
    }

在我的项目中,我试图找出最频繁的数字和最不频繁的数字。在这里,我想要最频繁的数字。

我生成了1000个类似于我的项目场景的随机数,并计算了不同的数字,然后计算了它们的出现次数。

    int max = Integer.MIN_VALUE;
    List<Integer> vals = new ArrayList<>();

    for (int q=0; q < frequency.length; ++q) {

        if (frequency[q] == max) {
            vals.add(q);
        }

        else if (frequency[q] > max) {
            vals.clear();
            vals.add(q);
            max = frequency[q];
        }
    }

    for(int num : vals){
        System.out.println(distinct[num]+" => "+frequency[num]);
    }

在计算每个数字的频率后,我使用here的答案计算了大多数频率的数字,该答案是优化的。

func main() {
...
db, err := sql.Open("sqlite3", "./libreread.db")
CheckError(err)
defer db.Close()
...
}

这里,循环在第一个代码中使整个过程变慢。这只是大型代码和示例测试用例的一部分。

我想让这个过程更快,因为在实际情况下阵列中可能有大的元素。

有人有办法优化这些循环吗?  要么 获得结果的其他方法是什么?

感谢任何形式的帮助。

5 个答案:

答案 0 :(得分:4)

我会为此使用流。它不会变得非常短,但是一旦你对溪流感到满意,它在概念上就会更简单。

    Map<Integer, Long> frequencies = Arrays.stream(data)
            .boxed()
            .collect(Collectors.groupingBy(i -> i, Collectors.counting()));
    if (frequencies.isEmpty()) {
        System.out.println("No data");
    } else {
        long topFrequency = frequencies.values()
                .stream()
                .max(Long::compareTo)
                .get();
        int[] topNumbers = frequencies.entrySet()
                .stream()
                .filter(e -> e.getValue() == topFrequency)
                .mapToInt(Map.Entry::getKey)
                .toArray();
        for (int number : topNumbers) {
            System.out.println("" + number + " => " + topFrequency);
        }
    }

使用问题中的示例数据,它打印出所需的(仅在另一个不可预测的顺序中):

1 => 2
8 => 2
9 => 2

编辑:tucuxi问:为什么不用流来打印呢?当然,您可以使用更短更简单的代码:

        frequencies.entrySet()
                .stream()
                .filter(e -> e.getValue() == topFrequency)
                .mapToInt(Map.Entry::getKey)
                .forEach(n -> System.out.println("" + n + " => " + topFrequency));

选择什么取决于要求和品味。我期待OP需要存储最高频率数字,所以我演示了如何做到这一点,并打印它们以显示结果。还有一些人认为流应该没有副作用,我会考虑打印到标准输出的副作用。但如果你愿意,可以使用它。

答案 1 :(得分:3)

此代码效率非常低,在最坏的情况下可能会在O(n^2)中运行。

您可以通过构建Map<Integer,Integer>来实现单个for循环的目标,其中密钥是您遇到的每个唯一编号,值是其频率。

获得Map之后,找到具有最大频率的所有数字(只是迭代Map的所有条目)是微不足道的。总运行时间为O(n)

int maxFreq = Integer.MIN_VALUE;
Map<Integer,Integer> freqs = new HashMap<>();
for(int i=0;i<N;i++){
    int number = random.nextInt(n);
    data[i] = number;
    Integer freq = freqs.get(number);
    if (freq != null) {
        freq = freq + 1;
    } else {
        freq = 1;
    }
    freqs.put(number,freq);
    if (freq > maxFreq)
        maxFreq = freq;
}
for(Map.Entry<Integer,Integer> entry : freqs.entrySet()) {
    if (entry.getValue().equals(maxFreq)) {
        System.out.println(entry.getKey() +" => "+ maxFreq);
    }
}

答案 2 :(得分:2)

这应该对你有所帮助。一个完美优化的代码,猜猜是什么?它适用于O(N)。

+-----+----+-----+
|   _1|  _2|count|
+-----+----+-----+
|oded2|4818|    2|
| oded|4918|    2|
| oded|5018|    2|
+-----+----+-----+

答案 3 :(得分:1)

我会使用Java8流。在某些情况下,您甚至可以使用并行流来提高性能。我将如何做到这一点:

d = fetch(c,s) returns data for all fields from the Yahoo! website for the indicated security.

输出:

 public static void main(String[] args) {
    List<Integer> integers = Arrays.asList(1, 8, 7, 8, 9, 2, 1, 9, 6, 4, 3, 5);
    //Here we have statistics of frequency for all numbers
    LinkedHashMap<Integer, Integer> statistics = integers.stream().distinct()
        .collect(Collectors.toMap(Function.identity(), number -> Collections.frequency(integers, number)))
        .entrySet().stream().sorted(Collections.reverseOrder(Comparator.comparing(Map.Entry::getValue)))
        .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (o1, o2) -> o1, LinkedHashMap::new));
    //Calculate max frequency
    Integer maxFrequency = statistics.entrySet().stream()
        .max(Comparator.comparingInt(Map.Entry::getValue))
        .map(Map.Entry::getValue).orElse(null);
    //Collect max frequent numbers to a map
    Map<Integer, Integer> topFrequentNumbers = statistics.entrySet().stream()
        .filter(o -> o.getValue().equals(maxFrequency))
        .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
    //Print
    topFrequentNumbers.forEach((number, frequency) -> System.out.println(number + " => " + frequency));
}

正如我所提到的,您可以使用并行流并提取一些部分来提高性能。

答案 4 :(得分:1)

我认为这是最简单的最优(O(n),对于这个问题)答案。与许多其他答案一样,它计算所有整数的频率。与其他人不同,它不需要第二次传递来找到最频繁的结果,特别是不执行按频率排序,如果您只需要“最频繁”,那就太过分了。此外,短代码更容易调试。

   public static ArrayList<Integer> mostFrequent(int [] numbers) {
        HashMap<Integer, Integer> frequencies = new HashMap<>();
        ArrayList<Integer> mostFrequent = new ArrayList<>();
        int greatestFrequency = 0;
        for (int n : numbers) {

            // build number -> frequency of number map
            int f = frequencies.getOrDefault(n, 0) + 1;
            frequencies.put(n, f);

            if (f > greatestFrequency) {
                // this number is more frequent than all others:
                //  it is now the sole, most frequent, number: no ties
                mostFrequent.clear();
                greatestFrequency = f;
            }
            if (f == greatestFrequency) {
                // this number is as frequent as the most frequent:
                //  add it to the list of numbers tied for this privilege
                mostFrequent.add(n);
            }
        }

        // print out the final list of numbers that are tied for "most frequent"
        for (int n : mostFrequent) {
            System.out.println(n + " => " + greatestFrequency);
        }
    }

请注意,null仅针对空列表返回。抛出异常也可能有效。只需要很少的工作,这段代码就可以接受任何Iterable<Number>,但这会让它更难理解。我怀疑OP想要在生产系统中使用它。