前往LeetCode做题
题目
一个整型数组 nums 里除两个数字之外,其他数字都出现了两次。请写程序找出这两个只出现一次的数字。要求时间复杂度是O(n),空间复杂度是O(1)。
示例 1:
输入:nums = [4,1,4,6]
输出:[1,6] 或 [6,1]
思路
只有两个数字只出现了一次,其它都出现了两次
那么可以使用 位运算 异或 ^
- 两个相同的数字
异或
结果为0 - 这样一来,数组中的所有元素异或的结果,相当于那两个 只出现一次的数字 异或的结果
- 得到两个数字异或的结果之后,我们需要一个条件将两个数字区分开来
- 找到两个数字的
二进制位
中不相同的一位即可。(这样之后可以分组异或,最终得到两个数字各自和全部元素异或的结果)
位运算 异或 (^
) 的定义:
相同的位(0 0 或者 1 1)异或的结果是0,
不同的位(0 1 或者 1 0)异或的结果是1。
因此,我们需要找到两个数字异或的结果 的二进制位中的 1
一个数字 10
0000 1010
取反
1111 0101
取反+1(一个数字的负数,就是取反+1)
1111 0110
进行与运算 10 & (-10)
0000 1010
1111 0110
^这一位是相等的,
与(&
)运算的定义:
两个位都是1
(1 1)与的结果是1,
其它(0 1 或者 1 0 或 0 0)与的结果是0。
代码
class Solution {
public int[] singleNumbers(int[] nums) {
int x = 0;
// 得到两个只出现一次的数字
for(int num:nums){
x ^= num;
}
// 得到不相同的位了
x = x&(-x);
// 分组异或
int n1 = 0;
int n2 = 0;
for(int num:nums){
// 这里需要注意,位运算的优先级低于关系运算,所以最好的办法是加一个括号
if((num & x) == 0){
// 由于得到的 x 所有位中只包括了一个 1,所以那一位不为 0 的数字都会与 n1异或
n1 ^= num;
}else{
n2 ^= num;
}
}
return new int[]{n1, n2};
}
}
关键点
- 相同的数字异或结果为 0
x & ( -x)
可以得到从右边数第一个不为 0 的数字(重要)- 与运算的优先级别比较低,所以做
a&b == c
的运算的时候,注意加一个括号就万事大全了。(a&b) == c