需求
给定一个整数数组 nums,其中恰好有两个元素只出现一次,其余所有元素均出现两次。 找出只出现一次的那两个元素。
示例 :
输入: [1,2,1,3,2,5]
输出: [3,5]
知识讲解(这部分转自leetcode,详细理解见代码中的注释)
使用异或运算可以帮助我们消除出现两次的数字;我们计算 bitmask ^= x
,则 bitmask
留下的就是出现奇数次的位。
x & (-x)
是保留位中最右边 1
,且将其余的 1
设位 0
的方法。
首先计算 bitmask ^= x,则 bitmask 不会保留出现两次数字的值,因为相同数字的异或值为 0。
但是 bitmask 会保留只出现一次的两个数字(x 和 y)之间的差异。
我们可以直接从 bitmask 中提取 x 和 y 吗?不能,但是我们可以用 bitmask 作为标记来分离 x 和 y。
我们通过 bitmask & (-bitmask) 保留 bitmask 最右边的 1,这个 1 要么来自 x,要么来自 y。
当我们找到了 x
,那么 y = bitmask^x
。
代码实现
方法一:循环数组进行比较
/**
* Note: The returned array must be malloced, assume caller calls free().
*/
int* singleNumber(int* nums, int numsSize, int* returnSize){
if(NULL == nums || numsSize == 0){
printf("singleNumber param error\n");
*returnSize = 0;
return NULL;
}
int * p = NULL;
int i = 0, j = 0, k = 0;
int found = 0;
p = (int *)malloc(2 * sizeof(int));
if(NULL == p){
printf("singleNumber alloc error\n");
*returnSize = 0;
return NULL;
}
for(i = 0; i < numsSize; i++){
found = 0;
for(j = 0; j < numsSize; j++){
if(nums[i] == nums[j] && i != j){
found = 1;
break;
}
}
if(0 == found){
p[k++] = nums[i];
}
if(2 == k){
goto out;
}
}
out:
*returnSize = k;
return p;
}
方法二:通过异或取得单次数组的特征值,然后从数组提取的方式
int* singleNumber2(int* nums, int numsSize, int* returnSize){
if(NULL == nums || numsSize == 0){
printf("singleNumber param error\n");
*returnSize = 0;
return NULL;
}
int * p = NULL;
int i = 0, k = 0;
int bitmask = 0;
int diff = 0;
int x = 0;
p = (int *)malloc(2 * sizeof(int));
if(NULL == p){
printf("singleNumber alloc error\n");
*returnSize = 0;
return NULL;
}
/*
* 出现两次的数,被从bitmask中去掉了,出现一次的保存到了bitmask中
* 其实bitmask是数字的特征值,出现两个数字的会通过异或把特征值去除,
* 剩下的就是单独出现的两个数字的特征值。
* 如果单独出现的两个数字在某位上都是1的怎么处理呢?那不是情况了吗?
* 这就对了,相同为的特征值被抵消掉了,但一定会有不同的为,有特征值被保留下来,
* 否则就成相同的两个数字了
*/
for(i = 0; i < numsSize; i++){
bitmask ^= nums[i];
}
/*取得最右侧的1,到diff中*/
diff = bitmask & (-bitmask);
/*
* 这是的diff只有第一个出现一次的数 X 对应的bit位置
* 但是可能有很多的数,在改bit位上为1;
* 但是 x 通过不断的异或,把其他出现两次的数字去掉,
* 最后就剩下了只出现一次的且该位为1的 X
*/
for(i = 0; i < numsSize; i++){
if(0 != (diff & nums[i])){
x ^= nums[i];
}
}
p[k++] = x;
/*从bitmask中分离出x,就剩下了y*/
p[k++] = bitmask ^ x;
out:
*returnSize = k;
return p;
}
void testsingleNumber(void){
printf("\n************ testsingleNumber ************ \n");
int nums[] = {1,2,1,3,2,5};
int numsSize = sizeof(nums) / sizeof(int);
int returnSize = 0;
int * p= NULL;
int i = 0;
/*testcase 1*/
p = singleNumber(nums, numsSize, &returnSize);
if(NULL != p){
printf("\t returnSize = %d \n",returnSize);
printf("\t [ ");
for(i = 0; i < returnSize; i++){
printf(" %d ", p[i]);
}
printf(" ] \n");
free(p);
p = NULL;
}
/*testcase 2*/
p = singleNumber2(nums, numsSize, &returnSize);
if(NULL != p){
printf("\t returnSize = %d \n",returnSize);
printf("\t [ ");
for(i = 0; i < returnSize; i++){
printf(" %d ", p[i]);
}
printf(" ] \n");
free(p);
p = NULL;
}
return;
}
int main(int argc, char ** argv){
testsingleNumber();
}
代码编译
gcc SingleNumber.c -g -o a.exe -DDEBUG
调试输出
************ testsingleNumber ************
returnSize = 2
[ 3 5 ]
returnSize = 2
[ 3 5 ]