簡體   English   中英

在整數數組中查找k個最常出現的元素

[英]Find k most occurring elements in an integer array

給定具有可能重復條目的陣列A,找到最頻繁出現的k個條目。

我的方法:

創建由頻率排序的k個最常出現元素的MinHeap。 頂部元素顯然是其余元素的最少發生。 創建一個HashMap來跟蹤所有元素計數以及它們是否在MinHeap中。

讀取新整數時:

  • 檢查它是否在HashMap中:增加HashMap中的計數
  • 如果檢查它是否在堆中:那么也在那里增加計數並堆積。
  • 如果沒有則比較根元素計數並刪除根以在必要時添加它。 然后堆積。

最后返回MinHeap作為所需的輸出。

class Wrapper{
 boolean inHeap;
 int count;
}

這將花費O(n + k)空間和O(n log k)時間復雜度。 有沒有更好的方法來明智地做空間和/或時間復雜性。

我們可以說你的方法的空間復雜度是O(n) ,因為你永遠不會使用超過O(2n) = O(n)內存。


跳過堆並只創建HashMap。

在創建HashMap之后,您可以遍歷它並將所有元素放在一個數組中。

然后可以運行選擇算法quickselect陣列上獲得k個元素,並且所述第一k從那里元件(擴展,以提取所述第一k元件經由quickselect是相當微不足道的,也可以通過迭代剛剛再次抓住他們)。

然后根據需要對k元素進行排序。

如果需要排序,則預計運行時間為O(n)O(n + k log k)

空間復雜度為O(n)

要添加@Dukeling的答案。 我在下面用C ++添加了代碼來解釋quickselect方法。

腳步:

  1. 使用map獲取每個唯一元素的頻率。
  2. 執行quickselect以獲取最大的第k個元素。
  3. 選擇向量中從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);
}

輸出: 1 4 2

對於確定所謂的頻繁項目,基於計數器和基於草圖的任務,存在許多不同的算法。 在基於計數器的算法中,當前最好的算法是節省空間 (其他算法是有損計數頻繁 )。

節省空間需要在最壞的情況下O(n)時間和k + 1計數器在由$ n $條目組成的輸入中找到k個頻繁項。

考慮使用Map將數字映射到它的出現次數。 維護一個單獨的int,其中包含當前最大的任何數字計數,以及一個List,其中包含每個帶有/ count / numbers的數字。 此方法將允許您在對所有值進行單次迭代后了解結果。 在最壞的情況下(如果所有值都出現1次),則使用2x內存(地圖和列表中都有1個條目)。 即使這可以通過僅在單個條目出現2次時開始向列表添加項目來解決。

我同意堆會使它復雜化。 您可以簡單地MergeSort數組( O(k log k)時間),然后在創建HashMap( O(n)時間)后運行數組。 總運行時間O(n + k*log(k)) = O(k*log(k))

那么,在你的第2步中,你如何在log(k)時間內在堆中找到一個元素? 注意堆沒有排序,並且在父節點上,沒有辦法決定要去哪個子節點。 您必須迭代所有堆成員,因此總時間為O(nk)時間。

如果將堆更改為二叉搜索樹(如TreeMap),則可以在log(k)時間內找到頻率。 但是你必須處理重復的鍵,因為不同的元素可以具有相同的計數。

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
}

}

您可以使用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);
        }
    }

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM