[算法]位运算问题之一

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

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;
    }
时间: 2024-08-07 04:33:45

[算法]位运算问题之一的相关文章

java加密解密算法位运算

一.实例说明 本实例通过位运算的异或运算符 “ ^ ” 把字符串与一个指定的值进行异或运算,从而改变每个字符串中字符的值,这样就可以得到一个加密后的字符串.当把加密后的字符串作为程序输入内容,异或运算会把加密后的字符串还原为原有字符串的值.效果图如下: 二.实现过程 1 package com.itxxz; 2 3 import java.util.Scanner; 4 5 /** 6 * java加密解密算法 7 * 8 * @author 螃蟹 9 * 网站:IT学习者 10 * 网址:ht

Apriori算法-位运算-C语言

原文地址:http://blog.csdn.net/liema2000/article/details/6118423 ///////////////////////////////////////////////////////////////////////////** 文件名称:FAST apriori.cpp * 摘 要:采用位运算提高算法的效率 * 将每一项抽象为数值,通过与运算判断是否存在,abcde为16,8,4,2,1换成2进制,各占一个位置,互相不干扰.局限于字母.换算的比较字

数据结构与算法-位运算

位运算位运算是把数字用二进制表示之后,对每一位上的0或者1的运算.理解位运算的第一步是理解二进制.二进制是指数字每一位都是0或者1,如十进制的2转换为二进制之后是10,而十进制的10转换为二进制之后是1010.在程序员圈子里有一个流传了很久的笑话,说世界上有10种人,一种人知道二进制,另一种人不知道二进制. 下面是几个常见的关于位运算的算法题:基本位运算位运算总共只有5种运算:与&,或|,异或^,左移<<,右移>>.与,或,异或的运算规则: 左移,右移运算规则:左移运算m

算法——位运算

&:按位与. |  :按位或. ~ :按位取反,带符号位.(注意和!的区别,!只是逻辑取反)  ^ : 异或也叫半加运算:0⊕0=0,1⊕0=1,0⊕1=1,1⊕1=0. >> : 表示右移,算数右移,如果该数为正,高位补符. >>>:表示无符号右移,也叫逻辑右移,高位补零. 小操作: 1. 判断奇偶数 a & 1 2. 交换变量 a ^= b, b ^= a, a ^= b 3. 乘以(除以)2的n次方:左移,右移:取模运算:a % (2 ^ n) 等价于

[算法]位运算问题之三(实现加减乘除)

题目: 给定32位整数a和b,可正.可负.可0,不能使用算术运算符,可分别实现a和b的加减乘除运算. 加法运算: 无进位相加: a: 001010101 b: 000101111 a^b 001111010 只考虑进位: a   001010101 b   000101111 (a&b)<<1 000001010 把完全不考虑进位的相加值与只与考虑进位的产生值再相加,就是最后的结果.重复这样的过程,直到进位产生的值完全消失,说明所有的过程都加完了. public static int

[算法]位运算之二

题目一: 给定一个整形数组arr和一个大于1的整数k.已知arr中只有1个数出现了奇数次,其他的数都出现了偶数次,请返回出现了奇数次的数. 时间复杂度为O(N),额外空间复杂度为O(1). 思路: 整数n与0异或的结果是n,整数n与整数n异或的结果是0.所以先申请一个整形变量,记为eO.把eO和每个数异或(eO=eO^当前数),最后eO的值就是出现了奇数次的那个数. 异或运算满足交换律和结合律. public static void printOddTimesNum1(int[] arr) {

[算法]位运算实现加减法

异或可以实现不考虑进位的加法,相同为0,不同为1 0101 ^ 0001 = 0100 两个数相加不考虑进位从二进制角度上说就是相同为0,不同为1,因为二进制只有两个数1和0 1+1=0 0+1=1 与运算可以实现进位 同为1才为1 0101 & 0001 = 0001 说明最低位有进位 int add(int a, int b) { return b == 0 ? a : add(a ^ b, (a & b) << 1); } 左移一位表示 将进位加到前一位 递归表示要遍历进

算法学习 - 递归与非递归,位运算与乘除法速度比较

递归调用非递归调用 运行时间比较 结论 位运算与乘除法 结论 递归调用/非递归调用 我们都知道,很多算法,都是用递归实现的.当然它们同时也是可以用非递归来实现. 一般我们在对二叉树进行遍历的时候,还有求斐波那契数的时候,递归是非常简单的.代码容易懂,好实现. 但是递归的时候,有一个问题,就是需要压栈.为什么要压栈呢?因为当我在函数内部调用自身的时候,要中断当前的操作继续跳转到下一次的实现,而当前运行的状态要保存起来.所以就把当前状态进行压栈,等到运行到递归条件结束的时候,再弹栈. 所以递归就是需

位运算之——按位与(&amp;)操作——(快速取模算法)

由于位运算直接对内存数据进行操作,不需要转成十进制,因此处理速度非常快. 按位与(Bitwise AND),运算符号为& a&b 的操作的结果:a.b中对应位同时为1,则对应结果位也为1. 例如: 10010001101000101011001111000 & 111111100000000 --------------------------------------------- 10101100000000 对10101100000000进行右移8位得到的是101011,这就得