數字位運算操作與算法簡單示例


  我們對於位運算可能既陌生又熟悉。知道其運算方法運算過程,但不能運用好它。

  首先,我們還是回顧一下Java中位運算都包含那些操作:

      一、與運算(&)

  運算法則:將二進制數進行按位與運算。0&0=0;0&1=0;1&1=1 ;

      如:0011 & 0010 = 0010;

  二、或運算(|)

  運算法則:將二進制數進行按位或運算。0|0 =0;1|0 = 1;  1|1=1

  如:0011 & 0010 = 0011;

  三、異或運算(^)

  運算法則:將二進制數進行按位異或。   0^0 = 0; 1^1=0;1^0=1;

  如:0011 ^ 0010 = 0001;

  四、非運算(~)

  運算法則:將二進制數進行按位“取反”操作。 ~0 = 1;~1 = 0;

  如:~0011 = 1100

  五、左移(有符號左移,無符號左移)、右移

  參考上一篇:https://www.cnblogs.com/funmans/p/11179189.html

  第六條:“>>”、“>>>”、“<<”都是什么鬼?

  首先 >> 、>>> 都是Java中的右移操作,“>>”是有符號右移,“>>>”是無符號右移。顧名思義,有符號須得在右移過程中保持符號,所以,有符號右移,若是正數則在高位補0,反之補1,無符號右移不管正負都補0.

特別注意點:對於char、byte、short等類型。在運算時候會先進行int類型的轉換。都知道int類型4個字節,32位。所以,如果說右移的位數超過了32,那也是沒有用滴。Java采用了取余數的方式來保證右移位數的有效性,即 n>>m相當於 n>>(m%32); 右移相當於做除法操作

    “<<”是什么呢?它表示左移操作。那么怎么不見“<<<”這個東西呢?因為左移操作沒有有符號與無符號左移。左移操作的過程就是移除最高位,然后在末尾補0就OK。比如:4,二進制100,當然運算時是3位的,

即 0000 0000 0000 0000 0000 0000 0000 0100,如果進行<<3操作,也就是左移三位,去掉頭上三個0,后面再補上三個:0000 0000 0000 0000 0000 0000 0010 0000,換算成十進制就是32,所以左移也就是乘法運算。同樣的,如果你的左移位數大於32位,其實際左移位數位 對32 取余數的值

 

好了以上就是基本但位運算法則,接下來我們開始循序漸進的進入第一步:

1、給定一個數我們如何知道這個數M的二進制數的第 N 位是 1 還是 0 呢?

方法:通過與運算以及位移運算,通過將 1 進行左移 n位再進行跟原數據進行與操作。由於我們左移完成的這個數只有第N位是1,其他位置全都是 0 ,所以結果如果等於 0,那么給定數的第N位為0 ,否則為1;

 result = M & (1<<N) == 0 ? 0 : 1 ;

2、給定一個數,我們如何去改變這個數二進制表示中具體某一位的值?

這個首先分兩種情況:

將指定數M的二進制數第N為設置為1:

方法:通過或運算以及位移運算,通過將1 進行左移 N位,在進行與 M進行或運算,由於 0|0 = 0;0|1 = 1;所以,低位是不會改變的。

 result = M | (1<<N);

將指定數M的二進制數第N位設置為0:

 方法:通過與運算以及位移運算,將1 進行左移N位再取反或-1,然后與上M

result = M & ~(1<<N)  或者  result = M & ((1<<M) -1);

 


接下來我們進入一個簡單的算法階段:

問題:給定一個整型數組,求這個數組中的第二大元素?

可能很多同學第一眼看到這個有點懵,一想平時都是求數組中的最大的那個,怎么求第二大呢,略一思索后,得到一個方案:我先對這個數組進行排序,然后還不是想取第幾大就取第幾大。OK,當然排序是來解決這個問題是可以的,但是確實有點問題復雜化了。

實際上我們可能只需要對數組進行一次遍歷操作就OK了。(PS:當然肯定還有復雜度更低的方法,這里只是做一個簡單舉例說明)

    /**
     * 求第二大元素
     * @param data
     * @return
     */
    public int max(int[] data){
        int temp = data[0];
        int max = data[0];
        for (int i = 1; i < data.length; i++) {
            if(data[i]>temp){
                max = temp;
                temp = data[i];
            } else if(data[i] > max && data[i] != temp){
                max = data[i];
            }
        }
        return max;
    }

OK,上面的代碼如果只是考慮求取第二大元素,當然也能勉強使用。但是我們寫代碼肯定是要考慮通用性的,那么如果問題換成求取整型數組中的第N大元素或者第N小問題,又該如何呢?

既然本篇將到了位運算,當然需要用到了,基本思路如下:

1、獲取元素最大最小值

2、初始化一個二進制數空間用其每一位來標記數組中的一個數,存在(1)、不存在(0)

3、通過遍歷數組進行二進制數每一位的的賦值

4、通過對二進制數求第N個不為0的位數,來獲取第N大或第N小

     /**
     * 求第N小
     * @param data
     * @param index
     * @return
     */
    public  int minN(int[] data,int index){
        int max,min;
        max = min = data[0];
        //求最大最小值
        for (int datum : data) {
            max = Math.max(max,datum);
            min = Math.min(min,datum);
        }
        //初始化標記空間
        int bitSpace = 1<<(max - min +1);
        //遍歷數組進行空間標記.標記過程就是將bitSpace中的響應位置值設置為1
        for (int datum : data) {
            bitSpace |= 1<<(datum - min);
        }
        //獲取bitSpace的index位置值
        for (int i = 0; i < max-min+1; i++) {
            if((bitSpace & 1<<i) != 0){
                index --;
            }
            if(index <= 0){
                return i + min;
            }
        }
        return 0;
    }

以上程序就利用了二進制數據的的位運算來進行編程,利用二進制的 0 1,表示某個數據的存在與不存在。利用位運算進行二進制位的賦值、取值操作。

當然針對上述算法來說,還是又很多缺陷的。

比如:上述不能夠針對同一值進行多次標記。

 

總結:數字的位運算操作在算法中一般的使用場景都是用來作為標記使用。針對上一問題,如果需要針對同一值進行多次標記,則可將bitSpace換成數組(所占空間更大),但思想不變;再比如大名鼎鼎的布隆過濾器,也是標記。

 

》》》如有不當之處,歡迎批評指正。

    


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM