找到出现一次的元素

时间:2012-12-31 10:03:50

标签: algorithm

找到出现一次的元素

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

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

示例:

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

输出:2

10 个答案:

答案 0 :(得分:6)

如果没有O(1)空间约束,你可能已经找到了一个值为出现次数的hashmap。

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会将此位添加到1中,或者如果它已经存在,则将其从中删除。

如果有一位和两位,请将它从1和2中删除。

完成后,其中包含仅出现3 * n + 1次的位,这是仅出现一次的元素的位。

答案 1 :(得分:2)

如上所述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)额外的空间。

答案 2 :(得分: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 :(得分:1)

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

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

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

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

答案 4 :(得分:0)

虽然问题已经得到解答,但我发现以下更直观,因此将其作为答案添加。最初来自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;
}

答案 5 :(得分:0)

考虑它们的二进制表示形式,并求和每个位置的位数。例如,[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重复,除了其中之一”类型的问题,假设丢失的重复仅出现一次。

编辑: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

答案 6 :(得分:0)

将每个数字相加一次,然后将总和乘以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))

答案 7 :(得分:0)

最好的解决方案是使用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; 
    } 

答案 8 :(得分:0)

// 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;

}

答案 9 :(得分:-1)

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)
相关问题