算法学习100天——1 位运算

一、位运算

  1. 5种符号 ^(异或) &(与) ~(非) |(或) >>(右移) <<(左移)

    异或

    可以看做是 二进制无进位相加

    0^N = NN^N = 0

    满足交换律结合律

    三者结合使用

    求最右侧为1的那一位: N & (~N + 1)

    求某一位是否为0:设a = 00000100, N & a ==0 ? N的第三位为0 : N第三位不为0

  2. 例题:

    a. 一个数组,有一个数出现了奇次,其他出现了偶次,找到这个数

    /**
     * 这个题没有难度,只是利用异或的性质
     * a^b^c^b^c = a^b^b^c^c = a^0^0 = a
     */
    
    class Solution {
        public int singleNumber(int[] nums) {
            int m = 0;
            for(int n : nums){
                m ^= n;
            }
            return m;
        }
    }
    

    b. 一个数组,有两个数出现了奇次,其他出现了偶次,找到这两个数

    /**
     * 解题思路:
     * 1. 通过位运算得到a^b
     * 2. 不同的两个数异或,结果中总有一位为1,根据这个位置为1的情况,
     * 可以将数组中所有数分为两类,一类是该位为1,一类是为0
     * 3. 因为是根据a^b的结果来分的,则a和b一定分布于不同的两组中
     * 4. 再将两组分别异或,就得到了a和b
     * 【注】第4步也可以这样:
     * 将a^b的值与某一组异或,一定能得到a或b,再与a^b异或,得到另一个
     *
     * 这个题的难度在于:
     * 1.用位运算只能取得这个两个奇次异或(即a^b)的值
     * 2.根据a^b中为1的位将数组分为两组
     */
    
    class Solution {
        public int[] singleNumbers(int[] nums) {
            int m = 0;
            // 1.找到a^b
            for(int n : nums){
                m = m ^ n;
            }
            // 2.根据a^b的结果为1的那一位,将数组分为两组数
            int a = m;
            // 这里得到异或结果最右侧为1的那一位,m&(按位取反再加一)
            int rightOne = m & (~m + 1);
            for(int n : nums){
                // 这里判断n的那一位是否0,是的话就与a^b异或
                if((n & rightOne) == 0){
                    a ^= n;
                }
            }
            return new int[]{a, a ^ m};
        }
    }
    
posted @ 2022-02-26 10:49  浪漫主义程序员  阅读(42)  评论(0编辑  收藏  举报