常見的位操作及其應用
阿新 • • 發佈:2020-10-27
# 概述
與、或、異或、取反或者移位運算這幾種基本的位操作想必諸位讀者並不陌生,如果我們能在某些合適場景下使用位運算,有些時候可以大大提高演算法的效率。但由於本身位運算太過靈活,甚至某些技巧比較苦澀難懂,因而,本篇文章主要介紹幾種常見的或者有趣的位操作,並且給出一些用到這些技巧的演算法題目,便於讀者練習。
# 有趣的操作
## 1. 大小寫字母轉換
1. 利用`或操作`和空格將英文字母轉成小寫
```java
('a' | ' ') = 'a';
('A' | ' ') = 'a';
```
2. 利用與運算`&`和下劃線將英文字元轉換成大寫
```java
('b' & '_') = 'B';
('B' & '_') = 'B';
```
3. 利用異或運算`^`和空格進行英文字元大小寫互換
```java
('d' ^ ' ') = 'D';
('D' ^ ' ') = 'd'
```
**常用指數**: :six_pointed_star::six_pointed_star:
**容易指數:**:six_pointed_star::six_pointed_star::six_pointed_star:
**PS**:上述技巧能夠產生奇特效果的原因在於字元型別的資料都是通過ASCII進行編碼的,字元本身其實就是數字,而剛好這些字元對應的數字通過位運算子就可以得到正確結果,此處就不展開來說了。
## 2. 判斷兩個數是否異號
```java
int x = -1, y = 2;
boolean f = ((x ^ y) < 0); // true 兩個int型別資料進行異或運算小於零證明異號
int x = 1, y = 2;
boolean f = ((x ^ y) < 0); // false 兩個int型別資料進行疑惑運算大於零證明同好
```
**常用指數**::six_pointed_star::six_pointed_star::six_pointed_star:
**困難指數**::six_pointed_star:
**PS:**這個操作在我們判斷兩個數異號的時候**非常有用**,一方面運算效率較高,另一方面可以減少if else 分支的使用。其背後的原理主要是一個正數補碼的符號位和一個負數補碼的符號位肯定想法,經過疑惑運算後,最後符號位結果肯定是`1`(代表負數)。
## 3. 移除最後一位"1"
```java
byte n = 10;
// n的二進位制表示為: 0 0 0 0 1 0 1 0
// 異或運算 ^
//n-1的二進位制表示為:0 0 0 0 1 0 0 1
n & (n-1); //結果為:0 0 0 0 1 0 0 0
```
![image-20201027142445416](https://gitee.com/runWithHappy/images/raw/master/20201027142521.png)
根據上邊的註釋以及示意圖,整個操作過程應該不難理解
簡單來說`n & (n -1 )`**主要作用:**就是**消除數字n的二進位制表示中的最後一個1.**
**常用指數**::six_pointed_star::six_pointed_star::six_pointed_star::six_pointed_star::six_pointed_star:
**困難指數**::six_pointed_star::six_pointed_star:
**PS:**這個操作特別常用,在好多leetcode題目中都有涉及,比如用來判斷一個數的二進位制數中1的個數。
## 4. 獲取最後一個1
```java
byte n = 10;
//結果為:0 0 0 0 0 0 1 0
(n & (n-1)) ^ n;
```
**常用指數**::six_pointed_star::six_pointed_star::six_pointed_star::six_pointed_star:
**困難指數**::six_pointed_star::six_pointed_star::six_pointed_star:
**PS:**該操作剛好和[操作3](# 3. 移除最後一位"1")相反,**主要是為了獲取數字n的二進位制表示中的最後一個1**.該操作通常會用在位標記的時候使用(可參考[漢明距離](#2. 漢明距離))。
## 5. 異或運算的簡單性質
```java
a=0^a=a^0
0=a^a
```
**常用指數**::six_pointed_star::six_pointed_star::six_pointed_star::six_pointed_star:
**困難指數**::six_pointed_star:
**PS:**這個性質在我們判斷兩個數是否相同的時候**非常常用。**
# 應用
前邊總結了那麼多常用的位操作,下邊來做幾道題消化吸收一下剛剛所學的知識。
## 1. 只出現一次的數字
![image-20201027144825355](https://gitee.com/runWithHappy/images/raw/master/20201027144827.png)
這道題比較簡單,說實話即使不用位運算我們也可以有好多種方法求解,比如可以用一個Map來對元素進行boolean標記來求解。但如果我們此處能想到用**異或運算的性質**,那這道題目我們便可以簡單優雅的解出來。
**思路:**用一個初值為0的變數不斷和陣列中的元素進行異或運算,最終得到的變數值便是最終結果。
**原因:**因為0和任何一個元素進行異或運算都是0,而任何兩個相同元素進行異或之後的結果都是0,而題目中只有一個數是單獨存在的,其他數都是2個,因而不斷進行異或運算最後的結果必然是那個獨特的數值,也就是最終的結果。
程式碼如下:
```java
public int singleNumber(int[] nums) {
int result = 0;
for (int i =0;i