数组中,出现奇数次的数
从一个数组中找出出现了奇数次的数字,要求:
- 时间复杂度:
O(n)
- 空间复杂度:
O(1)
题目
-
从数组中找出一个出现奇数次的数字,如:
{1, 2, 2, 1, 3, 1, 1, 3, 3}
,结果返回: 3 -
从数组中找出两个出现奇数次的数字,如:
{1, 2, 2, 1, 3, 1, 1, 3, 3, 4}
,结果返回: 3, 4
使用异或解题 (java)
-
定义一个
int eor = 0;
,将array中的数据遍历,与eor进行异或计算:int array = {1, 2, 2, 1, 1, 1, 3}; int eor = 0; for (int cur: array) { eor = eor ^ cur; }
解析:当
cur = 1
,eor = 0
,eor与cur异或,将返回cur: 1。以此往下,偶数项将化为0。如下:1 ^ 2 = 3 1 ^ 2 ^ 1 = 2 1 ^ 2 ^ 1 ^ 2 = 0 1 ^ 2 ^ 1 ^ 2 ^ 1 = 1 1 ^ 2 ^ 1 ^ 2 ^ 1 ^ 1 = 0
只要这两个数在数组中为偶数,那一定会化为0,0再与任何数异或都是返回数的本身。因此:
3 ^ 3 = 0 3 ^ 3 ^ 3 = 3
我们通过遍历,将每个数进行异或,去除掉出现偶数次的数。就可以获得奇数次的
3
,将它存储到eor
-
定义一个
int eor = 0;
,与第一题一致的代码。这样我们就会得到eor = a ^ b
, 其中的a, b
是两个出现奇数次的数字。eor遍历一次后,偶数次的数将会都变成0,只剩下奇数次的数,如:- 处理前:
{1, 2, 2, 1, 3, 1, 1, 3, 3, 4}
- 处理后:
{3, 4}
你问为什么
{3, 3, 3}
会变成3
,因为:3 ^ 3 ^ 3 = 3
,前面两个3将被化掉。剩下3, 4。已知
a ^ b
,则a或b一定不为0,互不相等,且a ^ b的 二进制位上 为1的数,代表a, b这两位数上的二进制位是不一样的。如:A = 0 0 1 B = 0 1 0 A ^ B = 0 1 1
从此看出,A,B有两处的二进制位不同,我们只需要 通过最后一位1来与之比对,即可找到a或b。
// eor = a ^ b int rightOne = eor & (~eor + 1); /** * 获取最右侧1 * eg. * eor = 10101001 原码 * ~eor = 01010110 反码 * ~eor+1 = 01010111 补码 * eor & (~eor + 1) = * 10101001 * & 01010111 * ------------------ * 00000001 */
但是,最后一位1的位置,其他数的二进制位也可能是1,这样就会导致误判。因此,我们可以再进行异或操作,筛选掉出现偶数次的数,留下奇数次的数。
int onlyEor = 0; for (int cur: arr) { if ((cur & rightOne) == 0) { onlyEor = onlyEor ^ cur; } } /** * eg. * cur = 0010 * rightOne = 0001 * cur & rightOne = 0000 * 则 cur 可能是a或者b */
我们获取到了
a ^ b
, 由通过rightOne
获取到onlyEor = a 或 b
,那么如何取出a或b呢?这里又用到异或:a ^ b ^ a = b a ^ b ^ b = a
不管
onlyEor
是a或者b,都不影响我们取出另一个数。则:
System.out.println(onlyEor + ", " + (onlyEor ^ eor));
- 处理前:
文尾
java实现
public class Main {
public static void printOddNumber(int[] arr) {
int eor = 0;
for (int number: arr) {
eor = eor ^ number;
}
System.out.println("Odd number: " + eor);
}
public static void printOddNumberTwo(int[] arr) {
int eor = 0;
for (int cur: arr) {
eor = eor ^ cur;
}
// eor = a ^ b
int rightOne = eor & (~eor + 1);
/**
* 获取最右侧1
* eg.
* eor = 10101001
* ~eor = 01010110
* ~eor+1 = 01010111
* eor & (~eor + 1) =
* 10101001
* & 01010111
* ------------------
* 00000001
*/
// System.out.println("rightOne: " + rightOne);
int onlyEor = 0;
for (int cur: arr) {
if ((cur & rightOne) == 0) {
onlyEor = onlyEor ^ cur;
}
}
/**
* eg.
* cur = 01001011
* cur & rightOne =
* 01001010
* & 00000001
* ----------------
* 00000000
*
*/
/**
* eor = a ^ b
* onlyEor ^ eor = a 或 b
*/
System.out.println(onlyEor + ", " + (onlyEor ^ eor));
}
public static void main(String[] args) {
int[] arr = {1, 2, 1, 2, 1, 3, 1, 3, 3};
printOddNumber(arr);
int[] arr1 = {1, 2, 1, 2, 1, 1, 3, 3, 4, 3};
printOddNumberTwo(arr1);
}
}
信息
- 作者:PYmili
- 邮件:mc2005wj@163.com
- Q群: 706128290