【问题标题】:Algorithm to find two repeated numbers in an array, without sorting在数组中查找两个重复数字的算法,无需排序
【发布时间】:2010-10-08 00:45:02
【问题描述】:

有一个大小为 n 的数组(数字介于 0 和 n - 3 之间)并且只有 2 个数字重复。元素随机放置在数组中。

例如在{2, 3, 6, 1, 5, 4, 0, 3, 5} n=9,重复数字是3和5。

找到重复数字的最佳方法是什么?

附: [你不应该使用排序]

【问题讨论】:

  • “最佳”是什么意思?复杂?存储?
  • 整数还是浮点数?连续数?
  • 就地桶排序不需要额外的内存,它是 O(n)。见stackoverflow.com/questions/177118/…
  • 您说“查找”,是不是意味着您想要他们在数组中的位置?或者是否足以识别重复值(如示例中所示)?

标签: algorithm search


【解决方案1】:

如果您知道可能的输入域是什么,则有一个 O(n) 解决方案。例如,如果您的输入数组包含 0 到 100 之间的数字,请考虑以下代码。

bool flags[100];
for(int i = 0; i < 100; i++)
    flags[i] = false;

for(int i = 0; i < input_size; i++)
    if(flags[input_array[i]])
         return input_array[i];
    else       
        flags[input_array[i]] = true;

当然还有额外的内存,但这是最快的。

【讨论】:

  • 如果元素是整数或字符串,这很常见,那么这种方法就行不通了。
  • 问题已经指定了输入的域,所以这是完全可以接受的。
  • 哈希表可以替换数组,它适用于任何输入。正如我所提到的,缺点是需要额外的内存,但在速度方面它可以工作。
  • @sesh 这是最好的技术,但它可以做得更好——如果数字范围从 0 到 n,那么你只需要 n 位,而不是字节或布尔值。这听起来很迂腐,但这些类型的问题通常会被表述为 n 以十亿计。
  • 安德鲁 - 你是对的。只有我懒得输入位操作——它在文本编辑器中疯狂输入代码:)
【解决方案2】:

好吧,看来我不能休息了:)

最简单的解决方案

int A[N] = {...};

int signed_1(n) { return n%2<1 ? +n : -n;  } // 0,-1,+2,-3,+4,-5,+6,-7,...
int signed_2(n) { return n%4<2 ? +n : -n;  } // 0,+1,-2,-3,+4,+5,-6,-7,...

long S1 = 0;  // or int64, or long long, or some user-defined class
long S2 = 0;  // so that it has enough bits to contain sum without overflow

for (int i=0; i<N-2; ++i)
{
   S1 += signed_1(A[i]) - signed_1(i);
   S2 += signed_2(A[i]) - signed_2(i);
} 

for (int i=N-2; i<N; ++i)
{
   S1 += signed_1(A[i]);
   S2 += signed_2(A[i]);
} 

S1 = abs(S1);
S2 = abs(S2);

assert(S1 != S2);  // this algorithm fails in this case

p = (S1+S2)/2;
q = abs(S1-S2)/2;

一个和(S1 或 S2)包含符号相同的 p 和 q,另一个和 - 符号相反,所有其他成员都被消除。
S1 和 S2 必须有足够的位来容纳和,由于 abs(),算法不代表溢出。

如果 abs(S1)==abs(S2) 则算法失败,尽管该值仍然是 p 和 q 之间的差值(即 abs(p - q) == abs(S1))。

以前的解决方案

我怀疑有人会在该领域遇到这样的问题;)
我猜,我知道老师的期望:

让我们使用数组 {0,1,2,...,n-2,n-1},
给定的可以通过用未知的 p 和 q 替换最后两个元素 n-2 和 n-1 来生成(较少顺序)

所以,元素的总和将是 (n-1)n/2 + p + q - (n-2) - (n-1) 平方和 (n-1)n(2n-1)/6 + p^2 + q^2 - (n-2)^2 - (n-1)^2

简单的数学仍然存在:

  (1)  p+q = S1  
  (2)  p^2+q^2 = S2

你肯定不会像数学课教解平方方程那样解它。

首先,以 2^32 为模计算所有内容,即允许溢出。
然后检查对 {p,q}: {0, S1}, {1, S1-1} ... 根据表达式 (2) 找到候选对象 (由于取模和平方可能有超过 2 个)
最后检查找到的候选者是否真的出现在数组中两次。

【讨论】:

  • O(N) 并且只需要两个整数变量来存储总和。可爱的。
  • 可爱,但可能令人困惑。 p+q != p^2+p^2.
  • 我认为这个解决方案并不完美。看看这个数组: int A[] = {2, 0, 6, 1, 1, 4, 2, 3, 5};其中 n = 9。我得到的结果是 {1, 0} - 尽管它确实适用于 OP 给出的数组示例......
  • 如果 p 和 q 都是 4 的倍数以及许多其他情况,则此解决方案不起作用。
【解决方案3】:

您知道您的数组包含从 0 到 n-3 的每个数字以及两个重复的数字 (p & q)。为简单起见,我们暂时忽略 0-case。

你可以计算数组的总和和乘积,结果是:

1 + 2 + ... + n-3 + p + q = p + q + (n-3)(n-2)/2

所以如果你从整个数组的总和中减去 (n-3)(n-2)/2,你会得到

sum(Array) - (n-3)(n-2)/2 = x = p + q

现在对产品做同样的事情:

1 * 2 * ... * n - 3 * p * q = (n - 3)! * p * q

prod(Array) / (n - 3)! = y = p * q

您现在得到了这些条款:

x = p + q

y = p * q

=> y(p + q) = x(p * q)

如果你变换这个项,你应该可以计算出p和q

【讨论】:

  • 根据 Viète 定理,pq 是方程的根:z2 - (p + q)*z + p*q = 0 因此 p,q = [k/2 + 1/2*(-4*m + k2)**(1/2), k/2 - 1/2*(-4*m + k**2) **(1/2)],其中 k=x,m=y。
【解决方案4】:

将每个元素插入到一个集合/哈希表中,首先检查它是否已经在其中。

【讨论】:

  • 如果不同值的总数很小(小于 100),它是否是一个集合并不重要。线性搜索列表会快很多倍。
  • @John 如果数字很小,那没关系,因为这两种方法都很快。对于较大的 n,集合的哈希表或树实现要好得多。另外,为概念上的集合选择列表是不好的。
  • @starblue 我反对这一点。我的观点是线性总是优于树、哈希表等等。
  • 散列毫无意义,因为您已经知道确切的域,即从 0 开始的整数。
  • 我认为 hashtable 比数组好,因为你不需要在创建它们时指定大小,而数组不是这样
【解决方案5】:

您也许可以利用 sum(array) = (n-2)*(n-3)/2 + 两个缺失数字这一事实。

编辑:正如其他人所指出的,结合平方和,你可以使用它,我只是有点慢了。

【讨论】:

  • 题中指定数字在0到n-3之间,加上两个重复的数字,所以0到n-3之间的每一个数字都必须在数组中。
  • 就算我知道总和,那又怎样??
  • 数组元素的总和包含一次,因此公式为:x = (n-2) * (n-3) / 2 - 数组元素的总和。
  • 如果将 sum 与乘积结合,结果将是唯一的:stackoverflow.com/questions/555744/…
  • 是的 - 它就在我的脑海里,但我没有得到最后一点信息(我一直试图使用 XOR 找出解决方案 - 我仍然认为可能可能比平方和更快)
【解决方案6】:

查看这篇关于该主题的旧但很好的论文:

【讨论】:

  • 在这种情况下,这个解决方案是 O(n*log(n)) (由于数组中只有 2 个重复项)。因此,在这种情况下,它并不比排序好。最好的解决方案应该考虑到重复数为2且所有值都在[0, n-3]范围内。
【解决方案7】:

该问题的一些答案:Algorithm to determine if array contains n…n+m? 包含作为子问题的解决方案,您可以针对您的目的采用这些解决方案。

例如,这是来自my answer的相关部分:

bool has_duplicates(int* a, int m, int n)
{
  /** O(m) in time, O(1) in space (for 'typeof(m) == typeof(*a) == int')

      Whether a[] array has duplicates.

      precondition: all values are in [n, n+m) range.

      feature: It marks visited items using a sign bit.
  */
  assert((INT_MIN - (INT_MIN - 1)) == 1); // check n == INT_MIN
  for (int *p = a; p != &a[m]; ++p) {
    *p -= (n - 1); // [n, n+m) -> [1, m+1)
    assert(*p > 0);
  }

  // determine: are there duplicates
  bool has_dups = false;
  for (int i = 0; i < m; ++i) {
    const int j = abs(a[i]) - 1;
    assert(j >= 0);
    assert(j < m);
    if (a[j] > 0)
      a[j] *= -1; // mark
    else { // already seen
      has_dups = true;
      break;
    }
  }

  // restore the array
  for (int *p = a; p != &a[m]; ++p) {
    if (*p < 0) 
      *p *= -1; // unmark
    // [1, m+1) -> [n, n+m)
    *p += (n - 1);        
  }

  return has_dups; 
}

程序保持数组不变(数组应该是可写的,但它的值会在退出时恢复)。

它适用于最大为 INT_MAX 的数组大小(在 64 位系统上为 9223372036854775807)。

【讨论】:

    【解决方案8】:
    假设数组是
    
    a[0], a[1], a[2] ..... a[n-1]
    
    sumA = a[0] + a[1] +....+a[n-1]
    sumASquare = a[0]*a[0] + a[1]*a[1] + a[2]*a[2] + .... + a[n]*a[n]
    
    sumFirstN = (N*(N+1))/2 其中 N=n-3 所以
    sumFirstN = (n-3)(n-2)/2
    
    相似地
    
    sumFirstNSquare = N*(N+1)*(2*N+1)/6 = (n-3)(n-2)(2n-5)/6
    
    假设重复元素是 = X 和 Y
    
    所以 X + Y = sumA - sumFirstN;
    X*X + Y*Y = sumASquare - sumFirstNSquare;
    
    所以在求解这个二次方程时,我们可以得到 X 和 Y 的值。
    时间复杂度 = O(n)
    空间复杂度 = O(1)
    

    【讨论】:

      【解决方案9】:

      我知道这个问题很老了,但我突然想到了它,我想我有一个有趣的答案。 我们知道这是一个脑筋急转弯和一个琐碎的解决方案(即 HashMap、Sort 等),无论它们有多好都会很无聊。

      由于数字是整数,它们具有恒定的位大小(即 32)。让我们假设我们现在正在使用 4 位整数。我们寻找 AB,它们是重复的数字。

      我们需要 4 个存储桶,每个存储一位。每个桶包含其特定位为 1 的数字。例如桶 1 获取 2、3、4、7、...:

      Bucket 0 : Sum ( x where: x & 2 power 0 == 0 )
      ...
      Bucket i : Sum ( x where: x & 2 power i == 0 )
      

      如果没有重复,我们知道每个桶的总和是多少。我认为这是先验知识。

      一旦生成了上述存储桶,其中一些存储桶的值就会超出预期。通过从桶中构造数字,我们将拥有(A 或 B 供您参考)。

      我们可以计算出(A XOR B)如下:

      A XOR B = Array[i] XOR Array[i-1] XOR ... 0, XOR n-3 XOR n-2  ... XOR 0
      

      现在回到桶,我们确切地知道哪些桶有我们的数字,哪些只有一个(来自 XOR 位)。

      对于只有一个数字的桶,我们可以提取数字 num = (sum - 桶的预期总和)。但是,只有当我们能找到一个重复的数字时,我们才应该是好的,所以如果我们在 A XOR B 中至少有一位,我们就会得到答案。

      但是如果 A XOR B 为零怎么办? 那么这种情况只有在两个重复的数字是相同的数字时才有可能,那么我们的数字就是 A OR B 的答案。

      【讨论】:

        【解决方案10】:

        对数组进行排序似乎是最好的解决方案。然后,一个简单的排序将使搜索变得微不足道,并且将花费更少的时间/空间。

        否则,如果您知道数字的域,请创建一个包含那么多桶的数组,并在遍历数组时递增每个桶。像这样:

        int count [10];
        
        for (int i = 0; i < arraylen; i++) {
            count[array[i]]++;
        }
        

        然后只需在您的数组中搜索任何大于 1 的数字。这些是具有重复项的项目。只需要遍历原始数组和遍历计数数组。

        【讨论】:

        • 我同意这可能只是一个家庭作业问题......但是,如果元素不可比较,这可能是一个真正的问题。
        • 你怎么会有无与伦比的元素?您只需要强制对它们进行一些排序。使用任何相同的标准来推导该排序系统。
        • 鉴于只有两个重复数字的要求,一旦发现重复数字,最好只返回该值。
        • 当然你可以对任何东西强制排序......但是对于尚未预先定义为可比较的元素,你将如何做到这一点?这应该是解决方案的一部分。此外,在实践中很难有足够小的范围以使计数出现可行。
        • @tj 问题指定它们是数字并且它们在 0 到 n-3 的范围内。那将是可比较的并且足够小。
        【解决方案11】:

        这是@eugensk00 的答案(其中一个修订版)在 Python 中的实现,它不使用模运算。这是一个单程算法,O(log(n)) in space。如果使用固定宽度(例如 32 位)整数,则只需要两个固定宽度数字(例如,对于 32 位:一个 64 位数字和一个 128 位数字)。它可以处理任意大整数序列(它一次读取一个整数,因此整个序列不需要在内存中)。

        def two_repeated(iterable):
            s1, s2 = 0, 0
            for i, j in enumerate(iterable):
                s1 += j - i     # number_of_digits(s1) ~ 2 * number_of_digits(i)
                s2 += j*j - i*i # number_of_digits(s2) ~ 4 * number_of_digits(i) 
            s1 += (i - 1) + i
            s2 += (i - 1)**2 + i**2
        
            p = (s1 - int((2*s2 - s1**2)**.5)) // 2 
            # `Decimal().sqrt()` could replace `int()**.5` for really large integers
            # or any function to compute integer square root
            return p, s1 - p
        

        例子:

        >>> two_repeated([2, 3, 6, 1, 5, 4, 0, 3, 5])
        (3, 5)
        

        上述代码的更详细版本如下:

        def two_repeated_seq(arr):
            """Return the only two duplicates from `arr`.
        
            >>> two_repeated_seq([2, 3, 6, 1, 5, 4, 0, 3, 5])
            (3, 5)
            """
            n = len(arr)
            assert all(0 <= i < n - 2 for i in arr) # all in range [0, n-2)
            assert len(set(arr)) == (n - 2) # number of unique items
        
            s1 = (n-2) + (n-1)       # s1 and s2 have ~ 2*(k+1) and 4*(k+1) digits  
            s2 = (n-2)**2 + (n-1)**2 # where k is a number of digits in `max(arr)`
            for i, j in enumerate(arr):
                s1 += j - i     
                s2 += j*j - i*i
        
            """
            s1 = (n-2) + (n-1) + sum(arr) - sum(range(n))
               = sum(arr) - sum(range(n-2))
               = sum(range(n-2)) + p + q - sum(range(n-2))
               = p + q
            """
            assert s1 == (sum(arr) - sum(range(n-2)))
        
            """
            s2 = (n-2)**2 + (n-1)**2 + sum(i*i for i in arr) - sum(i*i for i in range(n))
               = sum(i*i for i in arr) - sum(i*i for i in range(n-2))
               = p*p + q*q
            """
            assert s2 == (sum(i*i for i in arr) - sum(i*i for i in range(n-2)))
        
            """
            s1 = p+q
            -> s1**2 = (p+q)**2
            -> s1**2 = p*p + 2*p*q + q*q
            -> s1**2 - (p*p + q*q) = 2*p*q
            s2 = p*p + q*q
            -> p*q = (s1**2 - s2)/2
        
            Let C = p*q = (s1**2 - s2)/2 and B = p+q = s1 then from Viete theorem follows
            that p and q are roots of x**2 - B*x + C = 0
            -> p = (B + sqrtD) / 2
            -> q = (B - sqrtD) / 2
            where sqrtD = sqrt(B**2 - 4*C)
        
            -> p = (s1 + sqrt(2*s2 - s1**2))/2
            """
            sqrtD = (2*s2 - s1**2)**.5
            assert int(sqrtD)**2 == (2*s2 - s1**2) # perfect square
            sqrtD = int(sqrtD)
            assert (s1 - sqrtD) % 2 == 0 # even
            p = (s1 - sqrtD) // 2
            q = s1 - p
            assert q == ((s1 + sqrtD) // 2)
            assert sqrtD == (q - p)
            return p, q
        

        注意:计算整数平方根 (~ N**4) 会使上述算法成为非线性算法。

        【讨论】:

          【解决方案12】:

          由于指定了范围,您可以执行基数排序。这将在 O(n) 中对您的数组进行排序。然后在排序数组中搜索重复项是 O(n)

          【讨论】:

            【解决方案13】:

            你可以使用简单的嵌套for循环

             int[] numArray = new int[] { 1, 2, 3, 4, 5, 7, 8, 3, 7 };
            
                    for (int i = 0; i < numArray.Length; i++)
                    {
                        for (int j = i + 1; j < numArray.Length; j++)
                        {
                            if (numArray[i] == numArray[j])
                            {
                               //DO SOMETHING
                            }
                        }
            

            *或者如果你想获得出现次数,你可以过滤数组并使用递归函数*

            int[] array = { 1, 2, 3, 4, 5, 4, 4, 1, 8, 9, 23, 4, 6, 8, 9, 1,4 };
            int[] myNewArray = null;
            int a = 1;
            
             void GetDuplicates(int[] array)
                for (int i = 0; i < array.Length; i++)
                        {
                            for (int j = i + 1; j < array.Length; j++)
                            {
                                if (array[i] == array[j])
                                {
                                      a += 1;
                                }
                            }
                            Console.WriteLine(" {0} occurred {1} time/s", array[i], a);
            
                            IEnumerable<int> num = from n in array where n != array[i] select n;
                             myNewArray = null;
                             a = 1;
                             myNewArray = num.ToArray() ;
            
                             break;
            
                        }
                         GetDuplicates(myNewArray);
            

            【讨论】:

              【解决方案14】:

              回答 18.. 你正在使用一个 9 的数组,元素从 0 开始。所以 max ele 在你的数组中将是 6。对 0 到 6 的元素求和,然后对数组元素求和。计算它们的差异(例如 d)。这是 p + q。现在对 0 到 6 的元素进行异或运算(比如 x1)。现在对数组元素进行异或(比如 x2)。 x2 是从 0 到 6 的所有元素的 XOR,除了两个重复元素,因为它们相互抵消。现在对于 i = 0 到 6,对于数组的每个 ele,说 p 是那个 ele a[i] 所以你可以通过从 d 中减去这个 ele 来计算 q。对 p 和 q 进行 XOR 并用 x2 对它们进行 XOR 并检查 x1==x2。同样对所有元素执行此操作,您将获得满足此条件的元素,并且您在 O(n) 中完成。继续编码!

              【讨论】:

                【解决方案15】:

                看看这个... O(n) 时间和 O(1) 空间复杂度

                 for(i=0;i< n;i++)
                 xor=xor^arr[i]
                 for(i=1;i<=n-3;i++)
                 xor=xor^i;
                

                所以在给定的例子中,你将得到 3 和 5 的异或

                xor=xor & -xor  //Isolate the last digit
                
                for(i = 0; i < n; i++)
                {
                if(arr[i] & xor)
                  x = x ^ arr[i]; 
                else
                  y = y ^ arr[i]; 
                }
                for(i = 1; i <= n-3; i++)
                {
                if(i & xor)
                  x = x ^ i; 
                else
                  y = y ^ i; 
                

                }

                x 和 y 是你的答案

                【讨论】:

                  【解决方案16】:

                  对于每个数字:检查它是否存在于数组的其余部分中。

                  【讨论】:

                  • 运行时间会很可怕。 O(N^2)。可能需要更好的解决方案。
                  • 确实如此。这比排序效率低。
                  • 而“最佳方式”可能与人们想象的不同:)
                  • 有趣的是,这么多人认为性能是唯一的质量指标 - 一个人快速理解算法的能力如何,而不是浪费你的大脑周期来探索一些简单问题的局部优化?
                  • 我们怎么知道性能是预期的指标?这是迄今为止最简单的代码。如果数组中有 1000 个条目,这很容易成为我书中最好的代码。在您进行分析之前不要进行优化。简而言之,我认为这个答案是最好的起点,除非我们知道我们在优化什么。
                  【解决方案17】:

                  如果不进行排序,您将可以跟踪您已经访问过的数字。

                  在伪代码中,这基本上是(这样做,所以我不只是给你答案):

                  for each number in the list
                     if number not already in unique numbers list
                        add it to the unique numbers list
                     else
                        return that number as it is a duplicate
                     end if
                  end for each
                  

                  【讨论】:

                  • 这运行时间不是还是O(N^2)吗?唯一号码列表的长度将增长到接近数组的长度,并且必须搜索每个号码。我想它可能是一个排序列表,所以 O(N * LogN) 可能是可能的。
                  • 如果用哈希表或集合替换列表,比O(n^2)快。
                  • 嗯,最好的方法是先对列表进行排序......但由于这不是一个选项,我们只想找到第一个重复项并将其返回。在最好的情况下,我们会很快返回……更坏的情况是,我们会在检查完整个列表后返回。我不确定如何避免这种情况
                  • 如果你为“唯一编号列表”使用适当的存储,就像一棵树,那么这也是 O(N log N)。如果您花费更多内存并使用例如一个 bool[] 表示看到的状态,它甚至在时间上变成 O(N) 而在空间上变成 O(MAX_NUMBER)。
                  • @tjdonaldson 是的,如果我用 C# 编写它,我肯定会把它放在哈希表中。但是由于没有指定语言,所以我用伪代码编写了它……这样,如果他用 Basic 或 Assembler 等编写,他就能弄清楚……
                  【解决方案18】:

                  这个怎么样:

                  for (i=0; i<n-1; i++) {
                    for (j=i+1; j<n; j++) {
                      if (a[i] == a[j]) {
                          printf("%d appears more than once\n",a[i]);
                          break;
                      }
                    }
                  }
                  

                  当然它不是最快的,但它简单易懂,并且需要 没有额外的内存。如果 n 是像 9 或 100 这样的小数,那么它很可能是“最好的”。 (即“最佳”可能意味着不同的东西:执行速度最快、内存占用最少、可维护性最强、开发成本最低等。)

                  【讨论】:

                  • 这个解决方案是 O(n**2)。标准的sort 例程通常是 O(n*log(n)),开发和维护不需要任何成本。
                  【解决方案19】:

                  在c中:

                      int arr[] = {2, 3, 6, 1, 5, 4, 0, 3, 5};
                  
                      int num = 0, i;
                  
                      for (i=0; i < 8; i++)
                           num = num ^ arr[i] ^i;
                  

                  由于x^x=0,重复奇数次的数字被中和。让我们将唯一的数字称为 a 和 b。剩下的是 a^b。我们知道a^b != 0,因为a != b。选择a^b 的任何1 位,并将其用作掩码,即选择x 作为2 的幂,这样x &amp; (a^b) 就不是零。

                  现在将列表分成两个子列表——一个子列表包含所有带有y&amp;x == 0 的数字y,其余的进入另一个子列表。通过我们选择 x 的方式,我们知道 a 和 b 的对在不同的桶中。所以我们现在可以将上面使用的相同方法分别应用于每个桶,并找出 a 和 b 是什么。

                  【讨论】:

                    【解决方案20】:

                    我写了一个小程序来找出不重复的元素的数量,请通过这个让我知道你的意见,目前我假设偶数元素是偶数,但也可以很容易地扩展到奇数。

                    所以我的想法是先对数字进行排序,然后应用我的算法。可以使用快速排序来对这些元素进行排序。

                    让我们采用如下的输入数组

                    int arr[] = {1,1,2,10,3,3,4,5,5,6,6};
                    

                    数字2,10和4不重复,但它们是按排序顺序排列的,如果没有排序使用快速排序先排序。

                    让我们在此应用我的程序

                    using namespace std;
                    
                    main()
                    {
                        //int arr[] = {2, 9, 6, 1, 1, 4, 2, 3, 5};
                        int arr[] = {1,1,2,10,3,3,4,5,5,6,6};
                    
                        int i = 0;
                    
                        vector<int> vec;
                    
                        int var = arr[0];
                        for(i = 1 ; i < sizeof(arr)/sizeof(arr[0]); i += 2)
                        {
                                var = var ^ arr[i];
                    
                                if(var != 0 )
                                {
                                    //put in vector
                                    var = arr[i-1];
                                    vec.push_back(var);
                                    i = i-1;
                                }
                                var = arr[i+1];
                        }
                    
                        for(int i = 0 ; i < vec.size() ; i++)
                            printf("value not repeated = %d\n",vec[i]);
                    
                    }
                    

                    这给出了输出:

                    value not repeated= 2
                    
                    value not repeated= 10
                    
                    value not repeated= 4
                    

                    它简单明了,只需使用 XOR man。

                    【讨论】:

                      【解决方案21】:
                      for(i=1;i<=n;i++) {
                        if(!(arr[i] ^ arr[i+1]))
                              printf("Found Repeated number %5d",arr[i]);
                      }
                      

                      【讨论】:

                        【解决方案22】:

                        这是一个使用订单统计并在O(n) 中运行的算法。

                        您可以通过重复调用SELECT 以中值作为参数来解决此问题。

                        您还依赖于在致电SELECT 之后, 小于或等于中位数的元素被移到中位数的左边。

                        • A 上调用SELECT,以中值作为参数。
                        • 如果中值是floor(n/2),则重复值正好是中值。因此,您继续使用数组的右半部分。
                        • 否则,如果不是这样,则将重复值留给中位数。因此,您继续使用数组的左半部分。
                        • 您以递归方式继续这种方式。

                        例如:

                        • A={2, 3, 6, 1, 5, 4, 0, 3, 5}n=9时,中位数应该是4的值。
                        • 第一次拨打SELECT之后
                        • A={3, 2, 0, 1, &lt;3&gt;, 4, 5, 6, 5} 中值小于4 所以我们继续左半边。
                        • A={3, 2, 0, 1, 3}
                        • 第二次致电SELECT
                        • A={1, 0, &lt;2&gt;, 3, 3} 那么中位数应该是 2,所以我们继续右半部分。
                        • A={3, 3},找到了。

                        此算法在O(n+n/2+n/4+...)=O(n) 中运行。

                        【讨论】:

                          【解决方案23】:

                          使用https://en.wikipedia.org/wiki/HyperLogLog 怎么样?

                          Redis 做http://redis.io/topics/data-types-intro#hyperloglogs

                          HyperLogLog 是一种概率数据结构,用于对独特事物进行计数(从技术上讲,这指的是估计集合的基数)。通常计算唯一项目需要使用与您要计算的项目数量成比例的内存量,因为您需要记住过去已经看到的元素以避免多次计算它们。但是,有一组算法可以用内存换取精度:在 Redis 实现的情况下,您会以标准误差的估计度量结束,该误差小于 1%。该算法的神奇之处在于您不再需要使用与计数的项目数成正比的内存量,而是可以使用恒定量的内存!在最坏的情况下为 12k 字节,或者如果您的 HyperLogLog(我们从现在开始将它们称为 HLL)看到的元素很少,则更少。

                          【讨论】:

                            【解决方案24】:

                            使用嵌套的 for 循环并假设问题是查找数组中仅出现两次的数字。

                            def repeated(ar,n):
                                count=0
                                for i in range(n):
                                    for j in range(i+1,n):
                                        if ar[i] == ar[j]:
                                            count+=1
                                    if count == 1:
                                        count=0
                                        print("repeated:",ar[i])    
                            
                            arr= [2, 3, 6, 1, 5, 4, 0, 3, 5]
                            n = len(arr)
                            repeated(arr,n)
                            

                            【讨论】:

                              【解决方案25】:

                              我们为什么要尝试做数学(特别是求解二次方程)这些都是昂贵的操作。解决这个问题的最佳方法是构造一个大小为 (n-3) bits 的位图,即 (n -3 ) +7 / 8 bytes 。最好为这个内存做一个 calloc,这样每一位都将被初始化为 0。然后遍历列表并在遇到时将特定位设置为 1,如果该位已经设置为 1,则表示 no ,那么这就是重复的 no 。 这可以扩展以找出数组中是否缺少任何 no。 这个解的时间复杂度是 O(n)

                              【讨论】:

                                猜你喜欢
                                • 1970-01-01
                                • 1970-01-01
                                • 1970-01-01
                                • 1970-01-01
                                • 1970-01-01
                                • 1970-01-01
                                • 1970-01-01
                                • 1970-01-01
                                • 2022-01-03
                                相关资源
                                最近更新 更多