Java 在整数数组中查找k个最常出现的元素

Java 在整数数组中查找k个最常出现的元素,java,arrays,algorithm,Java,Arrays,Algorithm,给定一个包含可能重复项的数组A,查找最频繁出现的k个项 我的做法: 创建一堆按频率排序的最常出现元素。顶部元素显然是其余元素中出现最少的元素。 创建一个HashMap来跟踪所有元素计数以及它们是否在MinHeap中 读取新整数时: 检查它是否在HashMap中:增加HashMap中的计数 另外,如果是,则检查它是否在堆中:然后也增加那里的计数并heapify 如果没有,则与根元素计数进行比较,必要时删除根元素以添加此元素。然后希皮菲 最后,返回MinHeap作为所需的输出 class Wra

给定一个包含可能重复项的数组A,查找最频繁出现的k个项

我的做法:

创建一堆按频率排序的最常出现元素。顶部元素显然是其余元素中出现最少的元素。 创建一个HashMap来跟踪所有元素计数以及它们是否在MinHeap中

读取新整数时:

  • 检查它是否在HashMap中:增加HashMap中的计数
  • 另外,如果是,则检查它是否在堆中:然后也增加那里的计数并heapify
  • 如果没有,则与根元素计数进行比较,必要时删除根元素以添加此元素。然后希皮菲
最后,返回MinHeap作为所需的输出

class Wrapper{
 boolean inHeap;
 int count;
}

这需要O(n+k)空间和O(n logk)时间复杂度。有没有更好的方法来处理空间和/或时间复杂性问题。

我们可以说,您的方法的空间复杂性是
O(n)
,因为您使用的内存不能超过
O(2n)=O(n)


跳过堆,只创建HashMap

创建HashMap后,可以对其进行迭代,并将所有元素放入一个数组中

然后,您可以在数组上运行一个,比如获取第k个元素,以及第一个元素(通过quickselect提取第一个
k
元素的扩展相当简单,或者您可以再次迭代获取它们)

然后根据需要对
k
元素进行排序

如果需要排序,预计运行时间为
O(n)
O(n+k log k)


空间复杂度将是
O(n)

考虑使用一个映射,将数字映射到它的发生计数。维护一个单独的int,其中包含任何数字中当前最大的计数,以及一个包含每个带有/count/numbers的数字的列表。
这种方法将允许您在对所有值进行一次迭代后了解结果。在最坏的情况下(如果所有值都有一次出现),您将使用2倍的内存(映射和列表中都有1个条目)。即使这样,只要一个条目出现两次,就可以开始向列表中添加条目。

我同意堆会使它复杂化。您可以简单地对数组进行合并排序(
O(k log k)
time),然后在创建HashMap(
O(n)
time))后运行数组。总运行时间
O(n+k*log(k))=O(k*log(k))

有许多不同的算法用于确定所谓的频繁项,基于计数器和基于草图。在基于计数器的算法中,当前最好的是(其他算法是和)


在最坏的情况下,节省空间需要O(n)时间和k+1计数器在由$n$条目的输入中查找k个频繁项

通过@Dukeling补充答案。我在C++中添加了代码来解释QuestScLoad方法。 步骤:

  • 使用
    映射
    获取每个唯一元素的频率
  • 执行以获取最大的第k个元素
  • 选择向量中从0到k的元素
  • 代码:

    #include <cmath>
    #include <cstdio>
    #include <vector>
    #include <iostream>
    #include <algorithm>
    #include <map>
    
    using namespace std;
    
    map<int,int> m;
    
    void swap(int *a,int *b){
        int temp=*a;
        *a=*b;
        *b=temp;
    }
    
    void printelements(vector<int> &temp,int k){
        for(int i=0;i<=k;++i){
            cout<<temp[i]<<endl;
        }
    } 
    
    int partition(vector<int> &a, int low,int high){
        int pivot = high-1;
        int i=low-1;
        for(int j=low;j<high-1;j++){
            if(m[a[j]]>=m[a[pivot]]){
                i++;
                swap(&a[i],&a[j]);
            }
        }
        i++;
        swap(&a[i],&a[pivot]);
        return i;
    }
    
    void quickselect(vector<int> &temp,int low,int high,int k){
        if(low>high){
            return ;
        }
        int pivot=partition(temp,low,high);
        if(k==pivot){
            printelements(temp,k);
            return;
        }
        else if(k<pivot){
            quickselect(temp,low,pivot,k);
        }
        else{
            quickselect(temp,pivot+1,high,k);
        }
    }
    
    void topKelements(int a[],int n,int k){
        if(k<0)return ;
        for(int i=0;i<n;i++){
            if(m.find(a[i])!=m.end()){
                m[a[i]]++;
            }
            else{
                m.insert(pair<int,int>(a[i],1));
            }
        }
        vector<int> temp;
        map<int,int>::iterator it;
        for(it=m.begin();it!=m.end();++it){
            temp.push_back(it->first);
        }
        k=min(k,(int)temp.size()-1);
        quickselect(temp,0,temp.size(),k);
    }
    
    int main() {
        int a[] = {1,2,3,4,1,1,2,3,4,4,4,1};
        int k = 2;
        topKelements(a,12,k-1);
    }
    
    #包括
    #包括
    #包括
    #包括
    #包括
    #包括
    使用名称空间std;
    地图m;
    无效交换(int*a,int*b){
    int temp=*a;
    *a=*b;
    *b=温度;
    }
    无效打印元素(矢量和温度,整数k){
    
    对于(int i=0;i那么,在您的步骤2中,如何在log(k)time中找到堆中的元素呢?请注意,堆没有排序,并且在父节点上,无法决定要转到哪个子节点。您必须迭代所有堆成员,所以总时间是O(nk)time

    如果将heap更改为二进制搜索树(如TreeMap),您可以在log(k)time中找到一个频率。但是您必须处理重复的键,因为不同的元素可能具有相同的计数。

    公共类数组问题{
    
    public class ArrayProblems {
        static class Pair {
            int value;
            int count;
    
            Pair(int value, int count) {
               this.value = value;
               this.count = count;
           }
        }
    /*
     * Find k numbers with most occurrences in the given array
     */
    public static void mostOccurrences(int[] array, int k) {
        Map<Integer, Pair> occurrences = new HashMap<>();
        for(int element : array) {
            int count = 1;
            Pair pair = new Pair(element, count);
            if(occurrences.containsKey(element)) {
                pair = occurrences.get(element);
                pair.count++;
            }
            else {
                occurrences.put(element, pair);
            }
        }
    
        List<Pair> pairs = new ArrayList<>(occurrences.values());
        pairs.sort(new Comparator<Pair>() {
            @Override
            public int compare(Pair pair1, Pair pair2) {
                int result = Integer.compare(pair2.count, pair1.count);
                if(result == 0) {
                    return Integer.compare(pair2.value, pair1.value);
                }
                return result;
            }
        });
    
        int[] result = new int[k];
        for(int i = 0; i < k; i++) {
            Pair pair = pairs.get(i);
            result[i] = pair.value;
        }
    
        System.out.println(k + " integers with most occurence: " + Arrays.toString(result));
    
    }
    
    public static void main(String [] arg)
    {
        int[] array  = {3, 1, 4, 4, 5, 2, 6, 1};
        int k = 6;
        ArrayProblems.mostOccurrences(array, k);
    
        // 3 --> 1
        // 1 --> 2
        // 4 --> 2
        // 5 --> 1
        // 2 --> 1
        // 6 --> 1
    }
    
    静态类对{ int值; 整数计数; 对(int值、int计数){ 这个值=值; this.count=计数; } } /* *查找给定数组中出现次数最多的k个数字 */ 公共静态void mostOccurrences(int[]数组,int k){ 映射出现次数=新建HashMap(); for(int元素:数组){ 整数计数=1; 对=新对(元素、计数); if(事件.容器(元素)){ pair=引用。get(元素); count++; } 否则{ 事件。放置(元素,对); } } 列表对=新的ArrayList(引用.values()); pairs.sort(新的比较器(){ @凌驾 公共整数比较(对pair1,对pair2){ int result=Integer.compare(pair2.count,pair1.count); 如果(结果==0){ 返回整数.compare(pair2.value,pair1.value); } 返回结果; } }); int[]结果=新的int[k]; for(int i=0;i 1 // 1 --> 2 // 4 --> 2 // 5 --> 1 // 2 --> 1 // 6 --> 1 }

    }

    您可以使用hashmap。如果映射已经存在,则在映射中增加值。然后使用lambda排序结果映射,限制为k值

        import java.util.Arrays;
        import java.util.HashMap;
        import java.util.LinkedHashMap;
        import java.util.Map;
    
        public class MaxRepeating
        {
            static void maxRepeating(int arr[], int n, int k)
            {
                Map<Integer,Integer> map=new HashMap<Integer,Integer>();
                // increment value in map if already present
                for (int i = 0; i< n; i++){
                    map.put(arr[i], map.getOrDefault(arr[i], 0)+1);
    
                }
                map.entrySet().stream()
                        .sorted(Map.Entry.<Integer, Integer>comparingByValue().reversed())
                           .limit(k).forEach(System.out::println);
    
            }
    
            /*Driver function to check for above function*/
            public static void main (String[] args)
            {
    
                int arr[] = {7, 10, 11, 5, 2, 5, 5, 7, 11, 8, 9};
                int n = arr.length;
                int k=4;
                maxRepeating(arr,n,k);
            }
        }
    
    导入java.util.array;
    导入java.util.HashMap;
    导入java.util.LinkedHashMap;
    导入java.util.Map;
    公共类最大重复
    {
    静态void maxRepeating(int arr[],int n,int k)
    {
    Map Map=newhashmap();
    //映射中的增量值(如果已存在)
    对于(int i=0;i