异或运算 ^:相同为0,不同为1【无进位相加】
与运算 &:相同为1,不同为0
或运算 |:有1为1
一个数对0进行异或,则结果为自身
如: 0 ^ 5 = 5. 0 ^ 1 = 1
0000 ^ 0101 = 0101
一个数对自己进行异或,则结果为0
如:5^5 = 0
0101 ^ 0101 = 0000
a^b^c = a^(b^c) = a^c^b
结合使用比如:
3^5^2^5^3 = (3^3)^(5^5)^2 = 0^0^2 = 2
- // a 值为 甲, b值为 乙
- a = a^b // a= 甲^乙, b= 乙
- b = a^b // a= 甲^乙, b= 甲^乙^乙 = 甲
- a = a^b // a= 甲^乙^甲 = 乙, b= 甲
示例:
- public static void main(String[] args) {
- int[] arr = {4,7,244,5,2};
- int i= 1,j=2;
- swap(arr,i,j);
- System.out.println(arr[i]+","+arr[j]);
- }
-
- //i 与 j 不能相同
- //对于引用来说,如果两个指针的地址指向同一个内存地址,那两个相同异或则会为0
- public static void swap(int[] arr,int i,int j){
- arr[i] = arr[i] ^ arr[j];
- arr[j] = arr[i] ^ arr[j];
- arr[i] = arr[i] ^ arr[j];
- }
-
-
- //结果
- 244,7
思路一:
使用hashmap,遍历数组,key为数组值,value为计数,最后遍历hashmap,找到基数value,返回key为该数。
思路二:
相同数进行异或值为0,0与其他数异或值为其他数。
定一个变量 eor = 0,分别与数组中每一个数进行异或
相当于:
0 ^ arr[0] ^ arr[1] ... arr[n-1]
由于数组是特殊的,只有一种数出现基数次,其余为偶数次,那么偶数次相同数进行异或都为0,最后剩下一个基数次的数
- public static void printOddTimesNum1(int[] arr){
- int eor = 0;
- for(int a:arr){
- eor ^= a;
- }
- System.out.println(eor);
- }
- public static void main(String[] args) {
- int [] arr = {1,4,3,4,2,1,3};
- printOddTimesNum1(arr);
- }
-
-
- //结果
- 2
思路: a & (-a)
与&运行:相同为1
-a 在二进制中表示为 取反 + 1
- a = 00110101000
- # -a = a取反加1
- -a = ~a + 1
- -a = 11001010111 + 1 = 11001011000
-
- 所以
- a = 00110101000
- -a = 11001011000
- a & -a = 00000001000
1.
首先参照2,先将偶数次的数消掉,即 eor = eor ^ arr,之后得到的就是两个基数进行异或的结果,假设这两个出现基数的数为a和b,这eor = a^b,并且因为a 不等于 b,所以eor也不等于0
2.
此时,eor为 a ^b,由于a 不等于b,观察eor的二进制形式,其中有1的位一定为a和b不相同的位置【因为异或为不同位为1,相同位为0】,指定最右侧为1位作为分界线,计算得rightOne,
假设最右侧位为第3位,则数组中的数可以分为两类,一类位第3位为1,记为左侧,一类位第3为为0,记为右侧,且a与b一定不在同一侧,假设a在左侧,b在右侧
3.
接着,即eor2 = 0,异或左侧数据,则得到 a,此时eor2 = a【左侧中只有一个是基数项为a,其他偶数项被消掉 】
4.
得到a后,b = eor ^ eor2[此时为eor = a ^b, eor2 = a,b = a^b ^a =b]
arr = [6,10,4,4,12,12,12,12,3,3] //此时出现基数次的为6,10
6 = 00110
10= 01010
eor = 6 ^ 10 = 01100
得到最右侧为1的数为00100
划分左侧即第三位为1的数有6,4,12,第三位为0的数有10,3
eor2 = 0 ^ 6 ^4^4^12^12^12^12 = 6
b = eor ^ eor2 = 6^10^6 = 10
得到两个基数次的数为6和10
- public static void printOddTimesNum2(int[] arr) {
- int eor = 0;
- for(int x:arr){
- eor ^= x;
- }
- //此时eor = a ^ b
- //最右侧为1
- int rightOne = eor & -eor;
- int eor2 = 0;
-
- //查找ringthOne位为1的进行异或操作,得到a
- for (int x : arr){
- if((x & rightOne) != 0){
- eor2 ^= x;
- }
- }
-
- int a = eor2;
- int b = eor ^ eor2;
- System.out.println(a+","+b);
- }
-
- public static void main(String[] args) {
-
- int [] arr1 = {6,10,12,12,12,12,4,4,3,3};
- printOddTimesNum2(arr1);
- }
-
- //结果
- //6,10
一个数组中有一种数出现K次,其他数都出现了M次,
M > 1, K < M
找到,出现了K次的数,
要求,额外空间复杂度O(1),时间复杂度O(N)
1.
准备一个长度为32的整形数组 t,遍历arr,将每一个元素的所以位为1的位置统计到t中,如元素a的第3位为1,则t[2]++,第9位为1,则t[8]++
即
t[0] 0位置的1出现了几个
t[i] i位置的1出现了几个
2.
对t数组每一个元素,如果对 M取模不为0,则说明出现k次的数在该为为1【因为如果该位能被7整除,说明该位的1出现次数为整数次】
设置一个变量ans = 0,遍历t,如果元素对M取模不为0,t[i] %M != 0,则 ans的第i位设置为1
3.
如何将第i位设置到ans上呢
将1左移i位,则i位置位1,其余位置为0,再 或运算到ans上,i位置变为1,其余位置保持不变
- public static void km1(int [] arr,int k,int m){
- int [] t = new int[32];
- //遍历arr数组
- for (int x : arr){
- //对每一元素的每一位检查,如果为1则t[i]计数增加1
- for(int i=0;i<32;i++){
- //x右移i位则将待检查位置移动到最右侧,与1进行与运算,如果该位置为1,则结果为1,如果该位置为0则结果为0
- if((x >> i & 1) != 0){
- t[i]++;
- }
- }
- }
- int ans = 0;
- //对t数组计算
- for (int i = 0; i < 32; i++) {
- //如果出现次数无法被m整除,说明出现k次的数在该位出现
- if(t[i] % m != 0){
- //则将该位记录,最终32位统计完成后,则为该数
- // 1 左移i位添加到ans中
- ans |= 1 << i;
- }
- }
- System.out.println(ans);
- }
-
- public static void main(String[] args) {
- //k = 2,m = 4
- int [] arr = {6,6,10,10,10,10,7,7,7,7,11,11,11,11,0,0,0,0,-1,-1,-1,-1};
- km1(arr,2,4);
- }
-
- //结果
- // 6