题目描述:
一个整型数组里除了两个数字之外,其他的数字都出现了两次。请写程序找出这两个只出现一次的数字。
//num1,num2分别为长度为1的数组。传出参数
//将num1[0],num2[0]设置为返回结果
public class Solution {
public void FindNumsAppearOnce(int [] array,int num1[] , int num2[]) {
}
}
分析:
思路一:暴力解法
先排序,然后比较a[i]和a[i-1],a[i+1],如果都不相同则输出,注意边界情况的判断。
思路二:暴力解法
遍历一遍,用ArrayList存储已经出现了一次的数字,当该数字出现第二次时,把它从ArrayList中移除,最后剩下的就是只出现了一次的数字。(用ArrayList存储是因为,该数据结构方便插入和删除操作。)
思路三:位相与
- 除了有两个数字只出现了一次,其他数字都出现了两次。异或运算中,任何一个数字和自己本身异或都是0,任何一个数字和0异或都是本身。
- 如果尝试把原数组分成两个子数组,且刚好每个子数组中各自包含一个只出现一次的数字。则在该前提下,每个子数组中,只有一个数字出现了一次,其他数字都出现了两次。
- 针对每个子数组,从头到尾依次异或每个数字,则最后留下来的就是只出现了一次的数字。因为出现两次的都抵消掉了。
- 怎样实现子数组的划分呢。若对原数组从头到尾的进行异或,则最后得到的结果就是两个只出现一次的数字的异或运算结果。这个结果的二进制表示中,至少有一位为1,因为这两个数不相同。该位记为从最低位开始计数的第n位。
- 则分组的标准定为从最低位开始计数的第n位是否为1。因为出现两次的同一个数字,各个位数上都是相同的,所以一定被分到同一个子数组中,且每个子数组中只包含一个出现一次的数字。
& 是 按位与运算符
-x 是x 的补码;补码为取反+1
x&(-x)返回x与2^64 的最大公约数,即x最多能被n个2整除就返回2^n;
如果x是奇数返回1
所以:
返回值为0,表示x=0
返回值为1,表示x为奇数
返回值为其他数,表示x为x与2^64的最大公约数
解答:
思路二:暴力解法
import java.util.ArrayList;
//num1,num2分别为长度为1的数组。传出参数
//将num1[0],num2[0]设置为返回结果
public class Solution {
public void FindNumsAppearOnce(int [] array,int num1[] , int num2[]) {
ArrayList<Integer> once = new ArrayList<Integer>();
for (int i = 0; i < array.length; i++) {
int a = array[i];
if(once.contains(a)){
once.remove(once.indexOf(a));
}else{
once.add(a);
}
}
num1[0] = once.get(0);
num2[0] = once.get(1);
}
}
思路三:位相与
//num1,num2分别为长度为1的数组。传出参数
//将num1[0],num2[0]设置为返回结果
public class Solution {
public void FindNumsAppearOnce(int[] nums, int num1[], int num2[]) {
int diff = 0;
for (int num : nums)
diff ^= num;
// 得到最右一位
diff &= -diff;
for (int num : nums) {
if ((num & diff) == 0)
num1[0] ^= num;
else
num2[0] ^= num;
}
}
}