题目:给你一个 非空 整数数组 nums ,除了某个元素只出现一次以外,其余每个元素均出现两次。找出那个只出现了一次的元素。
你必须设计并实现线性时间复杂度的算法来解决此问题,且该算法只使用常量额外空间。
示例 1 :
输入:nums = [2,2,1]
输出:1
示例 2 :
输入:nums = [4,1,2,1,2]
输出:4
示例 3 :
输入:nums = [1]
输出:1
了解异或运算的几个基本特性:
-
0^N=N N^N=0
-
异或运算符合是交换律和结合律即:a ^ b ^ c= a ^ (b ^ c) ,a ^ b ^ c = a ^ c ^ b
-
在一堆数进行交换异或运算的时候,运算的结果跟运算先后是没有关系的
所以我们看到这道题时可以很轻松,只需要定义一个变量,令它为零,然后遍历整个数组依次去进行异或操作即可,我们知道出现两次的数进行异或就变成零了,那么最后整个结果就时那个只出现一次的数了。代码如下:
可以看出通过异或运算可以非常轻松的ac这道题,不止于此,我们还可以通过异或运算进行两个数的交换:示例如下:
int a=10;
int b=19;
//交换:
a=a^b; a=10^19;
b=a^b; b=10^19^19//因为异或有结合律,可以对后两个数先进行异或,结果为0,10^0=10;
a=a^b; a=10^19^10=19
//交换后
a=19;
b=10;
有点小妙,这样用异或运算交换两个数就不用额外再使用一个辅助变量了;
前提:a和b这两个变量不能指向同一个地址,因为就上述例子,如果两个变量是对一个地址的,当a异或b后,a就等于0了,这是a与b指向同一个地址,b的值也被变成0了,这时候就把a跟b都置零了,就达不到想要的效果了
我们还可以用异或来解决该题的进阶版:一个数组,有两种数出现了奇数次,其余的数都出现偶数次,如何找到这两个数?要求时间复杂度O(n),空间复杂度O(1):解决方法如下:
public static void printOddtimesnum2(int[] nums){
int ero=0;
for (int i=0;i<arr.length;i++){
ero ^=arr[i];//得到的ero肯定是那两个奇数次的数的异或表达式 a^b
}
//通过题干我们可以知道,a和b是两个不同的数,那么a^b肯定不等于零 ero!=0
//那么ero在二进制的表示上,肯定有一位,至少有一位是不为零的(为1),那么这个位上的1的来源
//就是来自a和b在这个位上的数不同,要么a在这个位上为0,b为1,要么a为1,b为0
int rightOne=ero & (~ero+1);//提取出最右位的1,这行代码很重要!!
//这个时候rightone除了最右边那个数是1以外,其他的都是0;
int onlyOne=0;//用来储存a或者b的其中一个
for (int i=0;i<arr.length;i++){
if ((arr[i] & rightone)==0){//只要arr[i]在那个位上的数不是1,那么结果肯定为零,可以简单的认为数组中的数是分成两批的,一批在该位上是0,一批在该位是1,偶数次的数与上rightone结果就变成零了,奇数次的是本身,然后就可以得到其中的一个数!!
onlyOne ^=arr[i];//得到的这个数,要么是a要么是b
}
}
//因为ero是等于a^b的
int other =ero^onlyone;//得到另一个奇数次的数,要么是a要么是b;
}
That’s all~