给定具有可能重复条目的阵列A,找到最频繁出现的k个条目。
我的方法:
创建由频率排序的k个最常出现元素的MinHeap。顶部元素显然是其余元素的最少发生。创建一个HashMap来跟踪所有元素计数以及它们是否在MinHeap中。
读取新整数时:
最后返回MinHeap作为所需的输出。
class Wrapper{
boolean inHeap;
int count;
}
这将花费O(n + k)空间和O(n log k)时间复杂度。有没有更好的方法来明智地做空间和/或时间复杂性。
我们可以说你的方法的空间复杂性是O(n)
,因为你永远不会使用超过O(2n) = O(n)
内存。
跳过堆并只创建HashMap。
在创建HashMap之后,您可以遍历它并将所有元素放在一个数组中。
然后你可以在阵列上运行selection algorithm,例如quickselect,以获得k
-th元素,并从那里获得第一个k
元素(通过quickselect提取第一个k
元素的扩展相当简单,或者你可以再次迭代得到他们)。
然后,如果需要,您可以对k
元素进行排序。
如果需要排序,运行时间将是O(n)
或O(n + k log k)
。
空间复杂性将是O(n)
。
要添加@Dukeling的答案。我在下面用C ++添加了代码来解释quickselect方法。
脚步:
map
获取每个独特元素的频率。码:
#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
对于确定所谓的频繁项目,基于计数器和基于草图的任务,存在许多不同的算法。在基于计数器的算法中,当前最好的算法是Space Saving(其他算法是Lossy Count和Frequent)。
节省空间需要在最坏的情况下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);
}
}