前言
位运算 异或(^)蛮有意思的,分享一下。
一、题目
1-1000这1000个数放在含有1001个元素的数组中,只有唯一的一个元素值重复,其它均只出现一次。
每个数组元素只能访问一次,设计一个算法,将它找出来。
不用辅助存储空间,能否设计一个算法实现?
提示:异或( ^ )可以去除两个重复的值。
二、题解
1.奇巧淫技
利用异或( ^ ) 可以去重这一特点。a异或a等于0,a异或0等于a(101异或101等于0)例如,A ^ A ^ B ^ C ^ C等于B,因为A^A等于0,消除重复了。
解释:
先将 1 -1000的每一个数都 异或( ^ ) 一次。如果此时再将1-1000异或一次,那么值则为0了。
因为在数值中,有1001个数,中间有两个数是相同的。因为第一个for循环 异或^ 了1-1000,此时再次for循环遍历数组中所有数,同时 异或^ 所有数,那么重复的那个数字将会被保留下来,所以那个数字即为重复的数。(两个for循环中,重复的数字一共出现了3次,其他数只出现了两次)
使用异或^。时间复杂度为O(n)。
public static int solution(int[] arr) {
int x = 0;
// 先将1-1000异或一次,1^2^3^...^1000。
for (int i = 1; i <= 1000; i++) {
x = x ^ i;
}
// 再将1-1000异或一次,但是这里包含了一个重复的数(这个重复的数总共会被异或^三次,从而保留下来)。
for (int i = 0; i < arr.length; i++) {
x = x ^ arr[i];
}
return x;
}
测试数据main方法
public static void main(String[] args) {
// 随便生成的测试数据(数据可能是乱序的,我这里只是为了方便)
int[] arr = new int[1001];
for (int i = 0; i < 1000; i++) {
arr[i] = i + 1;
}
arr[1000] = (int)(Math.random() * (1000+1-1))+1; // 随机生成[1-1000]内的数
// 测试
System.out.print(solution(arr));
}
2.其他解法
使用辅助空间
新建了一个HashSet。使用 Set 不能添加重复的特点,如果找到重复的了就返回那个值。
public static int solution2(int[] arr) {
HashSet<Integer> set = new HashSet<>();
for (int i = 0; i < arr.length; i++) {
boolean add = set.add(arr[i]);
if (!add) {
return arr[i];
}
}
throw new IllegalArgumentException("数组中没有两个相同的数!");
}
新建了一个数组。因为值的范围在 1-1000,所以用值来作为新数组的下标,将值+1。
第二个for循环遍历,找出被值为2的下标,即重复出现的数。
public static int solution3(int[] arr) {
int[] newArr = new int[1001];
for (int i = 0; i < arr.length; i++) {
int value = arr[i];
newArr[value]++;
}
for (int i = 0; i < newArr.length; i++) {
if (newArr[i] == 2) {
return i;
}
}
throw new IllegalArgumentException("数组中没有两个相同的数!");
}
不使用辅助空间
暴力破解。时间复杂度有点高,为O(n2)
每找一个数时,判断这个数在数组中是否存在,存在则直接返回。
public static int solution4(int[] arr) {
for (int i = 0; i < arr.length; i++) {
for (int j = i+1; j < arr.length; j++) {
if (arr[i] == arr[j]) {
return arr[i];
}
}
}
throw new IllegalArgumentException("数组中没有两个相同的数!");
}
相关
更多奇淫巧技的题解请点击:【奇淫巧技】题解集