题目描述
在每日一练中有一道简单的题目:从数组中找出只出现一次的数据,具体描述如下:
给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现两次。找出那个只出现了一次的元素。
要求:线性时间复杂度,不使用额外空间。
例如给定数组[2,2,1],输出应为1;数组[4,1,2,1,2]结果为4。
思路探究
首先想到的思路为数组每个数循环与其他数字比较,找到不相等的数:
#include <stdio.h>
int nums[] = {1,1,2,3,4,5,4,5,2}; //给定一个测试数组
int main(){
int ret,i,count,arraySize,flag;
arraySize = sizeof(nums) / sizeof(int); //计算数组大小
for(count = 0;count < arraySize;count++) //循环数组中的每一个数
{
flag = 0; //是否有其他相同数标志
for(i = 0;i < arraySize;i++)
{
if(i != count && nums[i] == nums[count]) //与数组中其他数比较
{
flag = 1; //有其他相同的数
break;
}
}
if(flag == 0) //找到单独的数,输出,结束程序
{
printf("%d",nums[count]);
break;
}
}
return 0;
}
运行结果如下:
但这种笨方法并不符合线性时间复杂度的要求,正确的方法是利用异或运算的特性解题:
1、一个数和0异或保持不变;
2、一个数和自身异或结果为0。
那么按题目要求的偶数个数组成员异或后会为0,唯一的一个奇数个成员与0异或则为想要的结果:
#include <stdio.h>
int nums[] = {1,1,2,3,4,5,4,5,2}; //给定一个测试数组
int main(){
int ret,i,arraySize;
ret = 0;
arraySize = sizeof(nums) / sizeof(int); //计算数组大小
if(arraySize == 2 || arraySize == 0) return; //数组成员两个不符合题意
for(i = 0;i < arraySize;i++) ret ^= nums[i]; //依次异或
printf("异或方法:%d",ret);
return 0;
}
运行结果如下:
异或探究
进一步思考的话,凭借异或的两个特性,如果数组是这样[1,1,2,2,3,3,5]的话很好理解:0 ^ 1 = 1,1 ^ 1 = 0,0 ^ 2 = 2,2 ^ 2 = 0……。而程序中为什么可以忽略数组变量的排列顺序得到想要的结果呢?
通过两个例子来看:
[1,3,1,3]异或:
[1,2,3,4]异或:
可以看出,因为异或是按位运算,多个数异或其实就是根据这一位上1的奇偶判断:
1、偶数个1(包括0),该位为0;
2、奇数个1,该位为1。
由此,带入上面的题目就很好理解了,除开单独的数,其他数异或运算时每一位上1的个数一定是偶数个。
简单的可以理解为异或的运算顺序与“+”类似,可以随意的调换运算顺序而不影响结果。
同样的,使用异或交换值的过程就很好理解了:
a = a ^ b;
b = a ^ b;
a = a ^ b;
第二步b = a ^ b转换为b = a ^ b ^ b = a;
第三步a = a ^ b转换为a = a ^ b ^ a = b;
版权声明:本文为m0_46222499原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。