我刚刚编写了一个算法,该算法在输入整数数组中查找具有最大/最小出现次数的值。我的想法是对数组进行排序(所有事件现在按顺序排列)并使用<value:occurrences>
对为每个值存储相应的出现次数。
它应该是O(nlogn)
复杂度,但我认为有一些常数乘数。我该怎么做才能提高绩效?
#include <stdio.h>
#include <stdlib.h>
#include "e7_8.h"
#define N 20
/*Structure for <value, frequencies_count> pair*/
typedef struct {
int value;
int freq;
} VAL_FREQ;
void get_freq(int *v, int n, int *most_freq, int *less_freq) {
int v_i, vf_i, current_value, current_freq;
VAL_FREQ* sp = malloc(n*sizeof(VAL_FREQ));
if(sp == NULL) exit(EXIT_FAILURE);
mergesort(v,n);
vf_i = 0;
current_value = v[0];
current_freq = 1;
for(v_i=1; v_i<n+1; v_i++) {
if(v[v_i] == current_value) current_freq++;
else{
sp[vf_i].value = current_value;
sp[vf_i++].freq = current_freq;
current_value = v[v_i];
current_freq = 1;
}
}
/*Finding max,min frequency*/
int i, max_freq_val, max_freq, min_freq_val, min_freq;
max_freq = sp[0].freq;
max_freq_val = sp[0].value;
min_freq = sp[0].freq;
min_freq_val = sp[0].value;
for(i=1; i<vf_i; i++) {
if(sp[i].freq > max_freq) {
max_freq = sp[i].freq;
max_freq_val = sp[i].value;
}
if(sp[i].freq < min_freq) {
min_freq = sp[i].freq;
min_freq_val = sp[i].value;
}
}
*most_freq = max_freq_val;
*less_freq = min_freq_val;
free(sp);
}
答案 0 :(得分:6)
使用哈希表来实现键值映射?这应该给你O(n)预期时间。 *
<小时/> *但是,请注意,在最坏的情况下它是O(n 2 )。只有当所有条目都散列到同一个存储桶时才会出现这种情况,并且您有效地最终会在每次迭代时搜索链接列表!对于合适的散列表实现,发生这种情况的可能性非常低。
答案 1 :(得分:5)
让我们从你的算法已经是O(n * log(n))的事实开始,因为每一步都是O(n),排序是O(n * log(n))。如果它可以显着改善取决于您期望的输入类型。 编辑:除非,并且看起来是这种情况,否则不是要求在流程结束时对值进行排序(在任何情况下按值,而不是按出现次数排序)的要求的一部分,在这种情况下,不要错过Oli Charlesworth的答案。
实地有两个概念:第一个是你要获得多少样本(n);第二个是它们的值“有多集中”,这些值可以分布的范围有多窄或宽(w = MAX_VALUE - MIN_VALUE)。
如果n小于w(因此您的值很稀疏),那么您的方法已经是最优的并且几乎没有改进的空间。
但如果w很小且n很大,那么你可以通过以下方法获得很多好处。
假设您知道您不能获得低于MIN_VALUE的任何值,并且没有超过MAX_VALUE的值。然后,您可以将值用作收集频率的数组的索引。这样,您跳过排序步骤(O(n * log(n))),然后用O(n)计算频率。
int buffer_frequencies[MAX_VALUE - MIN_VALUE + 1];
//Now reset the array with some convenient function like memset
int* value_frequencies = buffer_frequencies;
value_frequencies -= MIN_VALUE; //Shift the beginning of the array, so that
//you can use the value directly as the array index
//You are allowed to use negative indexes
for(v_i=0; v_i < n; v_i++) {
value_frequencies[v[v_i]]++;
}
甚至(可能是稍微更快的for循环版本,但通常一个好的编译器已经在最有效的版本中转换它):
int* p_v = v;
int* end_p_v = v+n;
for(; p_v < end_p_v; p_v++) {
value_frequencies[*p_v]++;
}
请注意,此方法(两个版本)对输入值非常敏感,即如果得到的值超出MIN_VALUE或MAX_VALUE,则会破坏内存边界
然后是算法的第二部分:
//First cycle could be optimized, but it has no impact
int i = MIN_VALUE;
max_freq = value_frequencies[i];
max_freq_val = i;
min_freq = value_frequencies[i];
min_freq_val = i;
for(; i<MAX_VALUE; i++) {
max_freq_val = (value_frequencies[i] > max_freq) ? i : max_freq_val;
max_freq = (value_frequencies[i] > max_freq) ? value_frequencies[i] : max_freq;
min_freq_val = (value_frequencies[i] < min_freq) ? i : min_freq_val;
min_freq = (value_frequencies[i] < min_freq) ? value_frequencies[i] : min_freq;
}
}