一、认识异或运算
? ? ? ? 异或运算是算法中非常简单的一种运算,原理其实就是进行位运算,是一种无进位相加,基本所有对异或运算的应用都可以总结为异或运算的俩个性质,异或运算的性质:
1.0^N = N? ;?N^N = 0。
2.异或运算满足交换律和结合率
二、实例解析
????1.如何不用额外变量交换两个数
public static void test03(int num1, int num2){
//必须保证指向的内存是相同的
num1 = num1 ^ num2;
num2 = num1 ^ num2;// = num1 ^ num2 ^ num2 = num1 ^ 0 = num1;
num1 = num1 ^ num2;// = num1 ^ num2 ^ num1 = num2 ^ 0 = num2;
System.out.println(num1);
System.out.println(num2);
}
? ? 2.一个数组中有一种数出现了奇数次,其他数都出现了偶数次,怎么找到并打印这种数?
/**
* @Author zhangchunming
* @Description //TODO 一个数值中有一个数出现奇数次,其他数出现偶数次,如何找出它
* @Date 23:44 2021/4/28
* @Param [arr1]
* @return int
**/
public static int test01(int[] arr1){
//循环异或,最终留下的就是奇数
int ero=0;
for(int i=0;i<arr1.length;i++){
ero=ero^arr1[i];
}
return ero;
}
? ? 3.怎么把一个int类型的数,提取出最右侧的1来(就是将值转换为二进制,查看最右侧的1)
? ? ? ? 思路:这个思路在后边的题中经常使用。取反加一之后的值,除了最右侧的1,其他相反状态
/**
* @Author zhangchunming
* @Description //TODO 如何把一个整形的数,提取出最右侧的1(二进制中)
* @Date 22:49 2021/5/7
* @Param [num]
* @return int
**/
public static int test02(int num){
//取反加一之后的值,除了最右侧的1,其他相反状态
int num1=~num+1;
System.out.println(num1);//101
return num1 & num;
}
? ? 4.一个数组中有两种数出现了奇数次,其他数都出现了偶数次,怎么找到并打印这两种数
? ? ? ? 思路:在数组中a、b出现奇数次,那么将数组整体进行异或操作得出eor=a^b,找到eor中最左位置的1,假设此位置为rightOne,说明在此位置时a、b的值不相等,因为如果相等的话异或为0,此时可以将数组分为俩类,在rightOne这个位置上,一类数为0,一类数为1,这俩类分别包含a和b。此时只需要异或其中一类就可以,就可以找到a,之后将a和eor进行异或得到b(满足交换律)。
/**
* @Author zhangchunming
* @Description //TODO 一个数组中有两种数出现了奇数次,其他数都出现了偶数次,怎么找到并打印这两种数
* @Date 17:35 2021/8/18
**/
public static void printOddTimesNum2(int[] arr) {
int eor = 0;
// eor = a ^ b
for (int i = 0; i < arr.length; i++) {
eor ^= arr[i];
}
// 提取出最右的1
// 这个值类似于:0000 0000 0010,只有一个1其他都为0
int rightOne = eor & (~eor + 1);
int onlyOne = 0; // eor'
for (int i = 0 ; i < arr.length;i++) {
//举例:arr[1] = 1111 1111 1101
// rightOne = 0000 0000 0010
// 运算结果: 0000 0000 0000 = 0
// 下边也可以写为 if ((arr[i] & rightOne) != rightOne) {
if ((arr[i] & rightOne) != 0) {
//将所有在rightOne 位置为0的值进行异或,得到其中一个奇数贼
onlyOne ^= arr[i];
}
}
System.out.println(onlyOne + " " + (eor ^ onlyOne));
}
? ? ?
|