【问题标题】:Using a hash to find one duplicated and one missing number in an array使用哈希在数组中查找一个重复的数字和一个缺失的数字
【发布时间】:2014-03-26 19:18:46
【问题描述】:

我在一次采访中遇到了这个问题,很想知道它会如何实施。

给定一个从 0 到 x 的未排序整数数组。一个号码丢失,一个号码重复。找出那些数字。

这是我想出的:

int counts[x+1];

for(int i =0;i<=x; i++){
    counts[a[i]]++;
    if(counts[a[i]] == 2)
        cout<<”Duplicate element: “<<a[i];  //I realized I could find this here
}

for(int j=0; j<=x; j++){
    if(counts[j] == 0)
        cout<<”Missing element: “<<j; 
    //if(counts[j] == 2)
    //  cout<<”Duplicate element: “<<j;   //No longer needed here.
}

我最初的解决方案是创建另一个大小为 x+1 的数组,循环遍历给定的数组并在给定数组的值处索引到我的数组并递增。如果在增量之后我的数组中的任何值都是 2,那就是重复的。但是,我不得不再次循环遍历我的数组,以找到缺失数字为 0 的任何值。

我指出这可能不是最省时的解决方案,但当我被问到时,我不确定如何加快速度。我意识到我可以将查找副本移到第一个循环中,但这对丢失的数字没有帮助。在胡扯了一会儿之后,面试官终于给了我一个想法,即哈希将是一个更好/更快的解决方案。我没有太多使用哈希,所以我不确定如何实现它。有人可以启发我吗?另外,请随时指出我的代码中的任何其他明显错误...提前致谢!

【问题讨论】:

  • 是否允许对数组进行排序很重要?
  • @VladfromMoscow 面试官没有明确禁止对数组进行排序。然而,唯一被问到的是找到重复和缺失的号码。我只是在寻找一种能够以尽可能高效的方式产生这两个值的解决方案,因此没有想到对数组进行排序,因为排序通常是 O(nlogn) 时间,而我的解决方案实际上是 O(2n)。此外,以下 Scott Hunter 的解决方案找到了 O(n) 时间。
  • 哈希在这种情况下没有帮助。我发布了一个解释原因的答案。

标签: c++ arrays hash


【解决方案1】:

如果值的范围与数组中值的数量大致相同或小,那么使用哈希表将无济于事。在这种情况下,大小为 x+1 的数组中有 x+1 个可能的值(一个缺失,一个重复),因此不需要哈希表,只需一个您已经编码的直方图。

如果将分配更改为在大小为 100 万的数组中查找重复的 32 位值,则第二个数组(直方图)可能需要 2^32 = 40 亿个计数长。这时候哈希表会有所帮助,因为哈希表大小是数组大小的函数,而不是值的范围。大小为 1.5 到 200 万的哈希表就足够大了。在这种情况下,您将有 2^32 - 2^20 = 4293918720 个“缺失”值,因此这部分作业会消失。

关于哈希表的维基文章:

Hash Table

【讨论】:

    【解决方案2】:

    如果 x 足够小(可以表示 0..x 的总和),您可以计算 a 中唯一值的总和,然后从 0..x 的总和中减去它,得到缺少值,不需要第二个循环。

    【讨论】:

    • 但是你也有一个副本。因此,如果您只有差异,您不知道哪个值丢失,哪个重复,或者我错过了什么?
    • 好点。当我第一次想到解决方案时,我提到我们可以计算总和,但无法找到这两个数字,因为这可能是任意数量的组合。一旦我将重复项的发现带入第一个循环,你是对的,我可以计算总和以找到缺失值,而无需第二个循环。但是,我仍然对面试官提到的哈希实现感兴趣。感谢您的回答!
    • 是的@Mene,我最初想通过计算总和来解决这个问题,但遇到了你的逻辑并决定反对它。但是,由于我可以在第一个循环中找到重复项,因此我们可以使用总和来查找缺失值,因此只需要一个循环。但是...我仍然希望看到一个涉及哈希的解决方案。
    • 啊,我明白你的意思了。不错的解决方案!
    • 鉴于面试官给我的问题描述,我认为这将是最好的解决方案。关于这个问题的其他答案让我确信哈希在这种情况下没有用,因为数组的大小等于范围。谢谢@Scott 的回答!
    【解决方案3】:

    这是一个使用索引的解决方案的尝试(当数组保证只包含整数时,真正的键值散列没有意义)。抱歉,OP,它在 Ruby 中:

    values = mystery_array.sort.map.with_index { |n,i| n if n != i }.compact
    
    missing_value,duplicate_value = mystery_array.include?(values[0] - 1) ? \
        [values[-1] + 1, values[0]] : [values[0] - 1, values[-1]]
    

    所使用的函数可能在幕后使用了大量的循环,这将创建一个(可能非常大)变量values,其中包含缺失值和/或重复值之间的范围,以及第二个查找循环,但它有效。

    也许面试官的意思是说 Set 而不是 hash?

    【讨论】:

      【解决方案4】:

      允许排序?

      auto first = std::begin(a);
      auto last = std::end(a);
      
      // sort it
      std::sort( first, last );
      
      // find duplicates
      auto first_duplicate = *std::adjacent_find( first, last );
      
      // find missing value
      auto missing = std::adjacent_find(first, last, [](int x, int y) {return x+2 == y;});
      int missing_number = 0;
      if (missing != last)
      {
          missing_number = 1+ *missing;
      }
      else
      {
          if (counts[0] != 0)
          {
              missing_number = 0;
          }
          else
          {
              missing_number = 9;
          }
      }
      

      两者都可以在一个手写循环中完成,但我只想使用 stl 算法。处理极端情况有更好的主意吗?

      【讨论】:

      • 很遗憾,排序是O(n log n),所以会比原来的2个循环慢。
      • 排序可以用基数排序在线性时间内完成。但我更感兴趣的是一种使用标准算法且无需手动循环的方法。
      【解决方案5】:
      for (i=0 to length) { // first loop
       for( j=0 to length ){ // second loop
            if (t[i]==j+1) {
                 if (counter==0){//make sure duplicated number has not been found already
                     for(  k=i+1 to length ) { //search for duplicated number
                         if(t[k]==j+1){
                              j+1 is the duplicated number ;
                              if(missingIsFound)
                                     exit // exit program, missing and dup are found 
                              counter=1 ;
                              }//end if t[k]..
                       }//end loop for duplicated number
                       } // end condition to search
                continue ; // continue to first loop   
            }
       else{
             j+1 is the missing number ;
             if(duplicatedIsFound)
                        exit // exit program, missing and dup are found 
             continue ; //continue to first loop
      
       }//end second loop
      } //end first loop
      

      【讨论】:

      • 这将是 O(n^2),比原来慢得多。
      • 你是怎么知道的?请解释一下。
      • I 和 j 最多可以取 n 个不同的值,您可以遍历它们的每一个可能配对:经典 O(n^2)
      • 没有必要,因为一旦找到元素,两个循环就结束了。我已经使用条件优化了代码,以便退出找到两个元素的循环。
      • 但是如果要找到的元素在最后,你仍然会遍历所有内容。 O() 不是关于最好的情况,而是关于最坏的情况。
      猜你喜欢
      • 1970-01-01
      • 2015-07-20
      • 2023-04-11
      • 2023-03-10
      • 2021-08-25
      • 2018-07-27
      • 2013-11-30
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多