【40讲系列12】位运算

一、理论

常用的位运算:

X & 1 == 1 OR == 0  :判断奇偶

X = X & (X - 1) : 清零最低位的1 (常用来求二进制位有多少个1)

X & -X : 得到最低位的1, -X就是按位取反末尾加1,。 例如X为10100,-X为01100

X & (1 << (n - 1)) : 获取 X 的第 n  位的幂值

二、典型例题

①:二进制数中的比特位统计问题(LC191、剑指11.二进制中1的个数

思路:利用X = X & (X - 1) ,每次清零最低位的1.

②:判断一个数是否是2的幂次方(LC231)

思路:一个数如果是2的幂次方,其二进制位有如下特点:有且仅有一个1。

class Solution {
    public boolean isPowerOfTwo(int n) {
        return n > 0 && (n & (n - 1)) == 0;
    }
}

  

③:比特位计数(LC338)

class Solution {
    public int[] countBits(int num) {
        // 方法1 和 方法2 是最优解,它们都是利用数组前面已经算好的数来计算当前数的1的个数
        // 方法1:i & (i - 1) 可以清零最低位的1。
        int[] res = new int[num + 1];
        for (int i = 1; i <= num; i++) {
            res[i] = res[i & (i - 1)] + 1;
        }
        return res;

        // 方法2:当i的最低位是1,i中1的个数是i>>1中1的个数再加1;如果i最低位是0,则加0
        /*int[] res = new int[num + 1];
        for (int i = 1; i <= num; i++) {
            res[i] = res[i >> 1] + (i & 1);
        }
        return res;*/

        /* 方法3:遍历1~num,调用函数计算每一个数的 1的个数。
        int[] res = new int[num + 1];
        for (int i = 1; i <= num; i++) {
            res[i] = numberOf1(i);
        }
        return res;
        */
    }
    /**
     * LeetCode 191
     */
    public int numberOf1(int n){
        int count = 0;
        while (n != 0){
            count++;
            n = n & (n - 1);
        }
        return count;
    }
}

 

☆☆☆☆④:N皇后问题的位运算解法(LC52)

不使用位运算(执行耗时:2 ms,击败了61.47% 的Java用户),其解法见:【40讲系列9】剪枝  

使用位运算加速才是本题的最优解,(执行耗时:0 ms,击败了100.00% 的Java用户)

位运算版本1:(推荐手撕~)

class Solution {
    int res = 0;
    public int totalNQueens(int n) {
        if (n < 1 || n > 32) return 0;
        dfs(0,0,0,0, n);
        return res;
    }
    private void dfs(int row, int col, int pie, int na, int n) {
        if (row == n){
            res++;
            return;
        }
        // 得到当前所有的有效空位。
        // (~(col | pie | na))表示获取当前空位,标识为1。由于是32位整数,高位也全都是1.
        // 因此需要 & ((1 << n) - 1) 去掉高位,得到有效位置上的空位。例如8皇后,即末尾8位全为1
        int pos = (~(col | pie | na)) & ((1 << n) - 1);
        while (pos != 0){ // 遍历所有的空位
            // 获取最后的空位1。 执行后相当于该空位已经被占了,可以到下一行了
            int mostRightOne = pos & (-pos); // (-pos)即(~pos+1), 按位取反末尾加1
//            int mostRightOne = pos & (~pos + 1);
            pos &= (pos - 1); // 去掉最低位的1
//            pos = pos - mostRightOne;
            dfs(row + 1,(col | mostRightOne), // col、pie、na都要受到mostRightOne的影响,进行更新
                    (pie | mostRightOne) << 1,
                    (na | mostRightOne) >> 1,n);
        }
    }

}

 

位运算版本2:(参考左神)

class Solution {
    // 参考左神P240
    int res = 0;
    public int totalNQueens(int n) {
        if (n < 1 || n > 32) return 0;
        int upperLim = n == 32 ? -1 : (1 << n) - 1; // -1的二进制表示为32位全为1
        help(upperLim,0,0,0);
        return res;
    }

    /**  help():剩余的皇后在之前皇后的影响下,有多少种合法的摆法。
     *
     * @param upperLim 表示当前行哪些位置可以放皇后,1代表可以,0代表不能. 在递归过程中始终不变
     * @param colLim   表示递归计算到上一行为止,在哪些列上已经放置了皇后,1代表已经放置,0代表没有放置
     * @param leftDiaLim  表示递归计算到上一行为止,受已经放置的皇后的左下方斜线的影响,导致当前行不能放置的位置
     *                    1代表不能放置
     *                    leftDiaLim每次左移一位,就可以得到之前所有皇后的左下方斜线对当前行的影响。
     * @param rightDiaLim 表示递归计算到上一行为止,受已经放置的皇后的右下方斜线的影响,导致当前行不能放置的位置
     *                    1代表不能放置
     *                    rightDiaLim每次右移一位,就可以得到之前所有皇后的右下方斜线对当前行的影响。
     */
    private void help(int upperLim, int colLim, int leftDiaLim, int rightDiaLim){
        if (colLim == upperLim){
            res++;
            return;
        }
        int pos = 0; // 代表当前行在colLim、leftDiaLim、rightDiaLim的影响下,还有哪些位置是可选择的。1代表可以选择
        int mostRightOne = 0; // 代表在pos中,最右边的1是在什么位置。然后从右到左一次筛选出pos中可选择的位置进行递归尝试。
        pos = upperLim & (~(colLim | leftDiaLim | rightDiaLim));
        while (pos != 0){
            mostRightOne = pos & (~pos + 1);
            pos = pos - mostRightOne;
            help(upperLim,colLim | mostRightOne,
                    (leftDiaLim | mostRightOne) << 1,
                    (rightDiaLim | mostRightOne) >>> 1); // >>>无符号右移,忽略符号位,空位都以0补齐
        }
    }
}

 

posted @ 2020-11-24 15:33  不学无墅_NKer  阅读(184)  评论(0编辑  收藏  举报