日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

数据结构与算法--位运算

發布時間:2023/12/4 编程问答 32 豆豆
生活随笔 收集整理的這篇文章主要介紹了 数据结构与算法--位运算 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

位運算

  • 記得大學編譯原理剛開始面對的就是機器語言,而位運算就是講數字用二進制標識后,對每一位上的0, 或者1 進行運算。二進制以及位運算都是計算機學科的基礎,很多底層技術都離不開位運算,因此我們在實際開發過程中可能會遇到。由于我們人類熟悉的是十進制,因此接觸到二進制的時候可能難以適應。
  • 除了二進制,我們還可以將數字表示成其他的進制,比如,表示分秒的六十進制等。針對不太熟悉的進制,可能會有一些奇奇怪怪的問題,比如下面這種:
/**在Excel中,用A表示第一列,B表示第二列......Z表示第26 列,AA表示27 列,AB表示28列......以此類推那么我們用一個函數,輸入字母,輸出列的編碼是多少號*/ /*** @author liaojiamin* @Date:Created in 16:30 2021/3/17*/ public class NumberOfBit {//字母轉數字 A-Z :1-26public static int letterToNumber(char letter) {int num = (int)(letter - 'A' + 1) ;return num;}public static Long base26(String baseStr){if(baseStr == null){return -1L;}char[] charArray = baseStr.toCharArray();Long baseNumber = 26L;Long resultNum = 0L;double position = 0;for (int i = charArray.length -1; i >=0; i--) {Double temp = Math.pow(baseNumber, position) * letterToNumber(charArray[i]);resultNum += temp.longValue();position++;}return resultNum;}public static void main(String[] args) {System.out.println(base26("AA"));System.out.println(base26("AB"));System.out.println(base26("AZ"));System.out.println(base26("ZZ"));System.out.println(base26("AAA"));} }
  • 如上其實就是講A~Z理解成一個26進制,只要理解了二進制的計算規則,26進制也是同樣的算法。
  • 二進制中位運算也就只有五種與, 或,異或,左移,右移。前三種用如下規則
與(&):0&0 = 01&0 = 00&1 = 01&1=1 或(|):0|0 = 01|0 = 1. 0|1=11|1 = 1 異或(^):0^0 = 01^0=10^1=11^1 = 0

-左移運算符m << n表示吧m左移n位,左移n位的時候,最左邊的n位將被丟棄,同時最右邊補充n個0,例如:

0000 1010 << 2 = 0010 1000 1000 1010 << 3 = 0101 0000
  • 右移運算符m >> 表示把m右移n位, 右移n為的時候,最右邊的n為將被丟棄,當右移時候,處理最左邊為的情形要稍微復雜一點
    • 如果是無符號數值,則用0 填補最左邊的n為。
    • 如果是有符號數值,則用數字的符號位補充最左邊的n位,也就是說如果數字是一個正數,右移后補充n個0 在左邊,如果原數字是負數,則右移后最左邊補充n個1.
    • 如下對兩個8 為有符號數值做右移操作
0000 1010 >>2 == 0000 0010 1000 1010 >>3 = 1111 0001
具體案例分析
  • 題目:實現一個函數,輸入一個整數,輸出該整數二進制標識中1 的個數。例如把 9 標識成二進制后四位1001,有2位是1,因此如果輸入9 ,函數輸出2
分析
  • 上題中考察二進制的知識,基本思路也就是利用那五種運算規則的組合來解答,可以先判斷二進制數的最低位是不是1 ,將數值右移一位,接著判斷最低位是否是1 ,繼續如此直到數值為0 為止,
  • 現在需要確定怎么確定首位是1,:將整數與1 做 與操作,看結果是否是0 ,如果是0 則表示最低位是0 ,否則是1
  • 用9 為案例如下:
1001 & 1 = 1 1001 >> 1 = 0100 0100 & 1 = 0 0100 >> 1 = 0010 0010 & 1 = 0 0010 >> 1 = 0001 0001 & 1 = 1 0001 >> 1 = 0000
  • 以上我們可以得到如下代碼:
/*** 正整數n 二進制表示中 1 的個數* */public static int numberOf1(int n){int count = 0;while (n > 0){int andOne = n & 1;if(andOne == 1){count ++;}n = n>>1;}return count;}
  • 問題,以上代碼都只考慮正數情況,正數右移是補充0 在最高位,所以能得出正常的解。我們用一個負數來舉例,例如 -9 。
  • 首先必須要明確的是:***負數在計算機中都是以補碼來表示的。 負數的位運算也是在補碼上進行的。***這一點很重要
    • 我們用int類型為案例分析,int 32位有符號整型(-9) 的二進制標識:
    • 原碼:1000 0000 0000 0000 0000 0000 0000 1001
    • 反碼:1111 1111 1111 1111 1111 1111 1111 0110
    • 補碼:1111 1111 1111 1111 1111 1111 1111 0111
  • 用以上算法對-9 進行右移的時候,在最左邊補充的是1 ,因此不可能為0 ,因此會有死循環。
正確解法
  • 上面的算法中我們思路是通過對求解的數進行右移來判斷首位是否是1 ,既然不能通用,那么我們是否可能逐一判斷數值的每一位二進制位上是否是1 。我們用flag表示 甄別是否是1 的數值 ,首先取flag = 1
  • 首先將數值與flag進行與操作,判斷最低位是否是1 ,
  • 將flag 左移一位,繼續與數值與操作,判斷第二位是否為1
  • 繼續如上操作,直到 flag的值為 0 ,(此時移動了32 位,超出int界限,所有位置都為0 )
  • 我們可以得到如下算法實現:
/*** 通用二進制表示中 1 個數* */public static int numberOf1_1(int n){int count = 0;int flag = 1;while (flag > 0){if((n & flag) > 0){count ++;}flag = flag << 1;}return count;}
  • 以上算法能統一解決帶符號,和不帶符號的數值的問題,并且對所有數值的計算都需要循環32 次,但是其實其中只有幾次是有效的判斷,比如1001 只有2 次,我們是否有更精簡的方法。有幾個1 就幾次判斷?
最優解
  • 分析將一個數減1 , 如果是不為0 的數,那么二進制表示中必然有1 ,假設這個數最右邊一位是1,那么減1 的時候,最右邊(低位)就會1 變0,而其他所有位不變。相當于最后一位取反。例子:
0000 1000 0010 0000 0110 0000 0000 0001 - 1 = 0000 1000 0010 0000 0110 0000 0000 0000
  • 此時我們只能判斷出第一位是不是1,此時我們還繼續減1 ,那么假設第m位是1 ,這個時候,第m位 變為0 ,第m位以后的位都從0 變1 ,如下
0000 1000 0010 0000 0110 0000 0000 0000 - 1 = 0000 1000 0010 0000 0101 1111 1111 1111
  • 這個時候1 變多了,如果繼續減,顯然不能達到我們目的,我們需要將后面多出來的1 都變成0 ,如下
0000 1000 0010 0000 0101 1111 1111 1111 --> 0000 1000 0010 0000 0100 0000 0000 0000
  • 要達到如上目的,我們想到可以用與,或操作,兩個都試試,如下:
//或操作 0000 1000 0010 0000 0101 1111 1111 1111 | 0000 1000 0010 0000 0100 0000 0000 0000 = 0000 1000 0010 0000 0100 0000 0000 0000 //與操作 0000 1000 0010 0000 0101 1111 1111 1111 & 0000 1000 0010 0000 0110 0000 0000 0000 = 0000 1000 0010 0000 0100 0000 0000 0000
  • 如上與,或,操作都能得到對應的值,但是與原值操作的對象,我們怎么得到呢,我們可以觀察到,與操作中的操作對象的二進制值正好是原數據進行 -1 操作之前的二進制,那么問題就變的很簡單了。
  • 我們將以上分析總結:
    • 將整數減去1,在和原整數做與操作,
    • 此時能將該整數最右邊一個1 變成0 ,
    • 那么二進制表示中有多少個1 ,就可以進行多少次這種操作
    • 因此有如下算法
public static int numberOf1_2(int n){int count = 0;while (n != 0){n = n&(n-1);if(n != 0){count ++;}}return count;}
相關問題
  • 用一條語句判斷一個整數是不是2 的整數次方
    • 一個整數是2 的整數次方,那么二進制表示中必然只有一個2,而其他所有位都是0
    • 同樣依據解法三,將原數減1 后得到結果 ,將結果與原數進行與操作必然會變成0
  • 輸入兩個整數m,n。計算要改變m的二進制表中的多少位才能得到0,。例如10 的二進制 1010,與13的二進制1101
    • 此問題同樣考察位操作,如上案例10, 和13,有三位二進制是不同的,我們如何通過二進制位操作甄別
    • 查看五種位操作,與,或,異或,左右移位,其中異或操作中兩個二進制相同位數的值為0 ,不同的值為1
    • 利用這個特性, m ^ n 得到一個值x,我們只需要求解x 中1 的個數,就得到m與n中不同位數個數。

上一篇:數據結構與算法–再談遞歸與循環(斐波那契數列)
下一篇:數據結構與算法–代碼完整性案例分析

總結

以上是生活随笔為你收集整理的数据结构与算法--位运算的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。