算法:从数组中删除重复整数的有效方法

Posted

技术标签:

【中文标题】算法:从数组中删除重复整数的有效方法【英文标题】:Algorithm: efficient way to remove duplicate integers from an array 【发布时间】:2010-12-04 16:56:25 【问题描述】:

我在接受微软采访时遇到了这个问题。

给定一个随机整数数组, 用 C 编写一个算法,删除 重复数字并返回原始数字中的唯一数字 数组。

例如输入:4, 8, 4, 1, 1, 2, 9 输出:4, 8, 1, 2, 9, ?, ?

需要注意的是,预期的算法不应该要求首先对数组进行排序。并且当一个元素被删除时,以下元素也必须向前移动。无论如何,数组尾部元素向前移动的元素的值可以忽略不计。

更新:结果必须在原始数组中返回,并且不应使用辅助数据结构(例如哈希表)。不过,我想订单保存是没有必要的。

更新 2:对于那些想知道为什么这些不切实际的限制的人,这是一个面试问题,所有这些限制都在思考过程中进行了讨论,以了解我如何提出不同的想法。

【问题讨论】:

必须保留唯一编号的顺序吗? 结果是否必须在原始数组中返回? 我已经更新了这个问题。结果应该在原始数组中返回。但是,序列的顺序无关紧要。 当有人在问题和其他答案上拉皮条时,这很烦人。请耐心等待,人们会到达那里的。 为什么不允许使用哈希表?这种限制毫无意义。 【参考方案1】:

我女朋友建议的解决方案是归并排序的一种变体。唯一的修改是在合并步骤期间,只需忽略重复的值。这个解决方案也是 O(n log n)。在这种方法中,排序/重复删除结合在一起。不过,我不确定这是否有什么不同。

【讨论】:

很好的建议,但您需要一些簿记来跟踪每个合并输出的结束。我实际上做过一次,是的,在合并时消除重复项会更快。 尚不清楚 O(N/2) 额外空间是否算作问题中禁止的“辅助数据结构” - 我不知道该限制是否旨在规定 O(1) 额外空间,或者只是为了规定答案不应该依赖于大数据结构的实现。也许标准合并很好。但如果不是,重要提示:不要尝试在面试中编写就地合并排序,除非您真的知道自己在做什么。 好主意。但它要求剩余数据保持原始顺序。 一篇描述你女朋友建议的论文如下:dc-pubs.dbs.uni-leipzig.de/files/…【参考方案2】:

我之前在 SO 上发布过一次,但我会在这里复制它,因为它非常酷。它使用散列,在适当的位置构建类似于散列集的东西。它保证在腋窝空间中是 O(1)(递归是尾调用),并且通常是 O(N) 时间复杂度。算法如下:

    取出数组的第一个元素,这将是哨兵。 尽可能对数组的其余部分重新排序,以使每个元素都位于与其哈希相对应的位置。完成此步骤后,将发现重复项。将它们设置为哨兵。 将索引等于散列的所有元素移动到数组的开头。 将所有等于 sentinel 的元素(数组的第一个元素除外)移动到数组的末尾。 正确散列的元素和重复元素之间剩下的将是由于冲突而无法放入与其散列对应的索引中的元素。递归处理这些元素。

这可以证明是 O(N),前提是在散列中没有病态场景:即使没有重复,每次递归也会消除大约 2/3 的元素。每个级别的递归都是 O(n),其中小的 n 是剩余元素的数量。唯一的问题是,在实践中,当重复项很少时,它比快速排序要慢,即很多冲突。但是,当有大量重复时,它的速度非常快。

编辑:在 D 的当前实现中,hash_t 是 32 位。这个算法的所有内容都假设在完整的 32 位空间中,哈希冲突非常少(如果有的话)。然而,碰撞可能经常发生在模数空间中。但是,对于任何合理大小的数据集,这个假设很可能是正确的。如果密钥小于或等于 32 位,它可以是它自己的哈希,这意味着在完整的 32 位空间中发生冲突是不可能的。如果它更大,那么您根本无法将足够多的它们放入 32 位内存地址空间中,这将成为一个问题。我假设在 D 的 64 位实现中 hash_t 将增加到 64 位,其中数据集可以更大。此外,如果这确实被证明是一个问题,则可以在每个递归级别更改哈希函数。

下面是 D 编程语言的一个实现:

void uniqueInPlace(T)(ref T[] dataIn) 
    uniqueInPlaceImpl(dataIn, 0);


void uniqueInPlaceImpl(T)(ref T[] dataIn, size_t start) 
    if(dataIn.length - start < 2)
        return;

    invariant T sentinel = dataIn[start];
    T[] data = dataIn[start + 1..$];

    static hash_t getHash(T elem) 
        static if(is(T == uint) || is(T == int)) 
            return cast(hash_t) elem;
         else static if(__traits(compiles, elem.toHash)) 
            return elem.toHash;
         else 
            static auto ti = typeid(typeof(elem));
            return ti.getHash(&elem);
        
    

    for(size_t index = 0; index < data.length;) 
        if(data[index] == sentinel) 
            index++;
            continue;
        

        auto hash = getHash(data[index]) % data.length;
        if(index == hash) 
            index++;
            continue;
        

        if(data[index] == data[hash]) 
            data[index] = sentinel;
            index++;
            continue;
        

        if(data[hash] == sentinel) 
            swap(data[hash], data[index]);
            index++;
            continue;
        

        auto hashHash = getHash(data[hash]) % data.length;
        if(hashHash != hash) 
            swap(data[index], data[hash]);
            if(hash < index)
                index++;
         else 
            index++;
        
    


    size_t swapPos = 0;
    foreach(i; 0..data.length) 
        if(data[i] != sentinel && i == getHash(data[i]) % data.length) 
            swap(data[i], data[swapPos++]);
        
    

    size_t sentinelPos = data.length;
    for(size_t i = swapPos; i < sentinelPos;) 
        if(data[i] == sentinel) 
            swap(data[i], data[--sentinelPos]);
         else 
            i++;
        
    

    dataIn = dataIn[0..sentinelPos + start + 1];
    uniqueInPlaceImpl(dataIn, start + swapPos + 1);

【讨论】:

非常酷,被低估的答案!我喜欢使用位置 1 中的元素作为标记值的想法。如果我可以提出几个小建议,那就是将步骤 2 更改为包括“每个元素位于与其哈希 以数组大小为模 对应的位置”,并可能澄清重复项设置为哨兵的是具有相同值的元素(而不是相同的哈希,或相同的哈希模数组大小)。【参考方案3】:

怎么样:

void rmdup(int *array, int length)

    int *current , *end = array + length - 1;

    for ( current = array + 1; array < end; array++, current = array + 1 )
    
        while ( current <= end )
        
            if ( *current == *array )
            
                *current = *end--;
            
            else
            
                current++;
            
        
    

应该是 O(n^2) 或更小。

【讨论】:

这是一个简单的解决方案,很可能是面试问题所要寻找的。​​span> 他们甚至可能会检查您是否沉迷于过早的优化,除非他们也给了您运行时限制! :-) 大声笑,尽管对数组进行排序并处理已排序的数组肯定更快。排序应该由 API 提供,恕我直言,没有过早的优化。 不应该是while(current 为什么这被接受为正确答案?如果不需要保留顺序,那么仅使用合并排序 O(nlogn) 然后删除 O(n) 中的重复元素不是更好吗...总复杂度 - O(nlogn) 这比这个解决方案要好得多。 【参考方案4】:

如果您正在寻找优越的 O 表示法,那么使用 O(n log n) 排序对数组进行排序,然后执行 O(n) 遍历可能是最好的方法。没有排序,你正在查看 O(n^2)。

编辑:如果你只是在做整数,那么你也可以做基数排序来得到 O(n)。

【讨论】:

Jeff B 的答案只是 O(n)。哈希集和哈希字典是蜜蜂的膝盖。 ChrisW:如果假设没有冲突,哈希集/字典只有 O(1)。 (我并不是说我不会用它们来解决这个问题——我可能会——声称它们真的是 O(1) 只是一种谬误。) 其实既然事先知道数组的大小,就可以保证O(1)。然后,您可以在冲突与使用的额外内存之间进行权衡。 您可能需要重新考虑投反对票 - 新发布的问题条件使 Jeff B 的解决方案无效。 您可能需要详细说明“遍历”,因为幼稚的擦除方法可能会导致大量重复的 O(n^2)。【参考方案5】:

一种更高效的实现方式

int i, j;

/* new length of modified array */
int NewLength = 1;

for(i=1; i< Length; i++)

   for(j=0; j< NewLength ; j++)
   

      if(array[i] == array[j])
      break;
   

   /* if none of the values in index[0..j] of array is not same as array[i],
      then copy the current value to corresponding new position in array */

  if (j==NewLength )
      array[NewLength++] = array[i];

在这个实现中,不需要对数组进行排序。 此外,如果发现重复元素,则无需将其后的所有元素移动一个位置。

这段代码的输出是数组[],大小为NewLength

这里我们从数组中的第二个元素开始,并将它与数组中的所有元素进行比较,直到这个数组。 我们持有一个额外的索引变量“NewLength”来修改输入数组。 NewLength 变量初始化为 0。

array[1] 中的元素将与 array[0] 进行比较。 如果它们不同,则 array[NewLength] 中的值将被 array[1] 修改并增加 NewLength。 如果相同,则不修改NewLength。

所以如果我们有一个数组 [1 2 1 3 1], 那么

在'j'循环的第一遍中,array[1] (2) 将与 array0 进行比较,然后将 2 写入 array[NewLength] = array[1] 所以数组将是 [1 2] 因为 NewLength = 2

在'j'循环的第二遍中,array[2] (1) 将与array0 和array1 进行比较。这里由于 array[2] (1) 和 array0 是相同的循环将在这里中断。 所以数组将是 [1 2] 因为 NewLength = 2

等等

【讨论】:

不错的一个。我有一个改进的建议。第二个嵌套循环可以更改为 for(j=0; j 这是一个很好的建议。我已经根据你的评论更新了代码 如果我们在数组 1,1,1,1,1,1 中有相同的值,至少会失败。无用的代码。 这到底有多复杂,不也是O(n^2)吗? 这么多的赞成票,但这效率不高:当重复的次数很少时,它是 O(n^2)。【参考方案6】:

1.在 O(n log n) 时间内使用 O(1) 额外空间

这是可能的,例如:

首先进行原地 O(n log n) 排序 然后遍历列表一次,将 every 的第一个实例写回列表的开头

我相信 ejel 的合作伙伴是正确的,最好的方法是使用简化的合并步骤进行就地合并排序,如果你是这样的话,这可能是问题的意图。编写一个新的库函数来尽可能高效地执行此操作,而无法改进输入,并且在某些情况下,根据输入的种类,在没有哈希表的情况下这样做会很有用。但我还没有真正检查过。

2。在 O(n) 时间内使用 O(lots) 额外空间

声明一个足以容纳所有整数的零数组 遍历数组一次 将每个整数对应的数组元素设置为 1。 如果它已经是 1,则跳过该整数。

这只有在几个有问题的假设成立时才有效:

可以很便宜地将内存归零,或者 int 的大小与它们的数量相比很小 您很高兴向您的操作系统请求 256^sizepof(int) 内存 如果它是巨大的,它会非常有效地为您缓存它

这是一个糟糕的答案,但如果您有很多输入元素,但它们都是 8 位整数(甚至可能是 16 位整数),这可能是最好的方法。

3. O(little)-ish 额外空间,O(n)-ish 时间

同 #2,但使用哈希表。

4.明路

如果元素的数量很少,如果其他代码写得更快,读起来也更快,那么写一个合适的算法就没用了。

例如。遍历每个唯一元素的数组(即第一个元素、第二个元素(第一个元素的重复项已被删除)等)删除所有相同的元素。 O(1) 额外空间,O(n^2) 时间。

例如。使用执行此操作的库函数。效率取决于你有哪些容易获得的。

【讨论】:

【参考方案7】:

嗯,它的基本实现非常简单。遍历所有元素,检查其余元素中是否存在重复项,然后将其余元素移到它们之上。

这是非常低效的,您可以通过输出或排序/二叉树的辅助数组来加速它,但这似乎是不允许的。

【讨论】:

OTOH,实现排序树所需的附加代码可能比简单解决方案的(内存)效率更低,并且在运行时对于小型(比如少于 100 个元素)数组的效率可能更低.【参考方案8】:

如果您被允许使用 C++,调用 std::sort 然后调用 std::unique 会给您答案。排序的时间复杂度为 O(N log N),唯一遍历的时间复杂度为 O(N)。

如果 C++ 被淘汰,那么没有任何东西可以阻止这些相同的算法用 C 编写。

【讨论】:

"一个警告是预期的算法不应该要求首先对数组进行排序。" 这并不是说一旦得到数组就不能对数组进行排序...不使用 O(N) 外部内存排序是在 O(N log N) 中进行排序的唯一方法或更好。 为了解决问题,不应使用标准库工具。然而,关于排序,我越想它,我就越不确定它是否可以。 我认为参考 C++ 和 C++ 标准函数的答案很有用,即使它们没有回答最初的问题,因为它们为后来发现这个问题的人提供了更全面的答案。【参考方案9】:

如果您愿意牺牲内存,您可以在一次遍历中完成此操作。您可以简单地计算您是否在哈希/关联数组中看到了一个整数。如果您已经看到了一个数字,请随时将其删除,或者更好的是,将您未见过的数字移动到新数组中,避免原始数组中的任何移动。

在 Perl 中:

foreach $i (@myary) 
    if(!defined $seen$i) 
        $seen$i = 1;
        push @newary, $i;
    

【讨论】:

不清楚答案是否必须在原始数组中。 要在不需要新数组的情况下执行此操作,您可以简单地用从数组末尾弹出的元素替换重复项,然后重做当前循环,因为问题没有指定顺序很重要。这需要一些额外的边界检查,但非常可行。 这是个好主意,直​​到问题被编辑。您的哈希表想法显然违反了规则。 我不明白为什么这个答案得到最多的投票。正如问题所问,它是用 perl 编写的,并使用了 C 中没有的重要功能。 这个问题要求的是 c 代码,而不是 perl。使用 perl 可以免费获得哈希表和“推送”。如果我可以在 scala 中做到这一点,您只需调用 input.removeDuplicates,但我怀疑面试官是否可以接受 :)【参考方案10】:

函数的返回值应该是唯一元素的数量,它们都存储在数组的前面。如果没有这些附加信息,您甚至都不知道是否有任何重复。

外循环的每次迭代都会处理数组的一个元素。如果它是唯一的,它会留在数组的前面,如果它是重复的,它会被数组中最后一个未处理的元素覆盖。此解决方案在 O(n^2) 时间内运行。

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

size_t rmdup(int *arr, size_t len)

  size_t prev = 0;
  size_t curr = 1;
  size_t last = len - 1;
  while (curr <= last) 
    for (prev = 0; prev < curr && arr[curr] != arr[prev]; ++prev);
    if (prev == curr) 
      ++curr;
     else 
      arr[curr] = arr[last];
      --last;
    
  
  return curr;


void print_array(int *arr, size_t len)

  printf("");
  size_t curr = 0;
  for (curr = 0; curr < len; ++curr) 
    if (curr > 0) printf(", ");
    printf("%d", arr[curr]);
  
  printf("");


int main()

  int arr[] = 4, 8, 4, 1, 1, 2, 9;
  printf("Before: ");
  size_t len = sizeof (arr) / sizeof (arr[0]);
  print_array(arr, len);
  len = rmdup(arr, len);
  printf("\nAfter: ");
  print_array(arr, len);
  printf("\n");
  return 0;

【讨论】:

【参考方案11】:

这是一个 Java 版本。

int[] removeDuplicate(int[] input)

        int arrayLen = input.length;
        for(int i=0;i<arrayLen;i++)
            for(int j = i+1; j< arrayLen ; j++)
                if(((input[i]^input[j]) == 0))
                    input[j] = 0;
                
                if((input[j]==0) && j<arrayLen-1)
                        input[j] = input[j+1];
                        input[j+1] = 0;
                                   
            
               
        return input;       
    

【讨论】:

至少在下一个输入时失败:1,1,1,1,1,1,1 0,0,0,0,0,1,1,1,1,1 ,1【参考方案12】:

这是我的解决方案。

///// find duplicates in an array and remove them

void unique(int* input, int n)

     merge_sort(input, 0, n) ;

     int prev = 0  ;

     for(int i = 1 ; i < n ; i++)
     
          if(input[i] != input[prev])
               if(prev < i-1)
                   input[prev++] = input[i] ;                         
     

【讨论】:

【参考方案13】:

一个数组显然应该从右到左“遍历”以避免不必要地来回复制值。

如果你有无限的内存,你可以为sizeof(type-of-element-in-array) / 8字节分配一个位数组,让每个位表示你是否已经遇到了相应的值。

如果你不这样做,我想不出比遍历一个数组并将每个值与它后面的值进行比较,然后如果发现重复,则完全删除这些值更好的方法。这是O(n^2)(或O((n^2-n)/2))附近的某个地方。

IBM 有一个article,主题有点接近。

【讨论】:

确实 - 寻找最大元素的 O(n) 通道不会增加总体 O() 成本。【参考方案14】:

让我们看看:

O(N) 次通过找到最小/最大分配 找到的位数组 O(N) 次通过交换重复到结束。

【讨论】:

鉴于它们只是整数,为简单起见,您可以假设 32 位,而不必费心寻找最小/最大值:2^32 位“仅”512MB,因此找到边界只是一个记忆-使用和 O(1) 时间优化(在给定示例的情况下,这是一个巨大的优化)。如果它们是 64 位,则无关紧要,因为您不知道 min 和 max 不会比您拥有的内存位数更远。 抛开理论不谈,分配 512MB 不会比找到最小值/最大值花费更多时间吗? 取决于有多少数据,以及最小值/最大值是多少。如果您正在查看超过 512MB 的输入,那么避免额外的 O(N) 传递很可能会更快。当然,如果您正在查看这么多的输入,那么您不太可能有 512MB 的空闲空间。在最小值/最大值接近 0/INT_MAX 的情况下,优化也无济于事。我只是说,虽然第一步显然对小数字有帮助,但它无法避免这个算法在最坏情况下使用 UINT_MAX 位的事实,所以你需要为这个限制做好计划。 您很可能是对的——无论如何,澄清问题意味着使用位数组已经过时了。我会留下这个答案,以防以后有人没有限制地出现并想要查看所有可能的答案。【参考方案15】:

这可以通过 O(N log N) 算法一次性完成,无需额外存储。

从元素a[1] 继续到a[N]。在i 的每个阶段,a[i] 左侧的所有元素都包含一个排序的元素堆a[0]a[j]。同时,第二个索引j(最初为 0)跟踪堆的大小。

检查a[i] 并将其插入到堆中,该堆现在占用元素a[0]a[j+1]。在插入元素时,如果遇到具有相同值的重复元素a[k],则不要将a[i] 插入堆中(即丢弃它);否则将其插入堆中,该堆现在增长一个元素,现在包含a[0]a[j+1],并递增j

以这种方式继续,递增i,直到所有数组元素都被检查并插入到堆中,最终占用a[0]a[j]j 是堆最后一个元素的索引,堆只包含唯一的元素值。

int algorithm(int[] a, int n)

    int   i, j;  

    for (j = 0, i = 1;  i < n;  i++)
    
        // Insert a[i] into the heap a[0...j]
        if (heapInsert(a, j, a[i]))
            j++;
    
    return j;
  

bool heapInsert(a[], int n, int val)

    // Insert val into heap a[0...n]
    ...code omitted for brevity...
    if (duplicate element a[k] == val)
        return false;
    a[k] = val;
    return true;

看看这个例子,这并不是我们所要求的,因为结果数组保留了原始元素的顺序。但是如果放宽这个要求,上面的算法应该可以解决问题。

【讨论】:

【参考方案16】:

在 Java 中,我会这样解决。不知道怎么用 C 写这个。

   int length = array.length;
   for (int i = 0; i < length; i++) 
   
      for (int j = i + 1; j < length; j++) 
      
         if (array[i] == array[j]) 
         
            int k, j;
            for (k = j + 1, l = j; k < length; k++, l++) 
            
               if (array[k] != array[i]) 
               
                  array[l] = array[k];
               
               else
               
                  l--;
               
            
            length = l;
         
      
   

【讨论】:

如果你用数组末尾的值覆盖你找到的重复项,你可以避免在你的内部 for() 循环中移动整个数组。这将把你从 O(n^3) 带到 O(n^2)。我的 C 实现在某处漂浮... 我认为,转移是要求的一部分,但你当然是对的。 @mocj:我喜欢你的解决方案,看起来很优雅。但我认为如果最后两个元素相等则不起作用,因为您在最后一个元素之前停止检查相等性。 (在这里评论是因为在其他任何地方都无法评论:() 你是对的,只是原始问题指出数组末尾的值可以忽略不计。由于您没有返回修改后数组的长度,因此当两个值相等时,最后一个值和倒数第二个值之间的区别并不重要。调用者在哪里解释返回数组的结尾是【参考方案17】:

下面的呢?

int* temp = malloc(sizeof(int)*len);
int count = 0;
int x =0;
int y =0;
for(x=0;x<len;x++)

    for(y=0;y<count;y++)
    
        if(*(temp+y)==*(array+x))
        
            break;
        
    
    if(y==count)
    
        *(temp+count) = *(array+x);
        count++;
    

memcpy(array, temp, sizeof(int)*len);

我尝试声明一个临时数组并将元素放入其中,然后将所有内容复制回原始数组。

【讨论】:

【参考方案18】:

查看问题后,这是我的delphi方式,可能会有所帮助

var
A: Array of Integer;
I,J,C,K, P: Integer;
begin
C:=10;
SetLength(A,10);
A[0]:=1; A[1]:=4; A[2]:=2; A[3]:=6; A[4]:=3; A[5]:=4;
A[6]:=3; A[7]:=4; A[8]:=2; A[9]:=5;

for I := 0 to C-1 do
begin
  for J := I+1 to C-1 do
    if A[I]=A[J] then
    begin
      for K := C-1 Downto J do
        if A[J]<>A[k] then
        begin
          P:=A[K];
          A[K]:=0;
          A[J]:=P;
          C:=K;
          break;
        end
        else
        begin
          A[K]:=0;
          C:=K;
        end;
    end;
end;

//tructate array
setlength(A,C);
end;

【讨论】:

【参考方案19】:

以下示例应该可以解决您的问题:

def check_dump(x):
   if not x in t:
      t.append(x)
      return True

t=[]

output = filter(check_dump, input)

print(output)
True

【讨论】:

【参考方案20】:
import java.util.ArrayList;


public class C 

    public static void main(String[] args) 

        int arr[] = 2,5,5,5,9,11,11,23,34,34,34,45,45;

        ArrayList<Integer> arr1 = new ArrayList<Integer>();

        for(int i=0;i<arr.length-1;i++)

            if(arr[i] == arr[i+1])
                arr[i] = 99999;
            
        

        for(int i=0;i<arr.length;i++)
            if(arr[i] != 99999)

                arr1.add(arr[i]);
            
        

        System.out.println(arr1);

    

【讨论】:

arr[i+1] 应该为最后一个元素抛出 ArrayIndexOutOfBoundsException 吗? @Sathesh No. 因为“【参考方案21】:

这是简单的 (N*(N-1)/2) 解决方案。它使用恒定的额外空间并保持原始顺序。它类似于@Byju 的解决方案,但不使用if() 块。它还避免了将元素复制到自身上。

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

int numbers[] = 4, 8, 4, 1, 1, 2, 9;
#define COUNT (sizeof numbers / sizeof numbers[0])

size_t undup_it(int array[], size_t len)

size_t src,dst;

  /* an array of size=1 cannot contain duplicate values */
if (len <2) return len; 
  /* an array of size>1 will cannot at least one unique value */
for (src=dst=1; src < len; src++) 
        size_t cur;
        for (cur=0; cur < dst; cur++ ) 
                if (array[cur] == array[src]) break;
                
        if (cur != dst) continue; /* found a duplicate */

                /* array[src] must be new: add it to the list of non-duplicates */
        if (dst < src) array[dst] = array[src]; /* avoid copy-to-self */
        dst++;
        
return dst; /* number of valid alements in new array */


void print_it(int array[], size_t len)

size_t idx;

for (idx=0; idx < len; idx++)  
        printf("%c %d", (idx) ? ',' :'' , array[idx] );
        
printf("\n" );


int main(void)     
    size_t cnt = COUNT;

    printf("Before undup:" );    
    print_it(numbers, cnt);    

    cnt = undup_it(numbers,cnt);

    printf("After undup:" );    
    print_it(numbers, cnt);

    return 0;

【讨论】:

【参考方案22】:

这可以一次完成,在 O(N) 时间内输入的整数个数 列表,以及 O(N) 存储的唯一整数个数。

从前到后遍历列表,使用两个指针“dst”和 “src”初始化为第一项。从一个空的哈希表开始 “看到的整数”。如果 src 处的整数不存在于哈希中, 将其写入 dst 的插槽并增加 dst。在 src 添加整数 到哈希,然后增加 src。重复直到 src 结束 输入列表。

【讨论】:

在对原问题的修改中,不允许使用哈希表。不过,一旦您确定了重复项,您的两指针方法是一种压缩输出的好方法。【参考方案23】:

binary tree the disregards duplicates - O(nlog(n)) 中插入所有元素。然后通过遍历 -O(n) 将它们全部提取回数组中。我假设您不需要保留订单。

【讨论】:

【参考方案24】:

使用布隆过滤器进行散列。这将大大减少内存开销。

【讨论】:

想详细说明或提供参考?【参考方案25】:

在 JAVA 中,

    Integer[] arrayInteger = 1,2,3,4,3,2,4,6,7,8,9,9,10;

    String value ="";

    for(Integer i:arrayInteger)
    
        if(!value.contains(Integer.toString(i)))
            value +=Integer.toString(i)+",";
        

    

    String[] arraySplitToString = value.split(",");
    Integer[] arrayIntResult = new Integer[arraySplitToString.length];
    for(int i = 0 ; i < arraySplitToString.length ; i++)
        arrayIntResult[i] = Integer.parseInt(arraySplitToString[i]);
    

输出: 1, 2, 3, 4, 6, 7, 8, 9, 10

希望这会有所帮助

【讨论】:

用输入arrayInteger = 100,10,1;测试这个【参考方案26】:

创建一个复杂度为 O(n) 的 BinarySearchTree

【讨论】:

【参考方案27】:

首先,您应该创建一个数组check[n],其中 n 是您希望避免重复的数组元素的数量,并将(检查数组的)每个元素的值设置为等于 1。使用 for循环遍历重复的数组,说它的名字是arr,然后在for循环中写下:


    if (check[arr[i]] != 1) 
        arr[i] = 0;
    
    else 
        check[arr[i]] = 0;
    

这样,您将每个重复项设置为零。所以剩下要做的就是遍历arr 数组并打印它不等于零的所有内容。订单保持不变,需要线性时间 (3*n)。

【讨论】:

该问题不允许使用额外的数据结构。【参考方案28】:

给定一个包含 n 个元素的数组,编写一个算法以在 O(nlogn) 时间内从数组中删除所有重复项

Algorithm delete_duplicates (a[1....n])
//Remove duplicates from the given array 
//input parameters :a[1:n], an array of n elements.



temp[1:n]; //an array of n elements. 

temp[i]=a[i];for i=1 to n

 temp[i].value=a[i]

temp[i].key=i

 //based on 'value' sort the array temp.

//based on 'value' delete duplicate elements from temp.

//based on 'key' sort the array temp.//construct an array p using temp.

 p[i]=temp[i]value

  return p.

使用“键”在输出数组中维护其他元素。考虑键的长度为 O(n),对键和值执行排序所需的时间为 O(nlogn)。所以从数组中删除所有重复项所花费的时间是 O(nlogn)。

【讨论】:

对于所有粗体字形,您对 helper data structure (e.g. hashtable) should not be used 有何看法? 不一定需要。我只是为了理解而强调了这些。【参考方案29】:

这就是我所拥有的,尽管它错位了我们可以按升序或降序排序来修复它的顺序。

#include <stdio.h>
int main(void)
int x,n,myvar=0;
printf("Enter a number: \t");
scanf("%d",&n);
int arr[n],changedarr[n];

for(x=0;x<n;x++)
    printf("Enter a number for array[%d]: ",x);
    scanf("%d",&arr[x]);

printf("\nOriginal Number in an array\n");
for(x=0;x<n;x++)
    printf("%d\t",arr[x]);


int i=0,j=0;
// printf("i\tj\tarr\tchanged\n");

for (int i = 0; i < n; i++)

    // printf("%d\t%d\t%d\t%d\n",i,j,arr[i],changedarr[i] );
    for (int j = 0; j <n; j++)
       
        if (i==j)
        
            continue;

        
        else if(arr[i]==arr[j])
            changedarr[j]=0;

        
        else
            changedarr[i]=arr[i];

        
    // printf("%d\t%d\t%d\t%d\n",i,j,arr[i],changedarr[i] );
    
    myvar+=1;

// printf("\n\nmyvar=%d\n",myvar);
int count=0;
printf("\nThe unique items:\n");
for (int i = 0; i < myvar; i++)

        if(changedarr[i]!=0)
            count+=1;
            printf("%d\t",changedarr[i]);   
        

    printf("\n");

【讨论】:

【参考方案30】:

如果你有一个很好的 DataStructure 可以快速判断它是否包含整数,那就太棒了。也许是某种树。

DataStructure elementsSeen = new DataStructure();
int elementsRemoved = 0;
for(int i=0;i<array.Length;i++)
  if(elementsSeen.Contains(array[i])
    elementsRemoved++;
  else
    array[i-elementsRemoved] = array[i];

array.Length = array.Length - elementsRemoved;

【讨论】:

以上是关于算法:从数组中删除重复整数的有效方法的主要内容,如果未能解决你的问题,请参考以下文章

如何在不使用 Set 的情况下有效地从数组中删除重复项

排序旋转整数数组,搜索算法[重复]

识别和删除数组中重复项的最有效方法是啥?

使用 std::map 从数组中删除重复项

数据结构和算法爆肝三万字你必须知道的20个解决问题的技巧

数据结构和算法爆肝三万字你必须知道的20个解决问题的技巧