查找数组中重复数字的算法---最快的方法

时间:2009-12-05 09:38:41

标签: c algorithm

我需要最快速,最简单的算法,在数组中找到重复的数字,也应该能够知道重复的数量。

例如:如果数组是{2,3,4,5,2,4,6,2,4,7,3,8,2}

我应该知道有4个,2个3个,3个4个。

15 个答案:

答案 0 :(得分:3)

使用Linq可以很好地解决这个问题:

public static void Main(string[] args)
{
    List<int> list = new List<int> { 2, 3, 4, 5, 2, 4, 6, 2, 4, 7, 3, 8, 2 };

    var grouping = list
        .GroupBy(x => x)
        .Select(x => new { Item = x.Key, Count = x.Count()});

    foreach (var item in grouping)
        Console.WriteLine("Item {0} has count {1}", item.Item, item.Count);
}

在内部,它可能使用散列来对列表进行分区,但代码隐藏了内部细节 - 这里我们只告诉它要计算什么。编译器/运行时可以自由选择如何来计算它,并根据需要进行优化。感谢Linq,无论是在内存中运行列表,还是列表都在数据库中,相同的代码都能高效运行。在实际代码中你应该使用它,但我想你想知道它的内部工作原理。

演示实际算法的更为迫切的方法如下:

    List<int> list = new List<int> { 2, 3, 4, 5, 2, 4, 6, 2, 4, 7, 3, 8, 2 };

    Dictionary<int, int> counts = new Dictionary<int, int>();
    foreach (int item in list)
    {
        if (!counts.ContainsKey(item))
        {
            counts[item] = 1;
        }
        else
        {
            counts[item]++;
        }
    }

    foreach (KeyValuePair<int, int> item in counts)
        Console.WriteLine("Item {0} has count {1}", item.Key, item.Value);

在这里你可以看到我们只对列表进行一次迭代,并为我们在途中看到的每个项目保留一个计数。如果项目在数据库中,这将是一个坏主意,因此对于实际代码,更喜欢使用Linq方法。

答案 1 :(得分:3)

创建一个哈希表,其中键是数组项,值是计数器在数组中出现相应数组项的次数。这是一种有效的方法,但可能不是最快的方式。

像这样(伪代码)。您将find plenty of hash map implementations for C by googling

 hash_map = create_new_hash_map()
 for item in array {
   if hash_map.contains_key(item){
      counter = hash_map.get(item)
   } else {
      counter = 0
   }
   counter = counter + 1
   hash_map.put(item, counter)
 }

答案 2 :(得分:3)

这是一个用标准输入做的C版本;它与输入的长度一样快(注意,命令行上的参数数量有限......)但是应该让你知道如何继续:

#include <stdio.h>

int main ( int argc, char **argv ) {
    int dups[10] = { 0 };
    int i;

    for ( i = 1 ; i < argc ; i++ ) 
        dups[atoi(argv[i])]++;

    for ( i = 0 ; i < 10 ; i++ )
        printf("%d: %d\n", i, dups[i]);

    return 0;
}

示例用法:

    $ gcc -o dups dups.c

    $ ./dups 0 0 3 4 5
0: 2
1: 0
2: 0
3: 1
4: 1
5: 1
6: 0
7: 0
8: 0
9: 0
警告:

  • 如果您打算计算10s,11s等的数量 - &gt; dups []数组必须更大

  • 左边作为练习是实现从整数数组中读取并确定其位置

答案 3 :(得分:2)

如果您知道下限和上限,并且它们相距不太远,那么这将是使用Radix Sort的好地方。由于这是家庭作业的气味,我将把它留给OP阅读文章并实施算法。

答案 4 :(得分:2)

您告诉我们关于输入数组越多,我们制作算法的速度就越快。例如,对于您的单位数字的示例,然后创建一个包含10个元素的数组(索引为0:9)并在数组的右侧元素中累积数字的出现次数(措辞不当,但您可能会抓住我的漂移)是可能比散列更快。 (我说可能会更快,因为我没有做任何测量而不会)。

我同意大多数受访者认为散列可能是最常见案例的正确方法,但总是值得考虑你的是否是特例。

答案 5 :(得分:1)

如果您不想使用哈希表或类似的smtg,只需对数组进行排序,然后计算出现次数,如下所示

    Arrays.sort(array);
    lastOne=array's first element;
    count=0,
    for(i=0; i <array's length; i++)
    {
        if(array[i]==lastOne)
            increment count
        else        
            print(array[i] + " has " + count + " occurrences");
            lastOne=array[i+1];
    }

答案 6 :(得分:1)

如果数字的范围是已知的并且很小,您可以使用数组来跟踪您看到每个数字的次数(这本质上是一个存储桶排序)。如果它很大,你可以对它进行排序,然后计算重复数据,因为它们将相互跟随。

答案 7 :(得分:1)

您可以使用哈希表将每个元素值存储为键。然后每次键存在时增加+1。

答案 8 :(得分:0)

使用散列表/关联数组/字典(所有相同的东西,但术语在编程环境之间发生变化)是可行的方法。

作为python中的一个例子:

numberList = [1, 2, 3, 2, 1, ...]
countDict = {}
for value in numberList:
    countDict[value] = countDict.get(value, 0) + 1

# Now countDict contains each value pointing to their count

大多数编程语言都存在类似的结构。

答案 9 :(得分:0)

> I need the fastest and simple algorithm which finds the duplicate numbers in an array, also should be able to know the number of duplicates.

我认为最快的算法是计算数组中的重复项:

#include <stdlib.h> 
#include <stdio.h> 
#include <limits.h> 
#include <assert.h> 

typedef int arr_t;
typedef unsigned char dup_t;
const dup_t dup_t_max=UCHAR_MAX;

dup_t *count_duplicates( arr_t *arr, arr_t min, arr_t max, size_t arr_len ){
  assert( min <= max );
  dup_t *dup = calloc( max-min+1, sizeof(dup[0]) );
  for( size_t i=0; i<arr_len; i++ ){
    assert( min <= arr[i] && arr[i] <= max && dup[ arr[i]-min ] < dup_t_max );
    dup[ arr[i]-min ]++;
  }
  return dup;
}

int main(void){
  arr_t arr[] = {2,3,4,5,2,4,6,2,4,7,3,8,2};
  size_t arr_len = sizeof(arr)/sizeof(arr[0]);
  arr_t min=0, max=16;
  dup_t *dup = count_duplicates( arr, min, max, arr_len );
  printf( "  value count\n" );
  printf( "  -----------\n" );
  for( size_t i=0; i<(size_t)(max-min+1); i++ ){
    if( dup[i] ){
      printf( "%5i %5i\n", (int)(i+min), (int)(dup[i]) );
    }
  }
  free(dup);
}

注意:您不能在每个阵列上使用最快的算法。

答案 10 :(得分:0)

代码首先对数组进行排序,然后将唯一元素移动到前面,跟踪元素的数量。它比使用桶排序慢,但更方便。

#include <stdio.h>
#include <stdlib.h>

static int cmpi(const void *p1, const void *p2)
{
    int i1 = *(const int *)p1;
    int i2 = *(const int *)p2;
    return (i1 > i2) - (i1 < i2);
}

size_t make_unique(int values[], size_t count, size_t *occ_nums)
{
    if(!count) return 0;

    qsort(values, count, sizeof *values, cmpi);

    size_t top = 0;
    int prev_value = values[0];
    if(occ_nums) occ_nums[0] = 1;

    size_t i = 1;
    for(; i < count; ++i)
    {
        if(values[i] != prev_value)
        {
            ++top;
            values[top] = prev_value = values[i];
            if(occ_nums) occ_nums[top] = 1;
        }
        else ++occ_nums[top];
    }

    return top + 1;
}

int main(void)
{
    int values[] = { 2, 3, 4, 5, 2, 4, 6, 2, 4, 7, 3, 8, 2 };

    size_t occ_nums[sizeof values / sizeof *values];
    size_t unique_count = make_unique(
        values, sizeof values / sizeof *values, occ_nums);

    size_t i = 0;
    for(; i < unique_count; ++i)
    {
        printf("number %i occurred %u time%s\n",
            values[i], (unsigned)occ_nums[i], occ_nums[i] > 1 ? "s": "");
    }
}

答案 11 :(得分:0)

选项1:哈希吧。 选项2:对其进行排序,然后计算连续运行。

答案 12 :(得分:0)

我总是使用“算法”在Unix中的文件中找到重复的行:

sort file | uniq -d

如果你在C中实现相同的策略,那么用哈希表等更高级的策略来击败它是非常困难的。调用排序算法,然后调用您自己的函数来检测排序列表中的重复项。排序算法需要O(n * log(n))时间,uniq函数需要线性时间。 (Southern Hospitality提出了类似的观点,但我想强调的是,他所谓的“选项2”似乎比更流行的哈希表建议更简单,更快。)

答案 13 :(得分:0)

计算排序是上述问题的答案。如果您看到计数排序的算法,您将发现有一个数组被保留用于保持原始数组中存在的元素的计数。

答案 14 :(得分:0)

这是另一种解决方案,但需要O(nlogn)时间。 使用Divide and Conquer方法使用合并排序对给定数组进行排序。 在合并排序中的合并步骤期间,通过比较两个排序子阵列中的元素来查找重复项。