能夠這樣說,位運算是咱們剛開始學計算機就會接觸到的一種東西。那麼位運算這麼常見,咱們是否可使用它來作一些騷操做呢?html
使用的運算符包括下面(java還有一個>>>
無符號右移):java
含義 | 運算符 | 例子 |
---|---|---|
左移(後面補0) | << | 0011 => 0110 |
右移(正數前面補0,負數補1) | >> | 0110 => 0011 |
按位或 | ︳ | 0011 ------- => 1011 1011 |
按位與 | & | 0011 ------- => 1011 1011 |
按位取反 | ~ | 0011 => 1100 |
按位異或 (相同爲0不一樣爲1) | ^ | 0011 ------- => 1000 1011 |
左移右移運算
右移至關因而除,左移至關於就是乘,左移一位乘以2,左移二位乘以4,依此類推.不管正數、負數,它們的右移、左移、無符號右移32位都是其自己,算法
加法有兩個操做:求和
與進位
ide
求和:1+1=0 1+0=1 0+0=0函數
異或:1^1=0 1^0=1 0^0=0學習
進位:1+1=1 1+0=0 0+0=0ui
位與:1&1=1 1&0=0 0&0=0spa
那麼這時候,咱們使用位運算實現加法確定是使用異或
和位與
code
遞歸版本htm
// 使用遞歸實現
int add(int a,int b){
// 假如進位爲0
if(b ==0){
return a;
}
// 得到求和位
int s = a^b;
// 得到進位,而後須要向作移動一位表示進位
int c = ((a&b)<<1);
return add(s,c);
}
非遞歸版本
非遞歸版本和遞歸版本沒什麼不同的
int add2(int a,int b){
int s;
int c;
while(b != 0){
// 得到求和位
s = (a^b);
// 得到進位,而後須要向作移動一位表示進位
c = ((a&b)<<1);
a = s;
b = c;
}
return a;
}
減法,emm,簡單點來講,就是使用加法來作的,只是說加了一個負數而已
首先咱們得先將被減數取反(a取反就是~a+1
)
int adverse(int a){
return add(~a,1);
}
減法的徹底代碼
// 取得相反數
int adverse(int a){
return add(~a,1);
}
// 減法函數 其中add就是前面的加法函數
int subtract(int a,int b){
return add(a,adverse(b));
}
說乘法的前面咱們先考慮下符號問題。
首先呢,咱們得知道一個數是正數仍是負數
// 負數返回-1,正數返回0
int getsign(int i){
return (i >> 31);
}
若是爲負數,則進行取反
// 若是爲負數,則進行取反
int toPositive(int a) {
if (a >> 31 == -1)
// 進行取反
return add(~a, 1);
else
return a;
}
解決方法一:
乘法,咱們小時候學習乘法,老師告訴咱們,乘法就是累加,例如6*7就是7個6相加而已,so,咱們確定可使用加法來實現乘法。時間複雜度O(n)。
int multiply(int a, int b){
Boolean flag = true;
// 若是相乘爲正數,flag爲false
if (getsign(a) == getsign(b))
flag = false;
// 將a取正數
a = toPositive(a);
b = toPositive(b);
int re = 0;
while (b!=0) {
// 相加
re = add(re, a);
// b進行次數減一
b = subtract(b, 1);
}
// 假如結果是負數,則進行取反
if (flag)
re = adverse(re);
return re;
}
解決方法二
首先先看一張圖吧,這張圖是我用筆記本的觸摸板畫的,難看了一點(en,我本身都受不了的難看),上面畫的是6*2的二進制相乘。
上面咱們能夠發現,相乘有點相似與&
,只不過它會進位而已。那麼咱們的問題就能夠簡單化了。相與,而後進位便可,而後再相加。換個方向想:a向左移動,b向右移動,若是b的最後一位爲1,則能夠將a加上。(時間複雜度O(logn))
int multiply2(int a, int b) {
Boolean flag = true;
// 若是相乘爲正數,flag爲false
if (getsign(a) == getsign(b))
flag = false;
// 將a取正數
a = toPositive(a);
b = toPositive(b);
int re = 0;
while (b!=0) {
// 假如b的最後一位爲1
if((b&1) == 1){
// 相加
re = add(re, a);
}
b = (b>>1);
a = (a<<1);
}
// 假如結果是負數
if (flag)
re = adverse(re);
return re;
}
除法和乘法相似。
解決方法一
除法解決方法一和乘法的解決方法一相似,從被除數上面減去除數,直到不夠減了,才罷休。時間複雜度O(n)。
解決方法二
解決方法二的思路是這樣的:從b*(2**i)開始減
(2**i表明2的i次方),而後一直減到b*(2**0)
。這樣的解決方法的時間複雜度是O(logn)。
// a/b
int divide(int a,int b){
Boolean flag = true;
// 若是相除爲正數,flag爲false
if (getsign(a) == getsign(b))
flag = false;
// 將a取正數
a = toPositive(a);
b = toPositive(b);
int re = 0;
int i = 31;
while(i>=0){
// 若是夠減
// 不用(b<<i)<a是爲了防止溢出
if((a>>i)>=b){
// re表明結果
re = add(re, 1<<i);
a = subtract(a, (b<<i));
}
// i減一
i = subtract(i, 1);
}
// 假如結果是負數
if (flag)
re = adverse(re);
return re;
}
ok,今天的位運算四則運算就到這裏了,在這裏我都沒有考慮到數據溢出的狀況,由於重點不在這裏,下次的博客我將會說一下位運算在算法中的一些騷操做。