Find the element that appears once(查找出现一次的元素)

给定一个数组, 每个元素出现三次, 除了一个坏掉的元素, 这个元素只出现了一次。 问题来了, 如何查找这个出现一次的元素。 期望的时间复杂度为O(n),  辅助的空间复杂度为O(1)。 

例子:

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

Output: 2
我们可以花费O(n logn)的排序算法(例如heap sort, merge sort, quick sort(期望时间复杂度))对这些数进行排序, 然后用一个temp进行遍历。  我们也可以使用hashing的办法, 使用hashing的可能需要超过O(n)的时间复杂度, 而且更加不幸的是, hashing 需要更多的extra space(当数据大小为几亿的时候, 我们无法承受使用那么多的额外的空间复杂度 闭嘴)。

解决的idea就是使用bitwise的operator去解决。 这仅仅需要花费O(n)的时间复杂度, 而且仅仅使用O(1)的extra space。 但是, 显然不同于基于XOR的解决办法。 因为所有重复的元素仅仅appear odd number of times(这里为3)。 设想如果出现的是偶数次的话, 我们当然可以采用XOR的办法。 因为X XOR X = 0,  而且X XOR X XOR Y = Y, 所以从前到后进行相邻的去XOR遍历, 最终剩下的数字就是这个只出现一次的数据了。 但是是出现的是奇数次啊, 当然不能使用异或解决啦。

怎么办!!!

我们可以用一个loop去遍历掉array的所有的elements, 最每一次的迭代的时候, 我们记录如下两个值:

ones: the bits that have appeared 1st time or 4th time, or 7th time, ... etc(为等差为3的等差数列)

twos: the bits that have  appeared 2nd time or 5th time, or 8th time.... etc(亦为等差为3的等差数列)

最终我们返回the values of 'ones'。

那么如何维护ones 和 twos的数值呢?

首先, loop 开始之前, 我们吧ones 和 twos 初始化为0.  对于数组中的每一个元素, 找出 找出当前新的元素和ones的值以及twos的值 的common set bits(即两个数子中均为1的bits). 这些common set bits 其实是应该加到 twos 值中去的。 所以我们只需要对twos 以及common set bits 这两个使用bitwise or。 twos 会得到一些额外的bits, 这些bits 出现了三次了。  这些额外的bits将会在后面被移除掉(即出现三次的)。

通过使用把ones XOR 下一个新的元素去更新ones, 我们, 将找到可能出现了第三次的bits, 这些额外的bits将会在后面被移除掉。 

最后, ones 和twos 均包含额外的bits, 这些bits 都是出现了第三次的(即出现了三次)。 通过找出ones和twos之中均包含的的common set bits, 去移除掉这些额外的bits。  

程序如下:

#include <stdio.h>

int getSingle(int arr[], int n)
{
    int ones = 0, twos = 0 ;

    int common_bit_mask;

    // Let us take the example of {3, 3, 2, 3} to understand this
    for( int i=0; i< n; i++ )
    {
        /* The expression "one & arr[i]" gives the bits that are
           there in both 'ones' and new element from arr[].  We
           add these bits to 'twos' using bitwise OR

           Value of 'twos' will be set as 0, 3, 3 and 1 after 1st,
           2nd, 3rd and 4th iterations respectively */
        twos  = twos | (ones & arr[i]);


        /* XOR the new bits with previous 'ones' to get all bits
           appearing odd number of times

           Value of 'ones' will be set as 3, 0, 2 and 3 after 1st,
           2nd, 3rd and 4th iterations respectively */
        ones  = ones ^ arr[i];


        /* The common bits are those bits which appear third time
           So these bits should not be there in both 'ones' and 'twos'.
           common_bit_mask contains all these bits as 0, so that the bits can
           be removed from 'ones' and 'twos'

           Value of 'common_bit_mask' will be set as 00, 00, 01 and 10
           after 1st, 2nd, 3rd and 4th iterations respectively */
        common_bit_mask = ~(ones & twos);


        /* Remove common bits (the bits that appear third time) from 'ones'

           Value of 'ones' will be set as 3, 0, 0 and 2 after 1st,
           2nd, 3rd and 4th iterations respectively */
        ones &= common_bit_mask;


        /* Remove common bits (the bits that appear third time) from 'twos'

           Value of 'twos' will be set as 0, 3, 1 and 0 after 1st,
           2nd, 3rd and 4th itearations respectively */
        twos &= common_bit_mask;

        // uncomment this code to see intermediate values
        //printf (" %d %d \n", ones, twos);
    }

    return ones;
}

int main()
{
    int arr[] = {3, 3, 2, 3};
    int n = sizeof(arr) / sizeof(arr[0]);
    printf("The element with single occurrence is %d ",
            getSingle(arr, n));
    return 0;
}

办法二(没有第一个高效, 但是好理解):

Following is another O(n) time complexity and O(1) extra space method suggested by aj. We can sum the bits in same positions for all the numbers and take modulo with 3. The bits for which sum is not multiple of 3, are the bits of number with single occurrence.
Let us consider the example array {5, 5, 5, 8}. The 101, 101, 101, 1000
Sum of first bits%3 = (1 + 1 + 1 + 0)%3 = 0;
Sum of second bits%3 = (0 + 0 + 0 + 0)%0 = 0;
Sum of third bits%3 = (1 + 1 + 1 + 0)%3 = 0;
Sum of fourth bits%3 = (1)%3 = 1;
Hence number which appears once is 1000

#include <stdio.h>
#define INT_SIZE 32
 
int getSingle(int arr[], int n)
{
    // Initialize result
    int result = 0;
 
    int x, sum;
 
    // Iterate through every bit
    for (int i = 0; i < INT_SIZE; i++)
    {
      // Find sum of set bits at ith position in all
      // array elements
      sum = 0;
      x = (1 << i);
      for (int j=0; j< n; j++ )
      {
          if (arr[j] & x)
            sum++;
      }
 
      // The bits with sum not multiple of 3, are the
      // bits of element with single occurrence.
      if (sum % 3)
        result |= x;
    }
 
    return result;
}
 
// Driver program to test above function
int main()
{
    int arr[] = {12, 1, 12, 3, 12, 1, 1, 2, 3, 2, 2, 3, 7};
    int n = sizeof(arr) / sizeof(arr[0]);
    printf("The element with single occurrence is %d ",
            getSingle(arr, n));
    return 0;
}
7



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值