算法【Java】—— 位运算

位运算总结

位运算的运算符:按位与(&),按位或(|),按位异或(^),按位取反(~),还有移位操作符 <<,>>

确定一个数 n 的第 x 位是0 还是 1
(n >> x) & 1 等于 1 / 0 来进行判断
将 n 的第 x 位修改为 1
n |= (1 << x)
将 n 的第 x 位修改为0
n &= ~(1 << x)
提取 n 二进制最右侧的 1
n & -n
删除 n 二进制最右侧的 1
n & (n - 1)
异或运算的规律

a ^ a = 0

a ^ 0 = a

a ^ b ^ c = a ^ c ^ b (满足交换律)
位运算的优先级可以通过我们手动添加括号来解决,这样就可以减少我们的记忆负担了。
位图思想:和哈希表类似,只不过位图采用的是比特位标记的方式来记录数字。

实战演练

只出现一次的数字 Ⅰ

https://leetcode.cn/problems/single-number/


由于除了一个元素只出现一次,其他元素均出现了两次,所以我们可以将所有的数字进行异或运算,最后得到的结果就是只出现一次的元素。

java 复制代码
class Solution {
    public int singleNumber(int[] nums) {
        int ans = 0;
        for(int x : nums) {
            ans ^= x;
        }
        return ans;
    }
}

只出现一次的数字 Ⅲ

https://leetcode.cn/problems/single-number-iii/


假设最后的答案分别是 a ,b

按照上一道题目的思路,我们将所有的数字进行异或得到的结果为 a ^ b

现在我们需要将他们两个分开来,如何分开?首先这两个数字一定是不相同的,那么在某一个比特位上一定不同,我们找到这个不同的比特位,然后根据这个不同的比特位最为划分标准,将数组所有的元素分成两组,这两组再分别进行异或操作,最后就会得到 a 与 b 这两个数字。

如何找到不同的比特位?因为异或的无进位相加(相同的异或结果为0,相异的异或结果为1),所有只要能找到比特位为1,这个位置就可以作为后续的判断标准。这里我们就可以使用n & -n 获取n 最后一位的 1

java 复制代码
class Solution {
    public int[] singleNumber(int[] nums) {
        int sum = 0;
        for(int x : nums) {
            sum ^= x;
        }

        sum &= -sum;
        int ret1 = 0, ret2 = 0;
        for(int x : nums) {
            if((x & sum) == 0) {
                ret1 ^= x;
            } else {
                ret2 ^= x;
            }
        }

        return new int[]{ret1,ret2};
    }
}

位 1 的个数

https://leetcode.cn/problems/number-of-1-bits/


直接使用 n & (n-1) 即可

java 复制代码
class Solution {
    public int hammingWeight(int n) {
        int count = 0;
        while(n != 0) {
            n &= (n-1);
            count++;
        }
        return count;
    }
}

比特位计数

https://leetcode.cn/problems/counting-bits/

java 复制代码
class Solution {
    public int[] countBits(int n) {
        int[] ans = new int[n+1];
        for(int i = 0; i <= n; i++) {
            int count = 0;
            int tmp = i;
            while(tmp != 0) {
                tmp &= (tmp - 1);
                count++;
            }
            ans[i] = count;
        }
        return ans;
    }
}

汉明距离

https://leetcode.cn/problems/hamming-distance/


计算不同的二进制位数,首先进行异或运算,然后计算这个结果有多少个 1 即可

java 复制代码
class Solution {
    public int hammingDistance(int x, int y) {
        int sum = x ^ y;
        int count = 0;
        while(sum != 0) {
            sum &= (sum-1);
            count++;
        }
        return count;
    }
}

判定字符是否唯一

https://leetcode.cn/problems/is-unique-lcci/description/


这里我们首先会想到哈希表,但是如果不适用额外的数据结构,我们可以考虑位图,用一个变量上的比特位来记录数据,正好字符串都是小写字母,也就是26种字母,使用 26 个比特位就可以保存数据的状态。

java 复制代码
class Solution {
    public boolean isUnique(String astr) {
        if(astr.length() > 26) { //鹊巢原理
            return false;
        }
        int bitSet = 0;//位图
        char[] arr = astr.toCharArray();
        for(char x : arr) {
            if((1 << (x-'a') & bitSet) != 0) {
                return false;
            } 
            bitSet |= (1 << (x-'a'));
        }
        return true;
    }
}

丢失的数字

https://leetcode.cn/problems/missing-number/description/


这道题可以使用等差数列求和公式来做

当然这里是位运算专题,我们可以使用为运算来做,首先我们可以将 0 ~ n 之间所有的数字进行异或运算,然后将数组里的所有数字和上一次异或的结果一起异或,这样就可以找到丢失的数字了。

java 复制代码
class Solution {
    public int missingNumber(int[] nums) {
        int sum = 0;
        for(int i = 0; i <= nums.length; i++) {
            sum ^= i;
        }
        for(int x : nums) {
            sum ^= x;
        }
        return sum;
    }
}

两整数之和

https://leetcode.cn/problems/sum-of-two-integers/description/


我们知道异或运算可以得到无进位相加的结果,我们知道当两个比特位都为 1 进行相加的时候是需要进位的,所以我们可以通过两个操作数按位与再左移一位就可以知道哪一些比特位需要进行进位,再次重复上述操作,知道最后没有需要进位,就可以得到两个数字相加的结果。

java 复制代码
class Solution {
    public int getSum(int a, int b) {
        while(b != 0) {
            int sum = a ^ b;
            b = (b & a) << 1;
            a = sum;
        }
        return a;
    }
}

只出现一次的数字 Ⅱ

https://leetcode.cn/problems/single-number-ii/description/


只有一个元素出现一次,其他元素均出现三次,那么我们可以进行比特位计数,计算每一位上所有的数字的 1 的个数,最后将结果 %3 ,就会得到只出现一次的元素的那一位比特位上是 1 还是 0.

一个整型数据由有32 个比特位,所以进行三十二次循环。

java 复制代码
class Solution {
    public int singleNumber(int[] nums) {
        int ans = 0;
        for(int i = 0; i < 32; i++) {
            int count = 0;
            for(int x : nums) {
                if(((1 << i) & x) != 0) {
                    count++;
                }
            }
            if(count % 3 == 1) {
                ans |= (1 << i);
            }
        }
        return ans;
    }
}

消失的两个数字

https://leetcode.cn/problems/missing-two-lcci/


这道题我们先将 1 ~ n 和数组所有的数字异或就可以得到消失的两个数字的异或结果(这里记为 a ^ b),现在就是分离他们了,很简单,和上面一样,找到最后的比特位为 1,以这个为标准开始划分,将 1 ~ n 和数组所有的元素都进行划分就可以得到最终的两个数字了。

java 复制代码
class Solution {
    public int[] missingTwo(int[] nums) {
        int n = nums.length;
        int sum = 0;
        for(int i = 1; i <= n + 2; i++) {
            sum ^= i;
        }
        for(int x : nums) {
            sum ^= x;
        }

        sum &= -sum;
        int ret1 = 0, ret2 = 0;
        for(int i = 1; i <= n + 2; i++) {
            if((i & sum) != 0) {
                ret1 ^= i;
            } else {
                ret2 ^= i;
            }
        }
        for(int x : nums) {
            if((x & sum) != 0) {
                ret1 ^= x;
            } else {
                ret2 ^= x;
            }
        }
        return new int[]{ret1,ret2};
    }
}
相关推荐
火烧屁屁啦4 分钟前
【JavaEE进阶】初始Spring Web MVC
java·spring·java-ee
w_312345417 分钟前
自定义一个maven骨架 | 最佳实践
java·maven·intellij-idea
岁岁岁平安20 分钟前
spring学习(spring-DI(字符串或对象引用注入、集合注入)(XML配置))
java·学习·spring·依赖注入·集合注入·基本数据类型注入·引用数据类型注入
武昌库里写JAVA23 分钟前
Java成长之路(一)--SpringBoot基础学习--SpringBoot代码测试
java·开发语言·spring boot·学习·课程设计
Q_192849990630 分钟前
基于Spring Boot的九州美食城商户一体化系统
java·spring boot·后端
张国荣家的弟弟1 小时前
【Yonghong 企业日常问题 06】上传的文件不在白名单,修改allow.jar.digest属性添加允许上传的文件SH256值?
java·jar·bi
ZSYP-S1 小时前
Day 15:Spring 框架基础
java·开发语言·数据结构·后端·spring
yuanbenshidiaos1 小时前
C++----------函数的调用机制
java·c++·算法
唐叔在学习1 小时前
【唐叔学算法】第21天:超越比较-计数排序、桶排序与基数排序的Java实践及性能剖析
数据结构·算法·排序算法
是小崔啊1 小时前
开源轮子 - EasyExcel01(核心api)
java·开发语言·开源·excel·阿里巴巴