给定一个数组, 每个元素出现三次, 除了一个坏掉的元素, 这个元素只出现了一次。 问题来了, 如何查找这个出现一次的元素。 期望的时间复杂度为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