在一个数组中已知只有一种数出现了奇数次,其他的所有数都出现了偶数次?
问题一:怎么找到出现了奇数次的数?
问题二:如果有两种数出现了奇数次,如何找到?
要求时间复杂度 O(N),空间复杂度 O(1)
实现逻辑
使用异或运算符 ^
的特性实现:
1)不同为1 相同为0,如:1^1=0,0^0=0,1^0=1
2)无进位相加,如:二进制`0011+0101=0110` 转换成十进制就是`5^3=6`
3)0和任何数异或都为N,如:0^N=N
4)任何数和自己异或都为0,如:N^N=0
5)异或运算与顺序无关,谁先谁后都可,只要是同一批数结果都一样,如:a^b=b^a
另外需要注意的是 eor & (~eor + 1)
:
~eor:对eor按位取反。这意味着在~eor中1变0,0变1
~eor + 1:对~eor加1。考虑二进制数的性质,这实际上是将~eor最右侧的0变为1
eor & (~eor + 1):按位与运算的性质,只有当两个操作数的对应位都为1时,结果的对应位才为1,但是在前面已经对~eor取反了,只有+1之后的最右侧一位为1
综上所述,表达式 eor & (~eor + 1) 可以取出 eor 最右侧的1
java
public static void main(String[] args) {
int[] arr1 = {64, 25, 12, 22, 11, 11, 22, 11, 11, 25, 64};
// 调用问题一的函数,输出出现奇数次的数字
question1(arr1);
int[] arr2 = {64, 25, 12, 22, 11, 11, 22, 11, 11, 25, 64, 18};
// 调用问题二的函数,输出出现奇数次的两个数字
question2(arr2);
}
// 问题一的函数,通过异或运算找出出现奇数次的数字
public static void question1(int[] arr) {
// 初始化异或结果为0
int eor = 0;
// 遍历数组,对每个元素进行异或运算
for (int cur : arr) {
eor ^= cur;
}
// 输出异或结果,即为出现奇数次的数字
System.out.println("问题一答案:" + eor);
}
// 问题二的函数,通过异或运算找出出现奇数次的两个数字
public static void question2(int[] arr) {
// 初始化异或结果为0
int eor = 0;
// 遍历数组,对每个元素进行异或运算
for (int cur : arr) {
eor ^= cur;
}
// eor = a ^ b,a和b为出现奇数次的两个数字
// eor != 0,因为a和b不相等,所以至少有一个位置上是1
// eor必然有一个位置上是1,提取出最右侧的1
int rightOne = eor & (~eor + 1); // 提取出最右侧的1
// 初始化onlyOne为0,用于存储其中一个出现奇数次的数字
int onlyOne = 0;
// 遍历数组,对每个元素进行判断
for (int cur : arr) {
// 如果当前元素与rightOne进行与运算的结果不为0,说明当前元素在rightOne对应的位置上是1
// 对onlyOne进行异或运算,将其更新为a或b中的一个数字
if ((cur & rightOne) != 0) {
onlyOne ^= cur;
}
}
// 输出onlyOne和eor ^ onlyOne,即为出现奇数次的两个数字
System.out.println("问题二答案:" + onlyOne + "," + (eor ^ onlyOne));
}
文档信息
- 本文作者:carpe
- 本文链接:https://carpedx.com/wiki/algorithm-question-odd-number/
- 版权声明:自由转载-非商用-非衍生-保持署名(创意共享3.0许可证)