一个整型数组里除了两个数字之外,其他的数字都出现了两次。请写程序找出这两个只出现一次的数字。
看到题目的时候确实 有点轻敌了:
乍一看题目,哟吼,这不是就简单的数组计数器就可以搞定了吗?
然后一顿哐哐操作写好算法与测试类:
@Test
public void ttest1() {
int arr[] = {1, 1, 2, 2, 3, 4, 4, 5};
System.out.println(Arrays.toString(FindNumsAppearOnce_MyTest(arr)));
}
//num1,num2分别为长度为1的数组。传出参数
//将num1[0],num2[0]设置为返回结果
public int[] FindNumsAppearOnce_MyTest(int[] array) {
int[] ints = new int[array.length];
int[] result = new int[2];
int count = 0;
for (int anArray : array) {
ints[anArray]++;
}
for (int i = 0; i <array.length; i++) {
if (ints[i]==1){
result[count++]= i;
}
}
return result;
}
/*
输出结果:
[3, 5]
稳了,没毛病。
*/
结果将算法稍作改写丢到牛客网上:
//num1,num2分别为长度为1的数组。传出参数
//将num1[0],num2[0]设置为返回结果
public class Solution {
public void FindNumsAppearOnce(int [] array,int num1[] , int num2[]) {
int[] ints = new int[array.length];
int count = 0;
for (int i = 0; i < array.length; i++) {
ints[array[i]]++;
}
for (int i = 0; i <array.length; i++) {
if (ints[i]==1){
count++;
if (count == 1) {
num1[0] = i;
} else {
num2[0] = i;
}
}
}
}
}
顿时报错:
大意了啊 实在妹又想到还有这么大的数字,这对int[]
数组来说确实是不可行的。也就是说用数组计数器的方法不够全面。
不能用数组计数器做那得咋整啊?
仔细审题,题目中说寻找只出现一次的数字
众所周知,Map中的key是不能重复的,如果重复是会自动覆盖前者,因此我们可以利用HashMap进行操作(见解答一)
本题知识点: 位运算 哈希
不过遍历多次总感觉可以继续优化,查阅资料:
对于查找只出现一次的问题可以用 ^ 异或
运算进行解决
异或的性质:对于整数a,有
(1)a^a=0
(2)a^0=a
(3)a^b^c=a^(b^c)=(a^c)^b
因此首先将数组异或运算一次后会出现一个结果:
根据上述公式(3)是只出现一次的两个数(A^B的结果),这个结果的二进制中的1,表现的是A和B两个数字在二进制上的不同位。
int result = 0;
for (int i = 0; i < arr.length; i++) {
result ^= arr[i];
}
System.out.println(result);
接下来就是如何将(A^B)的结果拆分为A和B
//解答一:哈希算法
//在Map中用value标识出现的次数,用key存储元素
public void FindNumsAppearOnce(int [] array,int num1[] , int num2[]) {
HashMap<Integer, Integer> map = new HashMap<Integer, Integer>();
for(int i=0; i < array.length; i++){
if(map.containsKey(array[i]))
//如果出现过一次则直接存入{`出现两次的元素`:2}
map.put(array[i],2);
else
map.put(array[i],1);
}
int count = 0;
for(int i=0; i < array.length; i++){
if(map.get(array[i]) == 1){
if(count == 0){
num1[0] = array[i];
count++;
}else
num2[0] = array[i];
}
}
}
//解答2:运用位移和^运算
public String FindNumsAppearOnce_withOperation(int [] array) {
int num1[] = new int[1];
int num2[] = new int[1];
int xor1 = 0;
for(int i=0; i < array.length; i++){
xor1 = xor1^array[i];
}
//在xor1中找到第一个不同的位对数据进行分类,分类为两个队列对数据进行异或求和找到我们想要的结果
int index = 1;
while((index & xor1)==0)
//因为可能有多个位为1所以需要求一下位置
index = index <<1;
int result1 = 0;
int result2 = 0;
for(int i=0; i < array.length; i++){
if((index & array[i]) == 0)
result1 = result1^array[i];
else
result2 = result2^array[i];
}
num1[0] = result1;
num2[0] = result2;
return num1[0]+","+num2[0];
}