法一(位运算)
/**
* 法一(位运算)
* 1. 思路
* (1)设题目中这两个只出现1次的数字分别为A和B
* (2)由于A,B肯定是不相等的,因此在二进制上必定有一位是不同的,根据这一位是0还是1可以将A,B分开到A组和B组
* (3)再对A组和B组分别进行组内异或就可以得到A,B了
* 2. 注意
* (1)x ^ x = 0,x ^ 0 = x
* (2)x & -x = x & (~x + 1)
* (3)x & -x:保留二进制下最后出现的1的位置,其余位置置0
* 3. 理解
* (1)例如,int nums[] = {1, 2, 1, 3, 2, 5}
* (2)整个数组异或的结果xor为3^5,即 0011 ^ 0101 = 0110
* (3)xor & -xor,即 0110 & 1010 = 0010
* (4)对0110,第1位(从0开始,由低位向高位)就是1,据此可以将数组分为两组,二进制第1位为1的为一组,二进制第1位为0的为一组
* nums[0] = 1 0001 第一组
* nums[1] = 2 0010 第二组
* nums[2] = 1 0001 第一组
* nums[3] = 3 0011 第二组
* nums[4] = 2 0010 第二组
* nums[5] = 5 0101 第一组
* (4)第一组有{1,1,5},第二组有{2,2,3},然后对这两组分别进行组内异或就可以得到5和3了
* 4. 复杂度
* (1)时间复杂度 O(n)
* (2)空间复杂度 0(1)
*
* @param nums
* @return
*/
public int[] singleNumber(int[] nums) {
int xor = 0;
for (int num : nums) { // 先对nums中的所有元素执行异或操作,xor为A和B的异或值
xor ^= num;
}
int lsb = xor & -xor; // 保留xor二进制下最后出现的1的位置,其余位置置0
int[] ans = new int[2];
for (int num : nums) {
if ((num & lsb) != 0) { // 通过lsb可以将数组中的数字分为两组,分别进行组内异或运算
ans[0] ^= num;
} else {
ans[1] ^= num;
}
}
return ans;
}
法二(哈希)
/**
* 法二(哈希)
* 时间复杂度 O(n)
* 空间复杂度 0(n)
*
* @param nums
* @return
*/
public int[] singleNumber_2(int[] nums) {
Map<Integer, Integer> map = new HashMap<>();
for (int num : nums) {
map.put(num, map.getOrDefault(num, 0) + 1);
}
int[] ans = new int[2];
int index = 0;
for (int num : nums) {
if (map.get(num) == 1) {
ans[index++] = num;
}
}
return ans;
}
本地测试
/**
* 260. 只出现一次的数字 III
*/
lay.showTitle(260);
Solution260 sol260 = new Solution260();
int[] nums260 = new int[]{1, 2, 1, 3, 2, 5};
System.out.println(Arrays.toString(nums260));
System.out.println(Arrays.toString(sol260.singleNumber(nums260)));
System.out.println(Arrays.toString(sol260.singleNumber_2(nums260)));