【问题标题】:Find the element that appears once查找出现一次的元素
【发布时间】:2012-12-31 10:03:50
【问题描述】:

找到出现一次的元素

给定一个数组,其中每个元素出现 3 次,除了一个元素只出现一次。找到出现一次的元素。

预计时间复杂度为 O(n) 和 O(1) 额外空间。

例子:

输入:arr[] = {12, 1, 12, 3, 12, 1, 1, 2, 3, 3}

输出:2

【问题讨论】:

  • 提示:求和...

标签: algorithm


【解决方案1】:

如果不存在 O(1) 空间限制,您可能已经使用值作为出现次数的哈希图。

int getUniqueElement(int[] arr)
{
    int ones = 0 ; //At any point of time, this variable holds XOR of all the elements which have appeared "only" once.
    int twos = 0 ; //At any point of time, this variable holds XOR of all the elements which have appeared "only" twice.
    int not_threes ;

    for( int x : arr )
    {
        twos |= ones & x ; //add it to twos if it exists in ones
        ones ^= x ; //if it exists in ones, remove it, otherwise, add it

        // Next 3 lines of code just converts the common 1's between "ones" and "twos" to zero.

        not_threes = ~(ones & twos) ;//if x is in ones and twos, dont add it to Threes.
        ones &= not_threes ;//remove x from ones
        twos &= not_threes ;//remove x from twos
    } 
    return ones;
}

基本上,它利用了x^x = 00^x=x 的事实。所以所有成对的元素都会被异或并消失,留下孤独的元素。

简而言之:

如果已经有一个位,则将其添加到两个中。

如果它不存在,XOR 会将这个位添加到一个,如果它已经存在,则从一个中删除这个位。

如果一个位在一个和两个中,从一个和两个中删除它。

完成后,ones 包含仅出现 3*n+1 次的位,即仅出现一次的元素的位。

【讨论】:

  • 这没有给出正确的输出,用 {1,3,1} 试试吧
  • 我认为对数组中的所有元素做一个 NOR 是可行的。一个元素自身三次的 NOR 给出 0。单个元素的 NOR 为零只会翻转位,因此在顶部进行另一个否定将使元素出现一次。如有错误请指正。
  • @AnkitZalani,您的示例无效。一个元素应该出现一次,而其他元素应该恰好出现 3 次。
【解决方案2】:

JavaScript 解决方案:

function findElement(arr) {
  var ones = 0;
  var twos = 0;
  for (var i = 0; i < arr.length; i++) {
    ones = (ones ^ arr[i]) & ~twos;
    twos = (twos ^ arr[i]) & ~ones;
  }
  return ones;
}

【讨论】:

    【解决方案3】:

    here 所述,可以在最坏情况 O(n) 时间和 O(1) 额外空间中找到数组元素的中值。所以我们可以用分治法来解决这个问题:

    在 O(n) 时间内找到中位数并计算 O(n) 中小于或等于中位数的元素的数量。如果这个数字是 3k+1,则表示答案小于或等于中位数,所以在 O(n) 中省略大于中位数的元素。否则,省略那些小于或等于中位数的。然后递归地在 T(n/2) 的剩余元素中找到答案。注意:剩余元素的数量是 n/2,因为我们省略了一半的元素。

    所以 T(n) = T(n/2)+O(n) = O(n) 并且我们需要 O(1) 额外空间。

    【讨论】:

    • 你能用上面的例子证明它有效吗?谢谢
    【解决方案4】:
    // as we know every number is present thrice except one number means   that if we observe the bits at each place we will find that the set bits at each place are either multiple of 3n or 3n+1 ( when unique num has set bit at that place ) 
    

    例如 - 12、1、12、3、12、1、1、2、3、3

    12- 1 1 0 0
    1 - 0 0 0 1
    12- 1 1 0 0
    3 - 0 0 1 1
    12- 1 1 0 0
    1 - 0 0 0 1
    1 - 0 0 0 1
    2 - 0 0 1 0
    3 - 0 0 1 1
    3 - 0 0 1 1
    

        3 3 4 6    ( 3n or 3n+1 ) form 
    

    它是 3n+1 形式的地方意味着唯一编号在这里设置了位,所以现在我们很容易提取唯一编号,因为我们知道它的设置位位置。

    #include<iostream>
    
    using namespace std;
    
    int main() {
       
       int n,num,j;
       int set[64]={0}; // maximum bits
       cin>>n;
       for(int i=0;i<n;i++){
           cin>>num;
           j=0;
    
           while(num){
               set[j]+=(num&1);
               num=num>>1;
               j++;
           }
       }
    int ans=0,p=1;
    for(int i=0;i<n;i++){
    
        ans+=(set[i]%3)*p;
        p=p*2;
    }
    cout<<ans<<endl;
    
    }
    

    【讨论】:

    • edit您的帖子解释此代码如何回答问题。
    【解决方案5】:

    我提出了一个类似于 mhsekhavat 提出的解决方案。我建议使用荷兰国旗问题http://en.wikipedia.org/wiki/Dutch_national_flag_problem 的分区算法而不是确定中位数(是的,我是荷兰人,受过 Dijkstra 风格的教育)。

    应用该算法的结果是一个被划分为红色、白色和蓝色部分的数组。白色部分可以被认为是枢轴。请注意,白色部分由等于枢轴的所有元素组成,因此白色部分将包含 1 或 3 个元素。 红色部分由小于枢轴的元素组成,蓝色部分由大于枢轴的元素组成。 (注意红蓝部分没有排序!)

    接下来,计算红色、白色和蓝色部分的元素个数。如果任何部分由 1 个元素组成,那么这就是您要查找的数字。否则,对于给定数量的 k,红色或蓝色部分由 3k+1 个元素组成。对包含 3k+1 个元素的部分重复该算法。最终其中一个部件的尺寸为 1。

    算法在 O(n) 中运行,需要 O(1) 个变量。

    【讨论】:

      【解决方案6】:

      虽然问题已经得到解答,但我发现以下内容更直观,因此将其添加为答案。原文来自here

      int singleNumber(vector<int>& nums) {
          /*Bits in 'ones' are set to 1, when that particular bit 
            occurs for the first time. Bits in 'twos' are set to 1, 
            when that particular bit occurs for the second time. If a 
            bit is occurring for more than 2 times, we throw it away 
            and start counting again.*/
          int ones = 0;
          int twos = 0;
          for (auto elem : nums) {
              int onesOld = ones;
              /* (ones ^ elem): consider the ith bit in 'ones' be 0 (i.e. ith 
                 bit has not occurred till now or has occurred 2 times) and in 
                 'elem' be 1. So, for the ith bit (ones ^ elem) gives 1. Now,
                 ith bit could have occurred even number of times as well (i.e. 
                 ith bit in twos is set to 1). If that was the case, we 
                 would like to ignore such bit. This last part is taken care 
                 of by '&' with '~twos' 
                 */
              ones = (ones ^ elem) & ~twos;
              /* (onesOld & elem) gives the bits which have occurred ones 
                 and also occur in this particular element. (twos & ~elem) 
                 gives the bits that have occurred twice and do not occur 
                 in this element. Both these cases take care of the bits 
                 that have occurred 2 times (although a bit might be set 
                 more than 2 times, like 5,7... but we take only modulo 3 
                 count).
              */
              twos = (onesOld & elem) | (twos & ~elem);
          }
          return ones;
      }
      

      【讨论】:

        【解决方案7】:

        考虑它们的二进制表示并将每个位置的位数相加。例如 [ 1, 1, 1, 2, 2, 2, 3] 给出 [4, 4],只考虑表示这些数字所需的两位。取其中的 mod 3 给出 [1,1] ,它是二进制的 11 = 3,这是正确的答案。这仍然是 O(1) 空间,因为它不随元素数量而缩放,但仍然可能有一个巨大的前置因子。

        一些草率的cpp代码:

        int l = arr.size();
        int nbits = 32;
        vector<int> bits(nbits,0);
        
        for( int i = 0; i < l; i++ ){
            for( int j = 0; j < nbits; j++ ){
                bits[j] +=  ( A[i] >>j ) & 1;
            }
        }
        int missing = 0;
        for( int j = 0 ; j < nbits; j++ ){
            if( bits[j]%3 == 1 ){
                 set_bit( missing, j );
            }
        }
        

        几乎可以肯定,这可以由比我更了解按位运算的人进行优化(没有人喜欢循环中的循环......)。无论如何,我认为这应该适用于任何“k-duplicates 除了其中一个”类型的问题,假设丢失的只出现一次。

        编辑:似乎这个答案也由 Leo Polovets 详细介绍 https://www.quora.com/Given-an-integer-array-such-that-every-element-occurs-3-times-except-one-element-which-occurs-only-once-how-do-I-find-that-single-element-in-O-1-space-and-O-n-time-complexity

        【讨论】:

          【解决方案8】:

          最好的解决方案是使用 XOR。所有数组元素的 XOR 为我们提供了一次出现的数字。这个想法是基于以下两个事实。 a) 一个数与自身的 XOR 为 0。 b) 数字与 0 的异或就是数字本身。

          int findSingle(int ar[], int ar_size) 
              { 
                  // Do XOR of all elements and return 
                  int res = ar[0]; 
                  for (int i = 1; i < ar_size; i++) 
                      res = res ^ ar[i]; 
          
                  return res; 
              } 
          
          

          【讨论】:

          • 异或一个出现三次的元素将返回那个元素,就像它出现一次一样:12^12^12=12。所以这不会区分出现一次或三次的元素,因为 XORing 将返回所有出现奇数的元素!
          • 请正确阅读问题。您提出的解决方案适用于除一个之外的所有元素都存在两次而不是三次的情况
          【解决方案9】:

          将每个数字相加一次并将总和乘以 3,我们将得到数组中每个元素的总和的三次。将其存储为 thrice_sum。从 thrice_sum 中减去整个数组的总和,然后将结果除以 2。我们得到的数字就是所需的数字(在数组中出现一次)。

          def singlenumbers(arr):
             return (3* sum(set(arr)) - sum(arr))//2
          arr =  [6, 1, 3, 3, 3, 6, 6]
          print(singlenumbers(arr))
          

          【讨论】:

          • 由于你使用了一个集合,它不会像原始问题中预期的那样有 O(1) 空间。
          【解决方案10】:
          def apperasonce(arr1):
              n=len(arr1)
              for i in range(0,n):
                  if arr1.count(arr1[i]) == 1:
                      return arr1[i]
          
          arr1=[12,1,12,3,12,1,1,2,3,3]
          a=apperasonce(arr1)
          print(a)
          

          【讨论】:

          • 这是 O(n^2),而不是 O(n)。 count 内部有一个循环覆盖所有 n
          猜你喜欢
          • 2017-09-13
          • 2021-11-15
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2020-10-09
          相关资源
          最近更新 更多