问题:查找数组中出现次数为奇数的两个数,只有两个数出现次数为奇数,其余数字出现次数为偶数。
使用位运算性质:
1、a^a=0
2、-a=~a+1
3、a & (-a) 可以得到a二进制中最右侧1
假设两个奇数分别为a、b
- 第一次将数组中所有数字异或运算之后得到的结果result=a^b
for (int j : arr) {
result ^= j;
}
- 获取result二进制中最右侧1 r,因为result的结果为a^b,根据异或运算的性质可知,a和b只能有一个数字二进制最右侧1 为r,所以可以使用(r & i) != 0或者(r & i) == 0将数组中数字分为两组,一组二进制最右侧1 为r,一组二进制最右侧1 不为r,而且a和b一定不在同一组
int r = test2(result);
/**
* 获取一个数字最右侧1
* @param n
*/
private static int test2(int n) {
return n & (-n);
}
- 将分组后的其中一组进行异或运算得到s,得到a和b其中一个数,即s=a或s=b
int s = 0;
for (int i : arr) {
// 将数组中最右侧不为1的数^r
if ((r & i) != 0) {
s ^= i;
}
}
- 然后使用s^result,即s^a^b,因为异或运算满足交换律,因此,可以得到另一个数,完整代码如下:
/**
* 查找数组中出现次数为奇数的两个数
*
* @param arr
*/
private static List<Integer> test3(int[] arr) {
int result = 0;
for (int j : arr) {
result ^= j;
}
int r = test2(result);
System.out.println(r);
int s = 0;
for (int i : arr) {
// 将数组中最右侧不为1的数^r
if ((r & i) != 0) {
s ^= i;
}
}
List<Integer> integers = Arrays.asList(s, s ^ result);
integers.sort((o1, o2) -> o1 - o2);
return integers;
}/**
* 获取一个数字最右侧1
* @param n
*/
private static int test2(int n) {
return n & (-n);
}
使用hashmap辅助验证:
/**
* hashmap方式
* 查找数组中出现次数为奇数的两个数
*
* @param arr
*/
private static ArrayList<Integer> test3HashMap(int[] arr) {
HashMap<Integer, Integer> map = new HashMap<>();
for (int num : arr) {
if (map.containsKey(num)) {
map.put(num, map.get(num) + 1);
} else {
map.put(num, 1);
}
}
ArrayList<Integer> integers = new ArrayList<>();
for (int num : map.keySet()) {
if (map.get(num) % 2 != 0) {
integers.add(num);
}
}
integers.sort((o1, o2) -> o1 - o2);
return integers;
}
验证
:
public static void main(String[] args) {
int[] arr = randomArray(1000, 2, 20);
System.out.println(test3(arr));
System.out.println(test3HashMap(arr));
}
生成数组对数器randomArray:
/**
* 对数器
* @param max 最大值
* @param k 出现次数为奇数的个数
* @param m 出现次数为偶数的个数
* @return
*/
private static int[] randomArray(int max, int k, int m) {
// 记录每个奇数出现的次数
ArrayList<Integer> oddKinds = new ArrayList<>();
// 记录每个偶数出现的次数
ArrayList<Integer> evenKinds = new ArrayList<>();
// 生成出现k个奇数
int valueK;
// 生成每个奇数出现的次数
// 计算数组长度
int len = 0;
while (k != 0) {
do {
valueK = (int) (Math.random() * max + 1);
} while (valueK % 2 == 0);
k--;
len += valueK;
oddKinds.add(valueK);
}
// 生成每个偶数出现的次数
while (m != 0) {
do {
valueK = (int) (Math.random() * max + 1);
} while (valueK % 2 != 0);
m--;
len += valueK;
evenKinds.add(valueK);
}
int[] arr = new int[len];
HashSet<Integer> set = new HashSet<>();
// 添加奇数
int index = 0;
index = addArr(max, oddKinds, arr, set, index);
// 添加偶数
addArr(max, evenKinds, arr, set, index);
// 打乱数组顺序
for (int j = 0; j < arr.length; j++) {
int index1 = (int) (Math.random() * arr.length);
int temp = arr[j];
arr[j] = arr[index1];
arr[index1] = temp;
}
return arr;
}
private static int addArr(int max, ArrayList<Integer> oddKinds, int[] arr, HashSet<Integer> set, int index) {
int valueK;
for (Integer oddKind : oddKinds) {
do {
valueK = randomNumber(max);
} while (set.contains(valueK));
set.add(valueK);
for (int integer = 0; integer < oddKind; integer++) {
arr[index] = valueK;
index++;
}
}
return index;
}
/**
* 生成随机数
* @param range
* @return
*/
private static int randomNumber(int range) {
return ((int) (Math.random() * range) + 1) - ((int) (Math.random() * range) + 1);
}