题目描述:
一个数组中,有若干种数,出现次数为单数和双数,假如只有一种出现为单数的数,怎么找出来?
例:[8,2,3,8,2,2,2]
预期结果:[3]
思路:
第一种:介于本身实力有限,首先想到最简单的实现就是存HashMap,遍历数组存放不同键中,每次相同value+1,但频繁遍历+get可想而知效率就不谈了。
第二种:用异或(^)这种位运算来解决(因为位运算是底层二进制运算,所以速度非常快)
什么是异或运算
异或就是一种无进位相加的思想,相同位相加得0(1和1不会产生进位),异位相加得1
所以上述可推出两个异或非常关键的公式:
1.0^a = a
2.a^a = 0
由此可得偶数个数异或的结果都为0,因此把数组中每一个数都进行异或运算,得出最后为单数的那个数。
实现java代码:
public class FindOddNumberInArray {
public static int findOddNumberInArray(int[] arr) {
int result = 0;
for (int cur: arr) {
result = result ^ cur;
}
return result;
}
public static void main(String[] args) {
System.out.println(findOddNumberInArray(new int[]{8,2,3,8,2,2,2}));
}
}
拓展:
拓展:假如出现为单数的数为两种,怎么找出来
例:[8,2,3,8,2,2,2,4]
预期结果:[3,4]
通过上述的了解,暂时认为要求的数是a和b,进行异或运算时得到的最终结果是a^b,这里知道a和b是两种不同的数,所以得出a^b != 0,所以能推出a^b必然有一位是1,那么从数组整体角度上来看现在只有两种数,一种是某一位为1的数,一种是某一位为0的数,正因为他们分别都是偶数,所以可以先求出某一位为1的那一个单数或者某一位不为1的那个单数(这里我理解是向下拆分成两组,得出某一个单数),得到的单数和a^b做异或运算就可以得出另外的数。
看下代码实现:
public static int[] findTwoOddNumberInArray(int[] arr) {
int xor = 0;
int result = 0;
for (int cur: arr) {
xor = xor ^ cur;
}
int right = xor & (~xor + 1); //提取出最右侧的1
for (int cur: arr) {
if ((cur & right) == 0) {
result = result ^ cur;
}
}
return new int[]{result,result^xor};
}
这里解释一下这行:int right = xor & (~xor + 1)
a的非运算就是按位取反,与(&)运算就是只有都为1的时候才为1,所以如图就求出最右侧1代表的数(这里应用的就是原码与补码加1取出最右侧的1,想了解这部分知识的可以去看下原码和补码的知识 )