算法基础1.6位运算
前言
位运算这东西其实我一直都是能避免就避免,但是前一段时间老师上课突然讲了一个位运算的用法,而今天刚好又在算法课上学到了位运算(算法课上没有深究,只是讲了两个用法),所以我想着干脆直接借着这次机会把这方面搞得全面一点一劳永逸得了,所以我查了大量的资料,也大概总结出来了一些知识。
这篇文章将不再依托于课上所讲的东西,文章内容基本都是我自己总结得到的,算法课上讲的两个用法我会总结到位运算用法的部分里面
常见的用法其实用语言都不是很好解释,所以我基本都是自己做的图片,快累死我了。ProcessOn真是个好网站,让免费在线作图。
正文
基础知识
-
& 按位与
- 如果两个相应的二进制位都为1,则该位的结果值为1,否则为0
-
| 按位或
- 两个相应的二进制位中只要有一个为1,该位的结果值为1
-
^ 按位异或
- 若参加运算的两个二进制位值相同则为0,否则为1
-
~ 取反
- ~是一元运算符,用来对一个二进制数按位取反,即将0变1,将1变成0
-
<< 左移
- 用来将一个数的各二进制位全部左移n位,低位以0补充,高位越界后舍弃。
- 左移一位相当于乘2,但是要保证左移的过程中不会导致符号位发生改变
-
>> 右移
- 将一个数的各二进制位右移N位,移到右端的低位被舍弃,高位以符号位填充
- 右移一位相当于整除2,向下取整,(-3)>> 1 = -2 ,3 >> 1 = 1
- C++中使用
/2
为整除2,向0取整, (-3)/ 2 = -1,3 / 2 = 1 - 这个在竞赛里挺常用的,虽然目前来说右移带来的时间效益微乎其微,但是似乎已经成为了一种习惯。快排,归并中求中间值一般都是直接用右移
8+9 >> 1
是先运算加法,因为加法有限度比位移要高
优先级:
常见用法
先把y总讲的两个写下来
1. 求n二进制下的第k位
使用(n >> k) & 1
,注意,对于二进制来说,个位为第0位
我们先让二进制数右移k
位,这样就能让原本第k
位的数移动到第0位(也就是个位),然后让他与1进行按位与的操作。注意这里的1是十进制,转换为二进制为000....01
,前面的0会让n
的除了个位数的其他位都变成0,只有个位有判断的必要。如果个位为1,与1进行与运算就是1,反之为0 。而这两个结果转换为十进制后数值不变,所以刚刚好
2. 返回x的最后一位1
这个方法俗称lowbit
使用x & (~x+1)
,这个~x+1
就相当于是-x
,~
将符号位也进行了取反,其实就是先将x
的符号位取反变成-x
的反码形式,然后再对其他位进行取反并且+1,后面的这部操作就是计算-x
补码的过程,而程序中-x
用的就是补码。
这个原理可以通过一个例子就能解释的很清楚:
这里输出结果就是100...0
这一串的十进制
这里有一道题可以用到lowbit
代码如下:
3.交换两个变量的值
正常的方法就是新建一个临时变量,然后形成三角关系相互传递值。
而使用位运算则可以不创建这个临时变量,通过下面三行代码来实现
我们首先要明确几个公式,可自行推导
这里再说一下异或运算的含义:若参加运算的两个二进制位值相同则为0,否则为1
- 任意一个变量X与其自身进行异或运算,结果为0,即X^X=0
- 任意一个变量X与0进行异或运算,结果不变,即X^0=X
- 异或运算具有可结合性,即 a ^ b ^ c = (a ^ b) ^ c = a ^ (b ^ c)
- 异或运算具有可交换性,即a ^ b = b ^ a
现在来一步一步分析
a = a ^ b;
,这一步我们不需要实际知道a
变成了多少(一个没有任何规律的十进制数),只需要知道目前a
代表了a ^ b
这个集合就行b = a ^ b;
,现在我们把a
展开:b = a ^ b = a ^ b ^ b = a ^ (b ^ b) = a ^ 0 = a
,这里用到了结合律,然后再用公式1得知b ^ b =0
,公式2得出最后结果a = a ^ b;
,仍然展开,注意b
的值也变了:a = a ^ b = a ^ b ^ a = a ^ a ^ b = 0 ^ b = b
交换完毕。
4.判断奇偶性
先复习一下小学知识点:
- 奇 + 奇 = 偶
- 偶 + 偶 = 偶
- 奇 + 偶 = 奇
我们再想一下二进制转十进制的方法:2 ^ (x - 1) * x
,其中x >= 1
,代表位数。
所以除去第一位,后面的都是2 ^ n
(n >= 1),都是偶数。只有第一位是2 ^ 0 * x
,结果可能是1(奇数)或者0(偶数)
因此我们只需要判断第一位即可,余下位转化为十进制后的和一定是偶数。
- 如果第一位为0,第一位转换结果为0,偶 + 偶 = 偶
- 如果第一位为1,第一位转换结果为1,偶 + 奇 = 奇
代码如下:
这里的十进制1就是二进制的00000001
,所以其他的结果都是0,只有个位有判断按位与结果的必要。
5.求a的b次方
这里用到了数论的知识,但是但从过程中我们也可以理解其中的含义
假如我们要求a
的13次方,那么也就是b = 13
,而b
的二进制为1101
。
那么a ^ 1101 = a ^ 0001 * a ^ 0100 * a ^ 1000
,该步涉及到未学过的知识,我们暂认为公理
也就是说a ^ 13 = a ^ 1 * a ^ 4 * a ^ 8
,这样其实是可以理解的
- 这个思路的时间复杂度为
O(logn)
,比库函数pow
的O(n)
要快- 这个思路进行的步数其实就是
b
二进制的位数,而他的位数其实就是b
不断除2得到的,能被2除一次,位数就多1位 - 因此
O = 位数 = logb = logn
- 这个思路进行的步数其实就是
那么我们就可以给出下面的代码了
6.找出重复的数
注意题目要求:数组中,只有一个数出现奇数次,剩下都出现偶数次,找出出现奇数次的
这里与3中用的公式一样
- 任何数与0按位异或的结果都是他本身
- 任何数和自己按位异或的结果为0(每一位都相同,每一位结果都是0)
所以我们只需要设一个变量为0,将数组中所有数都与这个变量进行按位异或,那么最终这个变量的结果就是答案
我们可以进行一个模拟
- 数组为
[a , b , b , c ,c]
,这个数组里面数随机打乱,这里只是为了方便看所以写的是有序数组 - 设
ans = 0
- 进行运算
ans = ans ^ a ^ b ^ b ^ c ^ c = 0 ^ a ^ (b ^ b) ^ (c ^ c) = 0 ^ a ^ 0 ^ 0 = a
代码如下
7.用O(1)时间检测整数n是否是2的幂次
对于2的幂次n
,他的二进制应该符合下面两个规律
n > 0
n
的二进制里面只有一个1,因为2 ^ a + 2 ^ b
一定不是2的幂次(证明方法网上搜)
因此,如果我们忽略前导0,那么n
的二进制应该是100....0
这样的,那么我们在十进制方面的n-1
就应该是11...11
(位数比n
的少一位)。
假如n
的二进制有x
位,那么进行n & (n-1)
,前x-1
位都是0 & 1 = 0
,第x
位是1 & 0 = 0
(&后面的0是n-1
二进制的前导0),所以总体答案应该是n & (n-1) == 0
。
通过反证法可知非2幂次数不满足这个条件
因此代码如下
结语
位运算的用法多了去了,我真写不动了,我上面写的都是我查资料发现的简单一点的用法。
如果以后做题遇到新的我再补充,到时候估计会专门写个题解
写这篇,还有上一篇那个原码反码补码真的用了我好长时间。
__EOF__

本文链接:https://www.cnblogs.com/zaughtercode/p/17229490.html
关于博主:qq:1730119093 欢迎加我讨论
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角【推荐】一下。您的鼓励是博主的最大动力!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· .NET10 - 预览版1新功能体验(一)