题目:
数组中只出现一次的两个数字。
一个整型数组里除两个数字之外,其他数字都出现了两次。请写程序找出这两个只出现一次的数字。要求时间复杂度O(n),空间复杂度O(1)。
分析:
例如,输入数组{2,4,3,6,3,2,5,5},因为只有4和6只出现了一次,其他数字出现了两次,所以输出4和6。
先考虑这样一个问题,如果这个数组中,只有一个数组出现1次,其余数字都出现了2次,怎么找到这个出现了1次的数字呢?这时候,可以想到异或运算的一个性质:任何一个数字异或自己都等于0。也就是从头到尾异或一遍的结果,就是只出现1次的数字了,因为出现两次的数字都变成0了。
再回到原来的问题上,如果尝试将原数组分成两部分,将出现1次的两个数字分到两部分中,就可以使用上面的方法把出现1次的这两个数找出来了。此时,将数组里的数字依次异或,这个操作的效果等价于将只出现1次的两个数字做异或。这个结果值一定不为0,那么,这个数的二进制形式,一定至少存在一个1,在结果值的二进制中找到右数的第一个1。那么就根据这个特点,将原数组划分成两部分。那些成对出现的数字一定被划分到了一组中,出现1次的两个数字也可以被划分到两个部分。
解法:
package com.wsy;
public class Main {
public static void main(String[] args) {
int[] array = new int[]{2, 4, 3, 6, 3, 2, 5, 5};
int first = 0, sum = 0, length = array.length;
for (int i = 0; i < length; i++) {
sum ^= array[i];
}
int right = sum & (~sum + 1);// 找出最右边的1
for (int i = 0; i < length; i++) {
if ((array[i] & right) != 0) {// 根据right中的1对数组中的数据分组
first ^= array[i];
}
}
System.out.println("出现一次的数字是:" + first + "," + (sum ^ first));
}
}
题目:
数组中唯一只出现一次的数字。
在一个数组中除一个数字之外,其他数字都出现了三次。请找出那个只出现了一次的数字。
分析:
上面的异或思路,在这里不能用了,于是换一个思路。如果一个数字出现了3次,那么它的二进制表示的每一位也出现了3次,将所有出现3次的数字的二进制表示的每一位加起来,每一位的和都可以被3整除。
于是,我们将数组中的数字都用二进制表示出来,将它们按位相加,如果某一位的和能被3整除,那么只出现1次的这个数的二进制表示中,这一位是0,否则,这一位是1。那么就得到了这个只出现1次数字的二进制表示,也就得到了这个只出现一次的值。
解法:
package com.wsy;
public class Main {
public static void main(String[] args) {
int[] array = new int[]{1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4, 6, 7, 7, 7};
int[] bits = new int[32];
int length = array.length;
for (int i = 0; i < length; i++) {
for (int j = 0; j < 32; j++) {
bits[j] += array[i] >> (31 - j) & 1;// 结果:bits[0]是最高位,bits[31]是最低位
}
}
int result = 0;
for (int i = 0; i < 32; i++) {
result <<= 1;
result += bits[i] % 3;
}
System.out.println("出现一次的数字是:" + result);
}
}