[算法]位运算问题之一
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; }
以上是关于[算法]位运算问题之一的主要内容,如果未能解决你的问题,请参考以下文章