[算法]位运算问题之一

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[算法]位运算问题之一相关的知识,希望对你有一定的参考价值。

一、不用额外变量交换两个整数的值

a=a^b;
b=a^b;
a=a^b;
    或者:
a=a+b;
b=a-b;
a=a-b;

二、不同任何比较判断找出两个数中较大

有两种方法,方法一有一定的局限性,a-b的值可能溢出,这样溢出后符号改变,返回结果就不正确。

而方法二对a和b是否异号进行了判断,如果同号,则按照方法一返回,否则直接返回正的数就行。

a-b的值如果为0的处理按照大于0处理。

//如果n为1,则返回0;如果n为0,则返回1
    public static int flip(int n) {
        return n ^ 1;
    }
    //返回整数n的符号,正数和0返回1,负数返回0
    public static int sign(int n) {
        return flip((n >> 31) & 1);
    }
    //方法一
    public static int getMax1(int a, int b) {
        int c = a - b;
        int scA = sign(c);
        int scB = flip(scA);
        return a * scA + b * scB;
    }
    //方法二
    public static int getMax2(int a, int b) {
        int c = a - b;
        int sa = sign(a);
        int sb = sign(b);
        int sc = sign(c);
        int difSab = sa ^ sb;
        int sameSab = flip(difSab);
        int returnA = difSab * sa + sameSab * sc;
        int returnB = flip(returnA);
        return a * returnA + b * returnB;
    }

三、整数的二进制表达式中有多少个1

题目:

给定一个32位整数n,可为0、整数、负数。返回该整数二进制表达式中1的个数。


首先简单介绍一下移位操作符:

移位操作符只可用来处理整型类型。左移位操作符(<<)能按照操作符右侧指定的位数将操作符左边的操作数向左移动(在低位补0)。“有符号”右移位操作符(>>)则按照操作符右侧指定的位数将操作符左边的操作数向右移动。“有符号”右移位操作符使用“符号扩展”:若符号为正,则在高位插入0;若符号为负,则在高位插入1。java中增加了一种“无符号”右移位操作符(>>>),它使用”零扩展”:无论正负,都在高位插入。


解法一:

最简单的解法。

每次进行无符号右移一位,检查最右边的bit是否为1。最复杂的情况下要进行32次循环。

public static int count1(int n) {
        int res = 0;
        while (n != 0) {
            res += n & 1;
            n >>>= 1;
        }
        return res;
    }

解法二:

循环次数与1的个数有关。每次循环去掉最右侧的1。

public static int count2(int n) {
        int res = 0;
        while (n != 0) {
            n &= (n - 1);
            res++;
        }
        return res;
    }

解法三:

与解法二大致相同,只是删除最右侧1的方式不同。

public static int count3(int n) {
        int res = 0;
        while (n != 0) {
            n -= n & (~n + 1);
            res++;
        }
        return res;
    }

解法四:

平行算法:

 

public static int count4(int n) {
        n = (n & 0x55555555) + ((n >>> 1) & 0x55555555);
        n = (n & 0x33333333) + ((n >>> 2) & 0x33333333);
        n = (n & 0x0f0f0f0f) + ((n >>> 4) & 0x0f0f0f0f);
        n = (n & 0x00ff00ff) + ((n >>> 8) & 0x00ff00ff);
        n = (n & 0x0000ffff) + ((n >>> 16) & 0x0000ffff);
        return n;

解法五:

MIT hackmem

public int count5(int x) {
        int temp = x - (x >>> 1) & 033333333333 - (x >>> 2) & 011111111111;
        return (temp +(temp >>>3)) & 030707070707 % 63;
    }

解法六:

查表:

public int static4bit(int x) {
        int[] table = {0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4};
        int c = 0;
        for (; x > 0; x >>>= 4) {
            c += table[x & 0xF];
        }
        return c;
    }
    /**
     * 首先构造一个包含256个元素的表table,table[i]即i中1的个数,这里的i是[0-255]之间任意一个值。 
     * 然后对于任意一个32bit无符号整数n 
     * ,我们将其拆分成四个8bit,然后分别求出每个8bit中1的个数,再累加求和即可,这里用移位的方法,每次右移8位 
     * ,并与0xff相与,取得最低位的8bit 
     * ,累加后继续移位,如此往复,直到n为0。所以对于任意一个32位整数,需要查表4次。以十进制数2882400018为例 
     * ,其对应的二进制数为10101011110011011110111100010010 
     * ,对应的四次查表过程如下:红色表示当前8bit,绿色表示右移后高位补零。 
     *
     * 第一次(n & 0xff) 10101011110011011110111100010010 
     *
     * 第二次((n >> 8) & 0xff) 00000000101010111100110111101111 
     *
     * 第三次((n >> 16) & 0xff)00000000000000001010101111001101 
     *
     * 第四次((n >> 24) & 0xff)00000000000000000000000010101011 
     */
    public int static8bit(int x) {
        int[] table = {0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4, 1, 2,
                2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 1, 2, 2, 3, 2, 3, 3,
                4, 2, 3, 3, 4, 3, 4, 4, 5, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5,
                4, 5, 5, 6, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 2,
                3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 2, 3, 3, 4, 3, 4,
                4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5,
                6, 5, 6, 6, 7, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
                2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 2, 3, 3, 4, 3,
                4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5,
                5, 6, 5, 6, 6, 7, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5,
                6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 3, 4, 4, 5,
                4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 4, 5, 5, 6, 5, 6, 6, 7, 5,
                6, 6, 7, 6, 7, 7, 8,};
        int c = 0;
        for (; x > 0; x >>>= 8) {
            c += table[x & 0xFF];
        }
        return c;
    }

以上是关于[算法]位运算问题之一的主要内容,如果未能解决你的问题,请参考以下文章

ACM道路之一:基础算法(位运算)

n皇后(位运算)

算法之美

算法之美

LeetCode通关:求次数有妙招,位运算三连

LeetCode通关:求次数有妙招,位运算三连