数组中只出现一次的数字
一个整型数组里除了两个数字之外,其他的数字都出现了两次。请写程序找出这两个只出现一次的数字。
分析:
- 相信大家对数组中只有一个数字对出现一次,其他都出现2次的题目(算法)都清楚,嗯,对,就是异或。
/**
* 数组a中只有一个数出现一次,其他数都出现了2次,找出这个数字
* @param a
* @return
*/
public static int find1From2(int[] a){
int len = a.length, res = 0;
for(int i = 0; i < len; i++){
res = res ^ a[i];
}
return res;
}
- 然而此题是一个变种,再把全部数字异或得到的值为那两个数的异或值,无法得到单个元素值,此时感觉没法走下去了,别急,既然异或可以求出单个元素的情况,那么我们是否可以把原来的数组分组呢?只要把这两个不同的数分到不同组,再异或,就得到了值。额,是的,有的头绪了,慢着?那总得分组依据吧。。。额,又傻了是不是。。。仔细想下,第一个问题求出了两个不同数的异或值,我们知道不同的数异或值一定不等于0,即异或值位中最少有一个1,且这个1= 0^1 得到的,在这个位上那两个数是不同的,因此分组依据就清楚咯。下面直接看代码。
/**
* 一个整型数组里除了两个数字之外,其他的数字都出现了两次。请写程序找出这两个只出现一次的数字。
* num1,num2分别为长度为1的数组。传出参数
* 将num1[0],num2[0]设置为返回结果
* @param array
* @param num1
* @param num2
*/
public void FindNumsAppearOnce(int [] array,int num1[] , int num2[]) {
if (array == null || array.length <= 1) {
return;
}
int a = 0;
for (int i = 0;i<array.length;i++) {
a = array[i]^a;
}
int index = 0;
while ((a & 1) == 0) { //从右到左,寻找第一位为1的位
a = a >> 1;
index++;
}
for (int i =0;i<array.length;i++) {
if (isIndexBit(array[i], index)) {
num1[0] ^= array[i];
}else{
num2[0] ^= array[i];
}
}
}
private boolean isIndexBit(int num, int index) {
num = num>>index;
return (num & 1) == 1;
}
至此,问题已经解决了。。。
- 扩展问题: 数组a中只有一个数出现一次,其他数字都出现了3次,找出这个数字,这个问题,也需要点技巧,将每个位为1的个数统计出来,最后判断,如果这个为个数为3的倍数,那么这个数此位为0,否则为1,通过位移以及或运算可以求得这个数。
/**
* 数组a中只有一个数出现一次,其他数字都出现了3次,找出这个数字
* @param a
* @return
*/
public static int find1From3(int[] a){
int[] bits = new int[32];
int len = a.length;
for(int i = 0; i < len; i++){
for(int j = 0; j < 32; j++){
bits[j] = bits[j] + ( (a[i]>>>j) & 1);
}
}
int res = 0;
for(int i = 0; i < 32; i++){
if(bits[i] % 3 !=0){
res = res | (1 << i);
}
}
return res;
}
至此,问题才能说是圆满解决了!!!