使用位运算简化算法的复杂度

3,820 阅读2分钟

什么是位运算

因为在计算机的世界里,目前它只认识0和1.程序中的所有数在计算机内存中都是以二进制的形式储存的。位运算说穿了,就是直接对整数在内存中的二进制位进行操作。由于位运算直接对内存数据进行操作,不需要转成十进制,因此处理速度非常快。

位运算

按位与 a & b

按位或 a | b

按位异或 a ^ b

按位取反 ~a

左移 a << b

带符号右移 a >> b

带符号右移 无符号右移 a>>> b。

用位运算能解决的题目

1.一个数组中有一个数出现了奇数次,其他的数都是偶数次。求出这个数

思路

两个相同的数异或之后为0,也就是说成对出现的数异或之后都是0.所以整个数组异或之后的结果就是那个出现奇数次的数。

代码

   public static void printOddTimesNum(int[] arr){
        int eor =0;
        for (int i =0;i<arr.length;i++){
            eor  = eor^arr[i];
        }
        System.out.println(eor);
    }

2.升级一下,数组中有两种数出现了奇数次,求出这两个数

思路

假设这两个数是 a和b,根据上一题的思路,整个数组异或之后,得出的就是 a^b.暂且设置结果为eor。

那么怎么分离出a和b 呢?

可以把eor 中最右侧的二进制的1 作为分离因子,因为这个1 要么在a中,要么在b中。

遍历数组,把相同二进制中为1的数进行异或,这样就得到了 a或者是b。

最后 a^eor 就得到了b。

求一个数二进制中最右侧的1

//求一个数二进制最低位的1
    public static int getRightOne(int targetNum){
        int res = targetNum & ((~targetNum)+1);
        //直接可以这样写
        res = targetNum & (-targetNum);
        return res;
    }

引出另外一个题目,求一个数中二进制位有多少个1

 //求一个二进制数据中1的个数
    public static int bit1counts(int num){
        int count =0;
        while (num!=0){
            int rightOne = getRightOne(num);
            count++;
            num^=rightOne;
        }
        return count;
    }

题目2的代码

public static void printOddTimesNums2(int[] arr){
        int eor =0;
        //先求出 这两个数异或的结果
        for (int i=0;i<arr.length;i++){
            eor^=arr[i];
        }

        // 因为a 和b 是两种数
        //获取异或结果的最右边的1
        int rightone = getRightOne(eor);

        //分离出其中一个数
        int a = 0;
        for (int i=0;i<arr.length;i++){
            if ((arr[i] & rightone) !=0){
                a^=arr[i];
            }
        }
        System.out.println("a="+a+"  b="+(a^eor));
    }