常用的位操作

常用的位操作

学习 labuladong 的算法小抄,原文地址https://labuladong.gitee.io/algo/4/32/114/

一、几个有趣的位操作

  1. 利用或操作 | 和空格' '将英文字符转换为小写
('a' | ' ') = 'a'
('A' | ' ') = 'a'
  1. 利用与操作 & 和下划线'_'将英文字符转换为大写
('a' & '_') = 'A'
('A' & '_') = 'A'
  1. 利用异或操作 ^ 和空格' '进行英文字符大小写互换
('a' ^ ' ') = 'A'
('A' ^ ' ') = 'a'

原理

在这里插入图片描述
在ASCII码表中,空格的ASCII码为32,二进制表示为00100000;下划线的ASCII码为95,二进制表示为01011111.大写字母A~Z的ASCII码为65~90,二进制表示为01000001~ 01011010。小写字母a~z的ASCII码为97~122,二进制为01100001~ 01111010。

  1. 判断两个数是否异号
int x = -1, y = 2;
boolean f = ((x ^ y) < 0); //true

int x = 3, y = 2;
boolean f = ((x ^ y) < 0); //false

原理

在计算机系统中,数值一律用补码表示和存储。含符号位和数值位,符号位:0表示“正”; 1表示“负”。
正数的补码 = 原码
负数的补码 = 负数的原码取反(符号位保持不变)+ 1
原码表示法在数值前面增加了一位符号位(即最高位为符号位):正数该位为0,负数该位为1(0有两种表示:+0和-0),其余位表示数值的大小。
所以,正数的补码最高位是0,负数的补码最高位是1。而1^0 = 1,0^0=1, 1^1=0,可以看到只有两异号数进行异或操作后其最高位为1,同号数最高位则为0。

二、n & (n-1) 的运用

n & (n-1) 这个操作是算法中常见的,作用是消除数字 n 的二进制表示中的最后一个 1

在这里插入图片描述
在二进制表示中,如果第k值为1,则表示2^(k-1),所以对于数字n,如果要表示n-1,其最后一位1应置为0,低位全部置为1。

例题

  1. 计算汉明权重(Hamming Weight)
    在这里插入图片描述
    因为 n & (n - 1) 可以消除最后一个 1,所以可以用一个循环不停地消除 1 同时计数,直到 n 变成 0 为止。
public class Solution {
    // you need to treat n as an unsigned value
    public int hammingWeight(int n) {
        int res = 0;
        while(n != 0) {
            n = n & (n - 1);
            res++;
        }
        return res;
    }
}
  1. 判断一个数是不是 2 的指数
    一眼看出解法,判断是否只有一位1即可,注意0和负数即可
class Solution {
    public boolean isPowerOfTwo(int n) {
        if(n <= 0) return false;
        n &= n-1;
        return n == 0;
    }
}

三、a ^ a = 0 的运用

异或运算的性质是需要我们牢记的:

一个数和它本身做异或运算结果为 0,即 a ^ a = 0;一个数和 0 做异或运算的结果为它本身,即 a ^ 0 = a。

例题

  1. 查找只出现一次的元素
    在这里插入图片描述对于这道题目,我们只要把所有数字进行异或,成对儿的数字就会变成 0,落单的数字和 0 做异或还是它本身,所以最后异或的结果就是只出现一次的元素。
class Solution {
    public int singleNumber(int[] nums) {
        int res = 0;
        for(int n : nums) {
            res ^= n;
        }
        return res;
    }
}
  1. 寻找缺失的元素

再回顾一下异或运算的性质:一个数和它本身做异或运算结果为 0,一个数和 0 做异或运算还是它本身。

而且异或运算满足交换律和结合律,也就是说:
2 ^ 3 ^ 2 = 3 ^ (2 ^ 2) = 3 ^ 0 = 3
而这道题就可以通过这些性质巧妙算出缺失的那个元素,比如说 nums = [0,3,1,4]
在这里插入图片描述
了容易理解,我们假设先把索引补一位,然后让每个元素和自己相等的索引相对应:
在这里插入图片描述
这样做了之后,就可以发现除了缺失元素之外,所有的索引和元素都组成一对儿了,现在如果把这个落单的索引 2 找出来,也就找到了缺失的那个元素。

如何找这个落单的数字呢,只要把所有的元素和索引做异或运算,成对儿的数字都会消为 0,只有这个落单的元素会剩下,也就达到了我们的目的:
在这里插入图片描述

int missingNumber(int[] nums) {
    int n = nums.length;
    int res = 0;
    // 先和新补的索引异或一下
    res ^= n;
    // 和其他的元素、索引做异或
    for (int i = 0; i < n; i++)
        res ^= i ^ nums[i];
    return res;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值