我使用C中的哈希表库函数实现了下面的问题陈述。由于我从未在C中使用过标准库哈希表,我的问题是:
问题陈述:找到数组中最常见的元素。
我在SO处经历了一些类似的问题 - 在其中一个answers我确实看到推荐的方法是使用哈希表。
#include <stdio.h>
#include <stdlib.h>
#include <search.h>
#include <stdbool.h>
#define REPEAT 3
#define BUFFERSIZE 10
void freqElement(int* arr, int len, int times);
int createHT(int* arr, int len);
int main(void)
{
int arr[] = {2, 3, 5, 6, 10, 10, 2, 5, 2};
int len = sizeof(arr)/sizeof(int);
ENTRY e;
ENTRY *ep;
hcreate(len);
if (!createHT(arr, len))
{
printf(" error in entering data \n");
}
freqElement(arr, len, REPEAT);
hdestroy();
return 0;
}
int createHT(int* arr, int len)
{
ENTRY e, *ep;
for(int i = 0; i < len; i++)
{
char buffer[BUFFERSIZE];
snprintf(buffer, BUFFERSIZE, "%d", arr[i]);
e.key = buffer;
e.data = (void *)1;
ep = hsearch(e, FIND);
if (ep)
{
ep->data = (void *)((int)ep->data + (int)e.data);
}
ep = hsearch(e, ENTER);
if (ep == NULL)
{
fprintf(stderr, "entry failed\n");
exit(EXIT_FAILURE);
}
}
return 1;
}
void freqElement(int* arr, int len, int times)
{
ENTRY *ep, e;
for (int i = 0; i < len; i++)
{
char buffer[BUFFERSIZE];
snprintf(buffer, BUFFERSIZE, "%d", arr[i]);
e.key = buffer;
ep = hsearch(e, FIND);
if(ep)
{
if((int)ep->data == times)
{
printf(" value %s is repeated %d times \n", ep->key, times);
break;
}
}
}
}
答案 0 :(得分:1)
我不确定我是否会使用hcreate()
,hsearch()
,hdestroy()
三重功能来执行此任务,但可以使用它。 POSIX规范在某些问题上并不明确,例如htdestroy()
发布了密钥,但Mac OS X手册说:
hdestroy()
函数处理搜索表,然后可能会再次调用hcreate()
。致电hdestroy()
后,数据将无法再被视为可访问。hdestroy()
函数为搜索表中的每个比较键调用free(3)
,但不调用与该键相关联的数据项。
(POSIX没有提及hdestroy()
在比较键上调用free()
。)
这里是一个相对简单的代码改编,可以在valgrind
下完全运行和运行,至少在Mac OS X 10.11.4上使用GCC 6.1.0和Valgrind 3.12.0-SVN。
$ gcc -O3 -g -std=c11 -Wall -Wextra -Wmissing-prototypes \
> -Wstrict-prototypes -Wold-style-definition -Werror hs17.c -o hs17
$
#include <search.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define BUFFERSIZE 10
void freqElement(int *arr, int len, int times);
int createHT(int *arr, int len);
int main(void)
{
int arr[] = { 2, 3, 5, 6, 10, 10, 2, 5, 2, 8, 8, 7, 8, 7, 8, 7, };
int len = sizeof(arr) / sizeof(int);
if (hcreate(len) == 0)
fprintf(stderr, "Failed to create hash table of size %d\n", len);
else
{
if (!createHT(arr, len))
fprintf(stderr, "error in entering data\n");
else
{
for (int i = 1; i < len; i++)
freqElement(arr, len, i);
}
hdestroy();
}
return 0;
}
int createHT(int *arr, int len)
{
ENTRY e, *ep;
for (int i = 0; i < len; i++)
{
char buffer[BUFFERSIZE];
snprintf(buffer, sizeof(buffer), "%d", arr[i]);
e.key = strdup(buffer);
e.data = (void *)0;
printf("Processing [%s]\n", e.key);
ep = hsearch(e, ENTER);
if (ep)
{
ep->data = (void *)((intptr_t)ep->data + 1);
if (ep->key != e.key)
free(e.key);
}
else
{
fprintf(stderr, "entry failed for [%s]\n", e.key);
free(e.key); // Not dreadfully important
exit(EXIT_FAILURE);
}
}
return 1;
}
// Check whether this number has been processed before
static bool processed_before(int *arr, int len, int value)
{
for (int j = 0; j < len; j++)
{
if (value == arr[j])
return true;
}
return false;
}
void freqElement(int *arr, int len, int times)
{
ENTRY *ep, e;
for (int i = 0; i < len; i++)
{
char buffer[BUFFERSIZE];
snprintf(buffer, BUFFERSIZE, "%d", arr[i]);
e.key = buffer;
ep = hsearch(e, FIND);
if (ep)
{
if ((intptr_t)ep->data == times && !processed_before(arr, i, arr[i]))
printf(" value %s is repeated %d times\n", ep->key, times);
}
}
}
processed_before()
函数可以防止多次打印多个条目的值 - 它是freqElement()
函数更改的结果,该函数报告具有给定出现次数的所有条目,而不仅仅是第一次这样的进入。它不是完全可取的,但代码包括一些打印,以便可以监视进度,这有助于确保代码正常工作。
Processing [2]
Processing [3]
Processing [5]
Processing [6]
Processing [10]
Processing [10]
Processing [2]
Processing [5]
Processing [2]
Processing [8]
Processing [8]
Processing [7]
Processing [8]
Processing [7]
Processing [8]
Processing [7]
value 3 is repeated 1 times
value 6 is repeated 1 times
value 5 is repeated 2 times
value 10 is repeated 2 times
value 2 is repeated 3 times
value 7 is repeated 3 times
value 8 is repeated 4 times
答案 1 :(得分:0)
让我们从问题陈述开始,因为正如我在评论中提到的,我不认为您当前的代码解决了这个问题:
找到数组中最常见的元素。
1&lt; N&lt; 100000 [阵列长度]
-1000000&lt; n&lt; 1000000 [数组整数]
我认为我们想要一个像这样的函数:
size_t n_most_popular(int input[], size_t input_size, int output[], size_t output_size);
此函数获取整数的输入数组(大小最大为100000)(介于-1000000和1000000之间),并使用输入中的N个最常见元素填充输出数组,其中N为output_size
。为方便起见,我们可以规定我们将最常见的元素放在输出的前面,而不太常见的元素放在后面。
直接的方法是首先对输入数组进行排序(可能使用标准qsort()
)。那么你将有一个像这样的数组:
[1,1,1,1,2,2,3,3,3,3,3,4,7,...]
然后,创建一个结构数组,其中每个结构包含输入中的唯一值,以及它发生的次数。这个的最大长度是input_size
,并且从排序的输入中一次性构建它是微不足道的。
最后,使用标准qsort()
按计数字段降序对此结构数组进行排序。将第一个output_size
元素复制到输出数组,并返回输出数组的实际填充大小(如果输入数组中没有足够的唯一值,则可能小于output_size
。) p>
这是一个有效的C程序:
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
size_t most_popular(int input[], size_t input_size, int output[], size_t output_size);
int main(void)
{
int arr[] = {2, 3, 5, 6, 10, 10, 2, 5, 2};
size_t len = sizeof(arr)/sizeof(int);
int out[3];
size_t outlen = sizeof(out)/sizeof(int);
size_t count = most_popular(arr, len, out, outlen);
for (size_t ii = 0; ii < count; ii++) {
printf("most popular rank %lu: %d\n", ii+1, out[ii]);
}
return 0;
}
typedef struct
{
int value;
int count;
} value_count;
int value_count_greater(const void* lhs, const void* rhs)
{
const value_count *vcl = lhs, *vcr = rhs;
return vcr->count - vcl->count;
}
int int_less(const void *lhs, const void *rhs)
{
const int *il = lhs, *ir = rhs;
return *il - *ir;
}
// returns 0 if out of memory or input_size is 0, else returns valid portion of output
size_t most_popular(int input[], size_t input_size, int output[], size_t output_size)
{
qsort(input, input_size, sizeof(input[0]), int_less);
value_count* value_counts = malloc(input_size * sizeof(value_count));
if (value_counts == NULL) {
return 0;
}
// count how many times each value occurs in input
size_t unique_count = 0;
for (size_t ii = 0; ii < input_size; ii++) {
if (ii == 0 || input[ii] != value_counts[unique_count-1].value) {
value_counts[unique_count].value = input[ii];
value_counts[unique_count].count = 1;
unique_count++;
} else {
value_counts[unique_count-1].count++;
}
}
// sort unique values by how often they occur, most popular first
qsort(value_counts, unique_count, sizeof(value_counts[0]), value_count_greater);
size_t result_size = unique_count < output_size ? unique_count : output_size;
for (size_t ii = 0; ii < result_size; ii++) {
output[ii] = value_counts[ii].value;
}
free(value_counts);
return result_size;
}