zoukankan      html  css  js  c++  java
  • 【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补齐
            }
        }
    }
  • 相关阅读:
    [PHP]AES加密----PHP服务端和Android客户端
    [PHP]memcache安装
    [Android]apk反编译方法
    [PHP]生成随机数(建立字典)
    [PHP]Mysql的运用
    [PHP]对象数组和普通数组总结
    ThinkPHP5+Redis单例型购物车
    移动硬盘新建选项消失、不能新建文件夹和文件的解决方案
    PHP substr() 函数截取中文字符串乱码
    php开发中遇到问题的找错误的方法
  • 原文地址:https://www.cnblogs.com/HuangYJ/p/14026965.html
Copyright © 2011-2022 走看看