溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

位運算的技巧有哪些

發(fā)布時間:2021-10-20 09:06:40 來源:億速云 閱讀:135 作者:iii 欄目:編程語言

這篇文章主要介紹“位運算的技巧有哪些”,在日常操作中,相信很多人在位運算的技巧有哪些問題上存在疑惑,小編查閱了各式資料,整理出簡單好用的操作方法,希望對大家解答”位運算的技巧有哪些”的疑惑有所幫助!接下來,請跟著小編一起來學(xué)習(xí)吧!

認識位運算

什么是位運算?

程序中的所有數(shù)在計算機內(nèi)存中都是以二進制的形式儲存的。位運算就是直接對整數(shù)在內(nèi)存中的二進制位進行操作。

位運算就是直接操作二進制數(shù),那么有哪些種類的位運算呢?

常見的運算符有與(&)、或(|)、異或(^)、取反(~)、左移(<<)、右移(>>是帶符號右移 >>>無符號右移動)。下面來細看看每一種位運算的規(guī)則。

位運算 & (與)

規(guī)則:二進制對應(yīng)位兩兩進行邏輯AND運算(只有對應(yīng)位的值都是 1 時結(jié)果才為 1, 否則即為 0)即 0&0=0,0&1=0,1&1=1

例如:2 & -2

位運算的技巧有哪些  

 

位運算 | (或)

規(guī)則:二進制對應(yīng)位兩兩進行邏輯或運算(對應(yīng)位中有一 個為1則為1) 即0|0=0,0|1=1,1|1=1

例如:2 | -2

位運算的技巧有哪些  

 

位運算 ^ (異或)

規(guī)則:二進制對應(yīng)位兩兩進行邏輯XOR (異或) 的運算(當(dāng)對應(yīng)位的值不同時為 1, 否則為 0)即0^0=0, 0^1=1, 1^1=0

例如:2 ^ -2

位運算的技巧有哪些  

 

按位取反~

規(guī)則:二進制的0變成1,1變成0。

位運算的技巧有哪些  

 

移位運算符

左移運算<<:左移后右邊位補 0

右移運算>>:右移后左邊位補原最左位值(可能是0,可能是1)

右移運算>>>:右移后左邊位補 0

  • 對于左移運算符<<沒有懸念右側(cè)填個零無論正負數(shù)相當(dāng)于整個數(shù)乘以2。

  • 而右移運算符就有分歧了,分別是左側(cè)補0>>>和左側(cè)補原始位>>,如果是正數(shù)沒爭議左側(cè)都是補0,達到除以2的效果;如果是負數(shù)的話左側(cè)補0>>>那么數(shù)值的正負會發(fā)生改變,會從一個負數(shù)變成一個相對較大的正數(shù)。而如果是左側(cè)補原始位(負數(shù)補1)>>那么整個數(shù)還是負數(shù),也就是相當(dāng)于除以2的效果。

下面這張圖可以很好的幫助你理解負數(shù)的移位運算符:

位運算的技巧有哪些  

 

到這里,我想你應(yīng)該對位運算有了初步的認識,在這里把上面提到的部分案例執(zhí)行對比一下讓你看一下可能會理解的更清晰:

位運算的技巧有哪些  

 
 

位運算小技巧

在這里有些常用的位運算小技巧。

 

判斷奇偶數(shù)

正常判斷奇數(shù)偶數(shù)的時候我們會這樣寫:

if( n % 2 == 1)
    // n 是個奇數(shù)
}
 

使用位運算可以這么寫:

if(n & 1 == 1){
    // n 是個奇數(shù)。
}
 

其核心就是判斷二進制的最后一位是否為1,如果為1那么結(jié)果加上2^0=1一定是個奇數(shù),否則就是個偶數(shù)。

 

交換兩個數(shù)

對于傳統(tǒng)的交換兩個數(shù),我們需要使用一個變量來輔助完成操作,可能會是這樣:

int team = a;
a = b;
b = team;
 

但是使用位運算可以不需要借助額外空間完成數(shù)值交換:

a=a^b;//a=a^b
b=a^b;//b=(a^b)^b=a^0=a
a=a^b;//a=(a^b)^(a^b^b)=0^b=0
 

原理已經(jīng)寫在注釋里面了,是不是感覺非常diao呢?

 

二進制枚舉

在遇到子集問題的處理時候,我們有時候會借助二進制枚舉來遍歷各種狀態(tài)(效率大于dfs回溯)。這種就屬于排列組合的問題了,對于每個物品(位置)來說,就是使用和不使用的兩個狀態(tài),而在二進制中剛好可以用1和0來表示。而在實現(xiàn)上,通過枚舉數(shù)字范圍分析每個二進制數(shù)字各符號位上的特征進行計算求解操作即可。

位運算的技巧有哪些  

 

二進制枚舉的代碼實現(xiàn)為:

for(int i = 0; i < (1<<n); i++) //從0~2^n-1個狀態(tài)
{
  for(int j = 0; j < n; j++) //遍歷二進制的每一位 共n位
  {
    if(i & (1 << j))//判斷二進制數(shù)字i的第j位是否存在
    {
      //操作或者輸出
    }
  }
}
   

位運算經(jīng)典問題

有了上面的位運算基礎(chǔ),我們怎么用位運算處理實際問題呢?或者有哪些經(jīng)典的問題可以用位運算來解決呢。

 

不用加減乘除做加法

題目描述

寫一個函數(shù),求兩個整數(shù)之和,要求在函數(shù)體內(nèi)不得使用+、-、*、/四則運算符號。

分析:這道題咋一聽可能沒啥思路,簡單研究一下位運算還是能獨立推出來和理解的。

當(dāng)然,解決這題前,需要了解上面的四種位運算。還要知道二進制的運算:0+0=0,0+1=1,1+1=0(進位)

對于加法的一個二進制運算。如果不進位那么就是非常容易的。這時候相同位都為0則為0,0和1則為1.滿足這種運算的異或(不相同取1,相同取0)和或(有一個1則為1)都能滿足.位運算的技巧有哪些但事實肯定有進位的運算?。】吹缴厦娌僮鞯牟蛔阒?,我們肯定還需要解決進位的問題對于進位的兩數(shù)相加,這種核心思想為

  • 用兩個數(shù),     一個正常m相加(不考慮進位的)。用     異或a^b就是滿足這種要求,先不考慮進位(如果沒進位那么就是最終結(jié)果)。另一個     專門考慮進位的n。兩個1需要進位。所以我們用     a&b與記錄需要進位的。但是還有個問題,進位的要往上面進位,所以就變成這個需要進位的數(shù)左移一位。
  • 然后就變成     m+n重新迭代開始上面直到不需要進位的(即n=0時候)。
位運算的技巧有哪些  

 

實現(xiàn)代碼為:

public class Solution {
     public int Add(int num1,int num2) {
  /*
   *  5+3   5^3(0110)   5&3(0001) 
   *  0101    
   *  0011 
   */
  int a=num1^num2;
  int b=num1&num2;
  b=b<<1;
  if(b==0)return a;
  else {
   return Add(a, b);
  }        
  }
}
 

當(dāng)然,這里也可以科普一下二進制求加法:average = (a&b) + ((a^b)>>1) ;

 

二進制中1的個數(shù)

這是一道經(jīng)典題,在劍指offer上也有對應(yīng)題目,其具體題目要求輸入一個整數(shù),輸出該數(shù)二進制表示中1的個數(shù)(其中負數(shù)用補碼表示)。

對于這個問題,不用位運算將它轉(zhuǎn)成二進制字符串直接枚舉字符'1'的個數(shù)也可以直接求出來,但是這樣做是沒有靈魂的并且效率比較差。這里提供兩種解決思路

法一:大家知道每個類型的數(shù)據(jù)它的背后實際都是二進制操作。大家知道int的數(shù)據(jù)類型的范圍是(-2^31,2^31 -1)。并且int有32位。但是并非32位全部用來表示數(shù)據(jù)。真正用來表示數(shù)據(jù)大小的也是31位。最高位用來表示正負。

首先要知道:位運算的技巧有哪些

其次還要知道位運算&與。兩個十進制與運算.每一位同1為1。所以我們用2的正數(shù)次冪與知道的數(shù)分別進行與運算操作。如果結(jié)果不為0,那么就說明這位為1.(前面31個都是大于0的最后一個與結(jié)果是負數(shù)但是如果該位為1那么結(jié)果肯定不為0)

位運算的技巧有哪些  

 

具體代碼實現(xiàn)為:

public int NumberOf1(int n) {
  int va=0;
  for(int i=0;i<32;i++)
  {
    if((n&(1<<i))!=0)
    {           
      va++;
    }
  }
  return va;       
}
 

法二是運用n&(n-1)。n如果不為0,那么n-1就是二進制第一個為1的變?yōu)?,后面全為1.這樣的n&(n-1)一次運算就相當(dāng)于把最后一個1變成0.這樣一直到運算的數(shù)為0停止計算次數(shù)就好了,如下圖共進行三次運算那么n的二進制中就有三個1。位運算的技巧有哪些實現(xiàn)代碼為:

public class Solution {
    public int NumberOf1(int n) {
    int count=0;
    while (n!=0) {
     n=n&(n-1);
     count++;
    }
    return count;
 }
}
   

只出現(xiàn)一次的(一個)數(shù)字①

問題描述:

給定一個非空整數(shù)數(shù)組,除了某個元素只出現(xiàn)一次以外,其余每個元素均出現(xiàn)兩次。找出那個只出現(xiàn)了一次的元素。

說明:你的算法應(yīng)該具有線性時間復(fù)雜度。你可以不使用額外空間來實現(xiàn)嗎?

分析:

這是一道簡單的面試題,面試官常問怎么樣用不太復(fù)雜的方法找出數(shù)組中僅出現(xiàn)一次的數(shù)字(其他均出現(xiàn)兩次),暴力枚舉或者使用其他的存儲結(jié)構(gòu)都不夠優(yōu)化,而這個問題最高效的答案就是使用位運算。首先你要注意兩點:

  • 0和任意數(shù)字進行異或操作結(jié)果為數(shù)字本身.
  • 兩個相同的數(shù)字進行異或的結(jié)果為0.

具體的操作就是用0開始和數(shù)組中每個數(shù)進行異或,得到的值和下個數(shù)進行異或,最終獲得的值就是出現(xiàn)一次(奇數(shù)次)的值。

位運算的技巧有哪些  

 
class Solution {
    public int singleNumber(int[] nums) {
        int value=0;
        for(int i=0;i<nums.length;i++)
        {
            value^=nums[i];
        }
        return value;
    }
}
   

只出現(xiàn)一次的(一個)數(shù)字②

問題描述:

給定一個非空整數(shù)數(shù)組,除了某個元素只出現(xiàn)一次以外,其余每個元素均出現(xiàn)了三次。找出那個只出現(xiàn)了一次的元素。

說明:你的算法應(yīng)該具有線性時間復(fù)雜度。你可以不使用額外空間來實現(xiàn)嗎?

分析:

這題和上一題的思路略有不同,這題其他數(shù)字出現(xiàn)了3次,那么我們?nèi)绻苯邮褂梦贿\算異或操作的話無法直接找到結(jié)果,就需要巧妙的運用二進制的其他特性:判斷整除求余操作。即判斷所有數(shù)字二進制1的總個數(shù)和0的總個數(shù)一定有一個不是三的整數(shù)倍,如果0不是三的整數(shù)倍那么就說明結(jié)果的該位二進制數(shù)字為0,同理否則為1.

位運算的技巧有哪些  

 

在具體的操作實現(xiàn)上,問題中給出數(shù)組中的數(shù)據(jù)在int范圍之內(nèi),那么我們就可以在實現(xiàn)上可以對int的32個位每個位進行依次判斷該位1的個數(shù)求余3后是否為1,如果為1說明結(jié)果該位二進制為1可以將結(jié)果加上去。最終得到的值即為答案。

具體代碼為:

class Solution {
    public int singleNumber(int[] nums) {
        int value=0;
        for(int i=0;i<32;i++)
        {
            int sum=0;
            for(int num:nums)
            {
                if(((num>>i)&1)==1)
                {
                    sum++;
                }
            }
            if(sum%3==1)
                value+=(1<<i);
        }
        return value;
    }
}
   

只出現(xiàn)一次的(兩個)數(shù)字③

題目描述

一個整型數(shù)組里除了兩個數(shù)字之外,其他的數(shù)字都出現(xiàn)了兩次。請寫程序找出這兩個只出現(xiàn)一次的數(shù)字。

思路

上面的問題處理和理解起來可能比較容易,但是這個問題可能稍微復(fù)雜一點,但是這題可以通過特殊的手段轉(zhuǎn)化為上面只出現(xiàn)一次的一個數(shù)字問題來解決,當(dāng)然核心的位運算也是異或^。

具體思路就是想辦法將數(shù)組邏輯上一分為二!先異或一遍到最后得到一個數(shù),得到的肯定是a^b(假設(shè)兩個數(shù)值分別為a和b)的值。在看異或^的屬性:不同為1,相同為0. 也就是說最終這個結(jié)果的二進制為1的位置上a和b是不相同的。而我們可以找到這個第一個不同的位,然后將數(shù)組中的數(shù)分成兩份,該位為0的進行異或求解得到其中一個結(jié)果a,該位為1的進行異或求解得到另一個結(jié)果b。

具體可以參考下圖流程:

位運算的技巧有哪些  

 

實現(xiàn)代碼為:

public int[] singleNumbers(int[] nums) {
    int value[]=new int[2];
    if(nums.length==2)
        return  nums;
    int val=0;//異或求的值
    for(int i=0;i<nums.length;i++)
    {
        val^=nums[i];
    }
    int index=getFirst1(val);
    int num1=0,num2=0;
    for(int i=0;i<nums.length;i++)
    {
        if(((nums[i]>>index)&1)==0)//如果這個數(shù)第index為0 和num1異或
            num1^=nums[i];
        else//否則和 num2 異或
            num2^=nums[i];
    }
    value[0]=num1;
    value[1]=num2;
    return  value;
}

private int getFirst1(int val) {
    int index=0;
    while (((val&1)==0&&index<32))
    {
        val>>=1;// val=val/2
        index++;
    }
    return index;
}

到此,關(guān)于“位運算的技巧有哪些”的學(xué)習(xí)就結(jié)束了,希望能夠解決大家的疑惑。理論與實踐的搭配能更好的幫助大家學(xué)習(xí),快去試試吧!若想繼續(xù)學(xué)習(xí)更多相關(guān)知識,請繼續(xù)關(guān)注億速云網(wǎng)站,小編會繼續(xù)努力為大家?guī)砀鄬嵱玫奈恼拢?/p>

向AI問一下細節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI