常见的位操作及其应用

概述

与、或、异或、取反或者移位运算这几种基本的位操作想必诸位读者并不陌生,如果我们能在某些合适场景下使用位运算,有些时候可以大大提高算法的效率。但由于本身位运算太过灵活,甚至某些技巧比较苦涩难懂,因而,本篇文章主要介绍几种常见的或者有趣的位操作,并且给出一些用到这些技巧的算法题目,便于读者练习。

有趣的操作

1. 大小写字母转换

  1. 利用 或操作和空格将英文字母转成小写
('a' | ' ') = 'a';
('A' | ' ') = 'a';
  1. 利用与运算 &和下划线将英文字符转换成大写
('b' & '_') = 'B';
('B' & '_') = 'B';
  1. 利用异或运算 ^和空格进行英文字符大小写互换
('d' ^ ' ') = 'D';
('D' ^ ' ') = 'd'

常用指数: 🔯🔯

容易指数:🔯🔯🔯

PS:上述技巧能够产生奇特效果的原因在于字符类型的数据都是通过ASCII进行编码的,字符本身其实就是数字,而刚好这些字符对应的数字通过位运算符就可以得到正确结果,此处就不展开来说了。

2. 判断两个数是否异号

int x = -1, y = 2;
boolean f = ((x ^ y) < 0); // true 两个int类型数据进行异或运算小于零证明异号

int x = 1, y = 2;
boolean f = ((x ^ y) < 0); // false 两个int类型数据进行疑惑运算大于零证明同好

常用指数:🔯🔯🔯

困难指数:🔯

PS:这个操作在我们判断两个数异号的时候 非常有用,一方面运算效率较高,另一方面可以减少if else 分支的使用。其背后的原理主要是一个正数补码的符号位和一个负数补码的符号位肯定想法,经过疑惑运算后,最后符号位结果肯定是 1(代表负数)。

3. 移除最后一位”1″

byte n = 10;
// n的二进制表示为: 0 0 0 0 1 0 1 0
//       异或运算  ^
//n-1的二进制表示为:0 0 0 0 1 0 0 1
n & (n-1); //结果为:0 0 0 0 1 0 0 0

常见的位操作及其应用

根据上边的注释以及示意图,整个操作过程应该不难理解

简单来说 n & (n -1 ) 主要作用:就是 消除数字n的二进制表示中的最后一个1.

常用指数:🔯🔯🔯🔯🔯

困难指数:🔯🔯

PS:这个操作特别常用,在好多leetcode题目中都有涉及,比如用来判断一个数的二进制数中1的个数。

4. 获取最后一个1

byte n = 10;
//结果为:0 0 0 0 0 0 1 0
(n & (n-1)) ^ n;

常用指数:🔯🔯🔯🔯

困难指数:🔯🔯🔯

PS:该操作刚好和操作3相反, 主要是为了获取数字n的二进制表示中的最后一个1.该操作通常会用在位标记的时候使用(可参考汉明距离)。

5. 异或运算的简单性质

a=0^a=a^0

0=a^a

常用指数:🔯🔯🔯🔯

困难指数:🔯

PS:这个性质在我们判断两个数是否相同的时候 非常常用。

应用

前边总结了那么多常用的位操作,下边来做几道题消化吸收一下刚刚所学的知识。

1. 只出现一次的数字

常见的位操作及其应用

这道题比较简单,说实话即使不用位运算我们也可以有好多种方法求解,比如可以用一个Map来对元素进行boolean标记来求解。但如果我们此处能想到用 异或运算的性质,那这道题目我们便可以简单优雅的解出来。

思路:用一个初值为0的变量不断和数组中的元素进行异或运算,最终得到的变量值便是最终结果。

原因:因为0和任何一个元素进行异或运算都是0,而任何两个相同元素进行异或之后的结果都是0,而题目中只有一个数是单独存在的,其他数都是2个,因而不断进行异或运算最后的结果必然是那个独特的数值,也就是最终的结果。

代码如下:

public int singleNumber(int[] nums) {
        int result = 0;
        for (int i =0;i

2. 汉明距离

常见的位操作及其应用

这个问题,我们常规思路可能是通过一次 for&#x5FAA;&#x73AF;并且在循环过程中判断不同位置的二进制位是否相同,同时做计数。

但同样的我们通过位运算可以比较快速的解决该问题

思路:通过一次异或运算,获取一个不同位置二进制数构成的一个整数,然后计算该整数中1的个数即为最终结果。在计算1个数的时候一个小技巧,可以通过n & (n-1)不断做移位运算来计算。

实现代码如下:

 public int hammingDistance(int x, int y) {
    x = x ^ y;
    int count = 0;
    while (x != 0) {
      x = x & (x - 1);
      count++;
    }
    return count;
  }

1. 只出现一次的数字 III

常见的位操作及其应用

这个问题跟只出现一次的数字很像,主要差别可能在这个唯一的数是 两个,而不是一个。同样的这个问题有很多常规的结果,比如街主要map来统计和标注每个数字出现的次数。但我们使用位运算的时候我们会发现其速度是极快的。

思路:

前面分析了此题目和原来题目的最大区别在于只出现一次的数字 不再是唯一的了,而变成了两个。因而我们考虑能否对着集合元素按照 某种标准做一个 分类,经过分类之后,每一个类别都分别包含一个特殊的元素以及若干相同的元素。此时该问题就转换成了问题1,那个简单的问题。

其实分类标准应该不难找,由于这两个元素是不相等的,因此这两个元素的某个二进制位必然是不相等。因此我们可能考虑根据 该二进制位为0或者1将数组分成A组合B组。而这个数组中其它数字要么就属于A组,要么就属于B组。再对A组和B组分别执行”异或”解法就可以得到A,B了。而要判断A,B在哪一位上不相同,只要根据A异或B的结果就可以知道了,这个结果在二进制上为1的位就说明A,B在这一位上是不相同的。

比如:

int a[] = {1, 1, 3, 5, 2, 2}

整个数组异或的结果为3^5,即 0x0011 ^ 0x0101 = 0x0110

0x0110,第1位(由低向高,从0开始)就是1。因此整个数组根据第1位是0还是1分成两组。

a[0] =1  0x0001  第一组

a[1] =1  0x0001  第一组

a[2] =3  0x0011  第二组

a[3] =5  0x0101  第一组

a[4] =2  0x0010  第二组

a[5] =2  0x0010  第二组

第一组有{1,1,5},第二组有{3,2,2},然后对这二组分别执行”异或”解法就可以得到5和3了。\

实现代码如下:

public int[] singleNumber(int[] nums) {
    int xorVal = 0;
    for (int num : nums) {
        xorVal ^= num;
    }
    // 获取最后一个 1
    xorVal = (xorVal & (xorVal - 1)) ^ xorVal;

    int res[] = new int[2];
    //根据和xorVal的与运算结果不同进行分类
    for (int num : nums) {
        if ((num & xorVal) == 0) {
            res[0] ^= num;
        } else {
            res[1] ^= num;
        }
    }
    return res;
}

总结

以上便是一些常用的位操作,以及对应的一些简单应用。其实关于微操作的技巧很多,有很多也非常有趣,其中有一个叫做 BitTwiddling Hacks 的外国网站收集了几乎所有位操作的黑科技玩法,感兴趣的话,可以看一看哈!!

参考

  1. https://leetcode-cn.com/problems/single-number-iii/solution/java-yi-huo-100-yao-shi-kan-bu-dong-wo-jiu-qu-chu-/
  2. https://greyireland.gitbook.io/algorithm-pattern/shu-ju-jie-gou-pian/binary_op#chang-jian-ti-mu

Original: https://www.cnblogs.com/goWithHappy/p/bit-operation.html
Author: vcjmhg
Title: 常见的位操作及其应用

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/599383/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

  • MySQL系统安装与部署

    数据库版本标准化 1.确认Supported Platforms https://www.mysql.com/support/ 2.确认安装版本 推荐:5.7.22 ,8.0.20…

    数据库 2023年5月24日
    074
  • [LeetCode]26. 删除排序数组中的重复项

    给定一个排序数组,你需要在 原地 删除重复出现的元素,使得每个元素只出现一次,返回移除后数组的新长度。 不要使用额外的数组空间,你必须在 原地 修改输入数组 并在使用 O(1) 额…

    数据库 2023年6月9日
    0115
  • ​探秘 Web 水印技术

    Web 水印技术在信息安全和版权保护等领域有着广泛的应用,对防止信息泄露或知识产品被侵犯有重要意义。水印根据可见性可分为可见水印和不可见水印(盲水印),本文将分别予以介绍,带你探秘…

    数据库 2023年6月14日
    0100
  • Golang环境安装

    一、下载地址 Golang: Downloads – The Go Programming Language GoLand编辑器: Download GoLand: A…

    数据库 2023年6月14日
    058
  • 常用API(Java)

    Object 场景:当我们使用toString方法想要输出对象变量时,官方提供的toString方法会直接输出对象所在的地址,而不是我们想要的对象变量,所以我们要把toString…

    数据库 2023年6月16日
    077
  • SQL Server中STATISTICS IO物理读和逻辑读的误区

    SQL Server中STATISTICS IO物理读和逻辑读的误区 大家知道,SQL Server中可以利用下面命令查看某个语句读写IO的情况 SET STATISTICS IO…

    数据库 2023年6月9日
    070
  • Linux(CentOS)安装Redis保姆级教程

    Linux(CentOs)安装Redis教程 一,下载Redis(两种方式) 1,找到redis官网(https://redis.io/download ) 如果想下载指定版本就去…

    数据库 2023年6月11日
    078
  • DDD(Domain Driver Design)领域驱动模型

    Domain Primitive(DP) DP概念DP 是 DDD 中的一个基础概念,是 DDD 中可以执行的一个最小单元,最直接的体现是,将业务相关的参数定义在一个特定的领域中(…

    数据库 2023年6月6日
    0109
  • java四种访问修饰符及各自的权限

    1.public,即共有的,是访问权限限制最宽的修饰符。被public修饰的类、属性、及方法不仅可以跨类访问,而且可以跨包访问。 2. protected,即保护访问权限,是介于p…

    数据库 2023年6月11日
    075
  • 我的创作纪念日

    机缘 2018 年 08 月 07 日是我的创作一周年纪念日。大三刚过完,我还是一名 IT 小白,也并没有考研的想法,当时应该是在实习,偶尔一次上网搜索代码问题的时候看到了 CSD…

    数据库 2023年6月6日
    085
  • 重新学习数据库(1)

    单元概述 通过本章的学习能够了解MySQL结构查询语言的概念,掌握SELECT查询语句的基本语法,掌握SELECT查询语句中过滤条件的使用,掌握过滤条件中比较运算符和逻辑运算符的使…

    数据库 2023年5月24日
    060
  • 数据库的备份和恢复命令,使用视图,索引,事务

    备份库 直接在cmd窗口中直接输入,结束不需要输入; mysqldump -h端口号 -u用户名 -p密码 数据库名>备份地址 恢复库 在cmd窗口中进行 1、连接数据库 m…

    数据库 2023年6月16日
    0112
  • 工具 | pg_recovery 设计原理与源码解读

    作者:张连壮 PostgreSQL 研发工程师从事多年 PostgreSQL 数据库内核开发,对 citus 有非常深入的研究。 本文将带大家了解 pg_recovery 工具的实…

    数据库 2023年5月24日
    077
  • Redis安装

    Redis For Windows 安装 Redis 官方只提供源码包,不支持Windows 老版本 Windows 版本下载地址(最高版本为3)老版本地址 新版本 Windows…

    数据库 2023年6月6日
    081
  • Spring(三)-AOP

    1、名词理解 切面(Aspect): 含有前置通知,后置通知,返回通知,异常抛出通知,环绕通知等方法的 类; 通知(Advice): 对原方法进行添加处理(如日志等)的 方法; 切…

    数据库 2023年6月16日
    071
  • 【干货总结】:可能是史上最全的MySQL和PGSQL对比材料

    版权情况:PostgreSQL 11(免费开源)、MySQL5.7 Oracle官方社区版(免费开源)1. CPU限制 PGSQL 没有CPU核心数限制,有多少CPU核就用多少 M…

    数据库 2023年6月9日
    058
亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球