【算法篇】3.位运算

位运算

  • 基础公式:x^x=0x&(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 = 00 ^ 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;
    }
}
相关推荐
Aaswk2 小时前
回溯算法的本质理解
c语言·算法·leetcode·力扣·剪枝
迷海2 小时前
力扣原题《分发糖果》,采用二分原则,纯手搓,待验证
c++·算法·leetcode
`Jay2 小时前
Python Redis连接池&账号管理池
redis·分布式·爬虫·python·学习
李白的粉2 小时前
基于springboot的新闻稿件管理系统
java·spring boot·毕业设计·课程设计·源代码·新闻稿件管理系统
玛卡巴卡ldf2 小时前
【LeetCode 手撕算法】(普通数组)53-最大子数组和、56-合并区间、189-轮转数组、238-除了自身以外数组的乘积
数据结构·算法·leetcode
2301_793804692 小时前
Python异步编程入门:Asyncio库的使用
jvm·数据库·python
j_xxx404_2 小时前
蓝桥杯基础--模拟
数据结构·c++·算法·蓝桥杯·排序算法
m0_488633322 小时前
C语言中结构体指针如何用 -> 取子数据及链表应用示例
c语言·数据结构·结构体指针·链表应用·指针操作
Sakinol#2 小时前
Leetcode Hot 100 ——动态规划part02
算法·leetcode·动态规划