位運算子
&
:與
|
:或
^
:異或
~
:非(取反)
>>
<<
:右移(補符號位),左移(補0)
>>>
:右移(0補充高位)
對於int型,1<<35與1<<3是相同的,而左邊的運算元是long型時需要對右側運算元模64
異或:
可以理解為不進位加法:1+1=0,0+0=0,1+0=1
性質:
1、交換律:a ^ b = b ^ a
2、結合律:(a ^ b) ^ c = a ^ (b ^ c)
3、對於任何數x,都有x ^ x = 0,x ^ 0 = x
4、自反性:a ^ b ^ b = a ^ 0 = a,連續和同一個因子做異或運算,最終結果為自己
應用
判斷奇偶數
x & 1 = 1
x是奇數
x & 1 = 0
x是偶數
核心是判斷二進位制數最後一位是1還是0
交換兩個整數變數的值
使用前提:a,b不相等
a = a^b;
b = a^b;
a = a^b;
不用判斷語句求整數絕對值
(num^(num>>31))+(num>>>31)
11111111 11111111 11111111 11110111:num
11111111 11111111 11111111 11111111:num>>31
00000000 00000000 00000000 00001000:num^(num>>31)
00000000 00000000 00000000 00000001:num>>>31
00000000 00000000 00000000 00001001:num^(num>>31))+(num>>>31
正數顯然滿足
負數的相反數是原碼含符號位取反加一
消除最低位的1
x&(x-1)
:可以使二進位制數x的最低位的1變成0;
例題
找出唯一成對的數
1-1000這1000個數放在含有1001個元素的陣列中,只有唯一的一個元素值重複,其它均只出現一次。每個陣列元素只能訪問一次,設計一個演算法,將它找出來;不用輔助儲存空間,能否設計一個演算法實現?
核心:x^x=0
可以強行配湊消去其他元素:這1001個元素連續異或,再異或上1-1000這1000個數。
其他元素都出現2次,異或後變成0.而唯一的一個元素值出現3次,連續異或後值不變。
public static void main(String[] args) {
int []arr = new int[]{1,2,3,4,4,5};
int ans = 0;
for(int i=1;i<=5;i++){
ans^=i;
}
for(int i=0;i<6;i++){
ans^=arr[i];
}
System.out.println(ans);
}
找出落單的數
一個陣列裡除了某一個數字之外,其他的數字都出現了兩次。請寫程式找出這個只出現一次的數字。
public static void main(String[] args) {
int []arr = new int[]{1,1,3,4,4};
int ans = 0;
for(int i=0;i<arr.length;i++){
ans^=arr[i];
}
System.out.println(ans);
}
二進位制中1的個數
請實現一個函式,輸入一個整數,輸出該整數二進位制表示中1的個數。
例:9的二進位制表示為1001,有2位是1
法一:
該二進位制數不斷無符號右移並與1做與運算,結果為1則count++,直到該數變為0
public static void main(String[] args) {
int n=-1;
int count = 0;
while (n!=0){
int tmp = n&1;
if(tmp == 1) count++;
n = n >>> 1 ;
}
System.out.println(count);
} //32
法二:
每次使該二進位制數的一個為1的二進位制位變成0,count++,直到該數變成0;
x&(x-1)
:可以使二進位制數的最低位的1變成0;
public static void main(String[] args) {
int n=-1;
int count = 0;
while (n!=0){
n = n&(n-1);
count++;
}
System.out.println(count);
}
是不是2的整數次方
用一條語句判斷一個整數是不是2的整數次方
也就是一個整數的二進位制形式中為1的二進位制位是不是隻有一個。聯絡上一題,也就是count是不是為1
public static void main(String[] args) {
int n=65536;
System.out.println((n&(n-1))==0);
}
整數的二進位制奇偶位交換
先取出偶數位和奇數位,偶數結果右移一位,奇數結果左移一位,最後異或
abababab abababab abababab abababab
a0a0a0a0 a0a0a0a0 a0a0a0a0 a0a0a0a0(ou)
0b0b0b0b 0b0b0b0b 0b0b0b0b 0b0b0b0b(ji)
0a0a0a0a 0a0a0a0a 0a0a0a0a 0a0a0a0a(ou>>>1)
b0b0b0b0 b0b0b0b0 b0b0b0b0 b0b0b0b0(ji<<1)
babababa babababa babababa babababa
public static void main(String[] args) {
int a = 0b01000000_00000000_00000000_00000000;
System.out.println(a);
int b = m(a);
System.out.println(b); //0b10000000_00000000_00000000_00000000
}
private static int m(int i) {
int ou = i & 0xaaaaaaaa;//和1010 1010 1010 。。。。做與運算取出偶數位
int ji = i & 0x55555555;//和0101 0101 0101 。。。。做與運算取出奇數位
return (ou >>> 1) ^ (ji << 1); // 連起來
}
0~1直接的浮點實數的二進位制表示
給定一個介於0和1之間的實數,(如0.625),型別為double,列印它的二進位制表示(0.101,因為小數點後的二進位制分別表示0.5,0.25.0.125......) 。
如果該數字無法精確地用32位以內的二進位制表示,則列印“ERROR”
public static void main(String[] args) {
double num = 0.625;
StringBuilder sb = new StringBuilder("0.");
while (num > 0) {
//乘2:挪整
double r = num * 2;
//判斷整數部分
if (r >= 1) {
sb.append("1");
//消掉整數部分
num = r - 1;
} else {
sb.append("0");
num = r;
}
if (sb.length() > 34) {
System.out.println("ERROR");
return;
}
}
System.out.println(sb.toString());
}
出現k次與出現1次
陣列中只有一個數出現了1次,其他的數都出現了k次,請輸出只出現了1次的數。
2個相同的2進位制數做不進位加法,結果為0
10個相同的10進位制數做不進位加法,結果為O
k個相同的k進位制數做不進位加法,結果為0
public static void main(String[] args) {
int[] arr = {2, 2, 2, 9, 7, 7, 7, 3, 3, 3, 6, 6, 6, 0, 0, 0};
int len = arr.length;
char[][] kRadix = new char[len][];
int k = 3;
int maxLen = 0;
//轉成k進位制字元陣列
//對於每個數字
for (int i = 0; i < len; i++) {
//求每個數字的三進位制字串並翻轉,然後轉為字元陣列
kRadix[i] = new StringBuilder(Integer.toString(arr[i], k)).reverse().toString().toCharArray();
if (kRadix[i].length > maxLen)
maxLen = kRadix[i].length;
}
//不進位加法
int[] resArr = new int[maxLen];
for (int i = 0; i < len; i++) {
// 不進位加法
for (int j = 0; j < maxLen; j++) {
if (j >= kRadix[i].length)
resArr[j] += 0;
else
resArr[j] += (kRadix[i][j] - '0');
}
}
int res = 0;
for (int i = 0; i < maxLen; i++) {
res += (resArr[i] % k) * (int) (Math.pow(k, i));// 8%3=2,
}
System.out.println(res);
}