【算法篇】3.位运算

位运算

  • 基础公式:x^x=0、x&(x-1)(消最右 1)、x&1(奇偶)是核心中的核心;
  • 高频技巧:lowbit 公式(x&-x)、2 的幂判断(x&(x-1)==0)、异或交换数需熟练;
  • 实战场景:唯一数、统计 1 的个数、矩阵区域和(结合位运算)等场景直接套用对应公式。
x ^ 0 = x 任何数异或 0 等于自身 101 ^ 000 = 101
x ^ x = 0 任何数异或自身等于 0(核心!) 101 ^ 101 = 000
x & 0 = 0 任何数与 0 等于 0 101 & 000 = 000
x & x = x 任何数与自身等于自身 101 & 101 = 101

x & 1 = 1 → 奇数

**(x >> k) & 1**-> 获取 x 的第 k 位(从 0 开始,最低位是第 0 位)

  • 统计二进制中 1 的个数、判断某一位是否为 1

x & ~(1 << k)->将 x 的第 k 位设为 0(其他位不变)

  • 取消标记、清除某一位

x & (1 << k) != 0 ->判断 x 的第 k 位是否为 1

  • 状态检查

x & (x - 1)---> 消除 x 的二进制中「最右边的 1」

  • 统计 1 的个数、判断是否为 2 的幂

**x & -x**-> 提取 x 的二进制中「最右边的 1」

  • 树状数组、拆分二进制位 、

x & (x - 1) == 0-> 判断 x 是否为 2 的幂(x>0)

  • 2 的幂数识别

必会:

x: 1010000

x-1: 1001111

x&(x-1) 1000000

常用操作

1. 交换两个数(无需临时变量)

  • 面试常考,无临时变量交换数值(注意:a 和 b 不能指向同一内存地址)
java 复制代码
// 核心:a ^ b ^ b = a,a ^ a ^ b = b
a = a ^ b;
b = a ^ b; // 等价于 (a^b)^b = a
a = a ^ b; // 等价于 (a^b)^a = b

2.找数组中唯一出现一次的数(其他数出现两次)

  • 核心逻辑 :x ^ x = 0,0 ^ x = x,两次出现的数异或抵消,最终剩下唯一数。
java 复制代码
int res = 0;
for (int num : nums) res ^= num;
return res;

3. 统计二进制中 1 的个数

java 复制代码
int count = 0;
while (x != 0) {
    x = x & (x - 1); // 消除最右边的1
    count++;
}
return count;

4. 快速计算绝对值(无分支)

java 复制代码
int sign = x >> 31; // 最高位:正数0,负数-1(补码)
int abs = (x ^ sign) - sign;

面试题 01.01. 判定字符是否唯一

java 复制代码
class Solution {
    public boolean isUnique(String astr) {
        int n=astr.length();
        if(n>26) return false;

        int bitmap=0; //只用整型变量(26个长度)
        for(int i=0;i<n;i++){
            int idx=astr.charAt(i)-'a'; //第几个位置
            //取第idx位看看是1还是0
            //1:该字符在 0 表不在
            if(((bitmap>>idx)&1)==1) return false; //存在
            //不存在,就得把第idx位设置为1,添加存在
            else  bitmap=bitmap|(1<<idx);
            //100
            //010
            //110(设置第三位为1)
        }
        return true;
    }
}

268. 丢失的数字

利用这两个公式:x^x=0 0^x=x

  • 自己补0-n的数出来
java 复制代码
class Solution {
    public int missingNumber(int[] nums) {
        // x^x=0  0^x=x
        int n=nums.length;
        int ret=0;
        //[0,n-1]
        for(int i=0;i<n;i++){
            ret=ret^nums[i];
            ret=ret^i;
        }
        ret=ret^n; //n
        return ret;

    }
}

136. 只出现一次的数字(同上)

对数组所有元素进行异或^的值就是最终结果。注:两个值不同,则异或结果为1;两个值相同,异或结果为0。

java 复制代码
class Solution {
    public int singleNumber(int[] nums) {
        // x^x=0  0^x=x
        int n=nums.length;
        int ret=0;
        //[0,n-1]
        for(int i=0;i<n;i++){
            ret=ret^nums[i];
        }
        return ret;
    }
}

338. 比特位计数

java 复制代码
class Solution {
    public int[] countBits(int n) {
        int[] ret=new int[n+1];

        for(int i=0;i<=n;i++){
            //统计1的个数
            ret[i]=oneCnt(i);
        }
        return ret;
    }
    //统计一个数二进制1的个数
    public int oneCnt(int n){
        //1010
        int cnt=0;
        while(n!=0){
            if((n&1)==1) cnt++;
            n=n>>1;
        }
        return cnt;
    }
}

统计函数修改为这样,更优雅

java 复制代码
public int oneCnt(int n){
        //1010
        int cnt=0;
        while(n!=0){
            n=n&(n-1);//去掉最右边的1
            cnt++;
        }
        return cnt;
    }

最优解法(DP + 位运算,O (n))

利用位运算的规律优化,核心思路是「状态转移」:

核心规律:

对于任意整数 i:

  • 如果 i 是偶数 → i = 2 * (i/2) → 二进制是 i/2 左移 1 位(末尾加 0)→ 1 的个数和 i/2 相同 → dp[i] = dp[i/2];
  • 如果 i 是奇数 → i = 2 * (i/2) + 1 → 二进制是 i/2 左移 1 位 + 末尾加 1 → 1 的个数 = i/2 的个数 + 1 → dp[i] = dp[i/2] + 1。

简化公式(用位运算替代除法 / 取模):

  • i/2 等价于 i >> 1(右移 1 位);
  • i 是奇数等价于 i & 1 == 1(最后一位是 1);→ 最终状态转移方程:dp[i] = dp[i >> 1] + (i & 1)。
java 复制代码
class Solution {
    public int[] countBits(int n) {
        int[] dp = new int[n + 1];
        for (int i = 1; i <= n; i++) { // 0的1的个数是0,从1开始算
            dp[i] = dp[i >> 1] + (i & 1); // 核心公式
        }
        return dp;
    }
}

461. 汉明距离

核心转化(关键一步)

两个数二进制位「不同」→ 对应位异或(^)结果为 1,「相同」则为 0。因此:

  • 第一步:计算 x ^ y(记为 num),得到一个数,其二进制中「1 的位置」就是 x 和 y 不同的位;
  • 第二步:统计 **num** 中 1 的个数,就是汉明距离。
java 复制代码
class Solution {
    public int hammingDistance(int x, int y) {
        //对应位置异或
        //1.不同为1  2.相同为0
        //本质数多少个1
        int num=x^y;
        int cnt=0;
        while(num!=0){
            num=num&(num-1); //去掉最右边1
            cnt++;
        }
        return cnt;
    }
}
相关推荐
天赐范式4 小时前
天赐范式第181天:让视图开始切换——PBFT视图切换与从safety到liveness的跨越
python·pbft·数字生命·天赐范式·动态运行时·拜占庭容错
sugarzhangnotes4 小时前
【无标题】
开发语言·人工智能·python
geovindu4 小时前
rust: Composite Pattern
开发语言·后端·设计模式·rust·组合模式
SearchMan4 小时前
还有人不知道&和&&的区别?
后端
想用offer打牌4 小时前
Personal Agent爆火 - 它到底是个什么
人工智能·后端·ai编程
IT_陈寒4 小时前
Vue的v-if和v-for混用居然是个天坑
前端·人工智能·后端
weixin_447195294 小时前
【无标题】
pytorch·python
迅猛龙办公室4 小时前
Python实现简单的人名对话
python
苏打豆5 小时前
CRISP源码阅读——基于ROS2力矩反馈控制器与速度加速度零空间
c++·线性代数·算法·矩阵·机器人
用户1305180712155 小时前
JDBC学习DAY2:从Statement到PreparedStatement
java