1.性质
1)异或运算:0^N == N N^N == 0
能长时间记住的概率接近0% 所以,异或运算就记成无进位相加运算!
2)异或运算满足交换律和结合率 上面的两个性质用无进位相加来理解就非常的容易
3) 同样一批数子的亦或运算,不管怎么排列亦或,结果相等
2.认识异或运算应用
1)题目一 如何不用额外变量交换两个数独立内存区域的 a,b变量
答 : a =甲 ,b =乙
a =a ^b
b =a^b
a =a^b
分析过程:
a=a^b =甲^乙
b=a^b =甲^乙^乙=甲^(乙^乙)=甲
a=a^b =甲^乙^甲=(甲^甲)^乙=乙
交换结束!!!
注意:如果同一个内存地址的两个引用,不能这样,N^N 就等于0了
2)题目二 一个数组中有一个数出现了奇数次,其他数都出现了偶数次,怎么找到并打印这个数
答:
//只有1出现了基数次 int[] intArray = {1,1,1,2,2,4,4,5,5,6,6,7,7,7,7}; // 找出来 int jishuValue =0; for (int i = 0; i < intArray.length; i++) { jishuValue= jishuValue ^ intArray[i]; } // 分析N^N=0,偶数次N^N ==0 ,0^M=M,所以出现基数次的M就被找出来了 System.out.println("jishuValue = " + jishuValue);//jishuValue = 1
3) 题目三 怎么把一个int类型的数,提取出最右侧的1来
a= 0 1 1 1 0 1 0 1 0 0 0
~a= 1 0 0 0 1 0 1 0 1 1 1 , 取反让1左侧 &的结果为0 ,右侧& 的结果为0,又因为自己1& 0 ==0 ,所以+1处理一下
~a+1= 1 0 0 0 1 0 1 1 0 0 0 a & (~a+1) == 0 0 0 0 0 0 0 1 0 0 0 ==a&(-a),
找出来最右侧的1 了.因为~a+1==-a,所以 a&(~a+1) ==a&(-a)
4) 题目四 一个数组中有两个数出现了奇数次,其他数都出现了偶数次,怎么找到并打印这两个数
public static void main(String[] args) {
int[] intArray = {1, 1, 1, 2, 4, 4, 5, 5, 6, 6, 7, 7, 7, 7};
//step1 找出来1,2 的亦或
int jishuEor = 0;
for (int i = 0; i < intArray.length; i++) {
jishuEor = jishuEor ^ intArray[i];
}
// 此时的jishuEor == 1^2
// 找出基数1,推出基数2
// step2 找出1: 因为1,2 肯定不相等 ==> jishuEor != 0 ==> jishuEor 的二进制数中肯定有1 ,找到最右侧的1,
// 通过整个1的位置,将数组分成这个位置为1 的 和不为1 的,对为1的进行单独的亦或。为1 的这一组,因为对应分到的这部分偶数项亦或也为0,
// 就剩下单独的一个基数了 ,比如是1, 根据 1^(1^2) ==>2 的值
int rightest1 = jishuEor ^ (-jishuEor);
int eOr = 0;
for (int i = 0; i < intArray.length; i++) {
if ((rightest1 & intArray[i]) != 0) {
eOr = eOr ^ intArray[i];
}
}
// 此时eOr为一个基数
int secondJishu = eOr ^ jishuEor;
System.out.println("基数1 = " + eOr);
System.out.println("基数2 = " + secondJishu);
}
5 )题目5 一个数组中有一个数出现K次,其他数都出现了M次, M > 1, K < M 找到,出现了K次的数, 要求,额外空间复杂度O(1),时间复杂度O(N)
思路:
已知 int [] arr, 出现k次的值设为x,其他数都出现m次,k<m,m>1,求x
因为:每个int 类型数,都是32位的二进制数,可以将其转换为32位数长度的整数数组;
所以,
1. 可以将数组中所有的数按二进制位对应位置出现1的次数统计,比如在0位置出现了
5次,1位置出现了4次......就能整理出一个32位的二进制数,记作w。 w的二进制每一位代表着数组arr中在这个位置上出现1的次数之和。
2. 这个和可能全部由m次的数组成,也可能由出现k次的数全部组成,也可能由k次,m次的数一起组成。但是由于k<m, 因此 这个数%m ,如果值 !=0 ,说明有余数,表示 x在这个位置上一定是1。同理只要在每个位置上都取模遍历一遍,就能得到每个位置是0还是1,也就能得到这个x。
package com.dchealth.test;
import java.util.HashMap;
import java.util.HashSet;
public class Test {
//step1.把数组中的每一个元素转换为32位二进制的数组,数组中32位的每一位,对应累加
//step2. 32位数组中每一位 % m,如果不为0,表示出现k次的那个元素在该位置上有加1,也就是说出现k次的元素,转换位32位的二进制后在该位置上为1
//step3,得到求值
public static void main(String[] args) {
// 遍历次数
int times = 10000;
// 元素范围 [-30,30]
int numRange = 30;
// 数组arr中同一种元素最多出现的次数
int oneElementMaxFindTimes = 10;
// 数组中元素的种类数
int maxKind = 10;
int a = (int) (Math.random() * oneElementMaxFindTimes) + 1;
int b = (int) (Math.random() * oneElementMaxFindTimes) + 1;
int k = Math.min(a, b);
int m = Math.max(a, b);
if (k == m) {
m++;
}
System.out.println("开始!");
for (int i = 0; i < times; i++) {
// 构建数组arr
int[] arr = buildArray(numRange, k, m, maxKind);
if (getKTimesNumMethod2(arr, k, m) != getKTimesNumMethod1(arr, k, m)) {
System.out.println(" 出错了!");
for (int j = 0; j < arr.length; j++) {
System.out.print(arr[j] + " ");
}
System.out.println(" ");
System.out.println("k = " + k);
System.out.println("m = " + m);
return;
}
}
System.out.println("结束!");
}
private static int[] buildArray(int numRange, int k, int m, int kind) {
//数组长度(至少大于等于两种)
int elementKindsNum = (int) (Math.random() * kind) + 2;
int len = k * 1 + (elementKindsNum - 1) * m;
int[] arr = new int[len];
// 数组中添加出现k次的元素
int elementOfFindKTimes = buildRandomNumOfInRange(numRange);
int index = 0;
for (index = 0; index < k; index++) {
arr[index] = elementOfFindKTimes;
}
elementKindsNum--;
HashSet<Integer> set = new HashSet<>();
set.add(elementOfFindKTimes);
// 数组中添加出现m次的元素
while (elementKindsNum != 0) {
//构建元素 && 添加m次
int currentNum = 0;
do {
currentNum = buildRandomNumOfInRange(numRange);
} while (set.contains(currentNum));
set.add(currentNum);
elementKindsNum--;
for (int i = 0; i < m; i++) {
arr[index++] = currentNum;
}
}
// 打乱有序的arr
for (int i = 0; i < arr.length; i++) {
int j = (int) (Math.random() * arr.length);// 0 ~ N-1
int tmp = arr[i];
arr[i] = arr[j];
arr[j] = tmp;
}
return arr;
}
public static int buildRandomNumOfInRange(int range) {
return (int) (Math.random() * (range + 1)) - (int) (Math.random() * (range + 1));
}
public static int getKTimesNumMethod2(int[] arr, int k, int m) {
if (arr == null || arr.length == 0) {
return -1;
}
int[] t = new int[32];
for (int num : arr) {
for (int i = 0; i < 32; i++) {
t[i] += (num >> i) & 1;
}
}
int res = 0;
for (int i = 0; i < 32; i++) {
if (t[i] % m != 0) {
res |= (1 << i);
}
}
return res;
}
public static int getKTimesNumMethod1(int[] arr, int k, int m) {
if (arr == null || arr.length == 0) {
return -1;
}
HashMap<Integer, Integer> map = new HashMap<>();
for (int num : arr) {
if (map.get(num) != null) {
map.put(num, map.get(num) + 1);
} else {
map.put(num, 1);
}
}
for (int num : arr) {
if (map.get(num) == k) {
return num;
}
}
return -1;
}
}