题目
一个整型数组 nums 里除两个数字之外,其他数字都出现了两次。请写程序找出这两个只出现一次的数字。要求时间复杂度是O(n),空间复杂度是O(1)。
示例 1:
输入:nums = [4,1,4,6]
输出:[1,6] 或 [6,1]
示例 2:
输入:nums = [1,2,10,4,1,4,3,3]
输出:[2,10] 或 [10,2]
思路
- 题干:整型数组 nums 里除两个数字之外,其他数字都出现了两次
- 如果某个数组中只有一个数字出现一次,其他都出现两次,那么所有元素求异或之后就是只有一次的数字,因为两个相同元素异或消除
- 如果有两个数字a,b 只出现一次,那么就试图将他俩分到不同组里。首先对所有元素求异或,那么最终结果是x=a^b,我们从最低位分析,那么如果a和b在xi位相同,则xi=0,因此假设xj=1,那么在这一位上a和b不同,将其他元素按照这一位划分成两组,分别取异或。
- 遍历异或:结果就是两个不同数字的异或值n
- 找出异或结果n中二进制第一个出现为1的位置:获得n最低位的1
- 分组:此位置与结果为0分为第一组,结果1分为第2组
两个不同的元素此位置势必不同(因为n就是它两的异或结果m就是这两不同元素的异或为1的值,不同异或才为1,这两元素肯定只有一个与此位置相同,一个不同)势必分为不同组,相同势必分为同一组
那两个不同的元素肯定会被分到不同组- 对每一组异或最后剩余的就是只出现一次的元素
class Solution {
public int[] singleNumbers(int[] nums) {
//如果某个数组中只有一个数字出现一次,其他都出现两次,那么所有元素求异或之后就是只有一次的数字,因为两个相同元素异或消除
//如果有两个数字a,b 只出现一次,那么就试图将他俩分到不同组里。首先对所有元素求异或,那么最终结果是x=a^b,我们从最低位分析,那么如果a和b在xi位相同,则xi=0,因此假设xj=1,那么在这一位上a和b不同,将其他元素按照这一位划分成两组,分别取异或。
int x=0;
int y=0;
int m=1;
int n=0;
//遍历异或:结果就是两个不同数字的异或值n
for(int num:nums){
n^=num;
}
//找出异或结果n中二进制第一个出现为1的位置:获得n最低位的1(获取n的任意出现1的位置根据此位置分组)
//while((n&m)!=1){}条件不成立,左移与出来的结果是0和非0(不是0和1)
while((n&m)==0){
m<<=1;
}
//分组:此位置与结果为0分为第一组,结果1分为第2组
//两个不同的元素此位置必不同势必分为不同组,相同势必分为同一组
//那两个不同的元素肯定会被分到不同组
//对每一组异或最后剩余的就是只出现一次的元素
/*
举例假设:
nums = [1,2,10,4,1,4,3,3]
第一组:1,1,3,3, 10
第二组:4,4, 2
*/
for(int num:nums){
if((m&num)==0){
x^=num;
}else{
y^=num;
}
}
return new int[]{x,y};
}
}
复杂度
时间复杂度O(n): n为数组的长度
空间复杂度O(1): x,y,m,n都是常数阶