与运算()、或运算(|)、异或运算(^)的本质 及 用途,文末附加 位运算面试题

article/2025/8/21 10:10:31

目录

一:与运算符(&)and

1、运算规则:

2、例如:3&5

3、用途:

1)判断 奇偶性

2)清零。

3)取一个数中指定位

二:或运算(|)or

1、运算规则:

2、例如:3|5 

3、用途:

1)对一个数据的某些位 - 置 1

2)强行变成最接近的偶数

三:异或 运算符(^)xor

1、运算规则:

2、例如:3^5

3、用途:

1)使特定位翻转找一个数

2)与 0 相异或,保留原值

异或的几条性质:

3)多项式除法

4)简单的加密

5)要交换两个变量的值 - 不用引入中间变量

四:左移运算符(<<)

五:右移运算符(>>)

六:面试题实战

1、小算法:

1)乘 2 的非负整数次幂

2)除以 2 的非负整数次幂

3)判断一个数是不是 2 的正整数次幂

4)对 2 的非负整数次幂取模

5)取绝对值

6)取两个数的最大/最小值

7)判断符号是否相同

8)获取一个数二进制的某一位

2、1-1000 放在含有 1001 个元素的数组中,只有唯一的一个元素值重复,其它均只出现一次。

2'、变种:给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现了三次。找出那个只出现了一次的元素。

3、一系列数中,除两个数外其他数字都出现过两次,求这两个数字 (综合&和^):

4、计算数值

5、二进制枚举

6、不用加减乘除做加法

7、二进制中1的个数

法一:与(&)运算 与 左移(<<)运算相结合

法二:是运用 n&(n-1)。


一:与运算符(&)and

1、运算规则:

0&0=0;0&1=0;1&0=0;1&1=1

即:两个同时为1,结果为1,否则为0

2、例如:3&5

十进制3转为二进制的3:0000 0011

十进制5转为二进制的5:0000 0101

------------------------结果:0000 0001 ->转为十进制:1

即:3&5 = 1

3、用途:

1)判断 奇偶性

一个数 and 1 的结果就是取二进制的最末位。

这可以用来判断一个整数的奇偶

二进制的 最末位为 0 表示该数为偶数,最末位为 1 表示该数为奇数

2)清零。

如果想将一个单元清零,即使其全部二进制位为0,只要与一个各位都为零的数值相与,结果为零。

3)取一个数中指定位

方法:找一个数,对应X要取的位,该数的对应位为1,其余位为零,此数与X进行“与运算”可以得到X中的指定位。

例:设X=10101110,

   取X的低4位,用 X & 0000 1111 = 00001110 即可得到;

   还可用来取X的2、4、6位。

二:或运算(|)or

1、运算规则:

0|0=0;  0|1=1;  1|0=1;   1|1=1;

即 :参加运算的两个对象,一个为1,其值为1。

2、例如:3|5 

即 00000011 | 0000 0101 = 00000111,因此,3|5=7。 

3、用途:

1)对一个数据的某些位 - 置 1

方法:找到一个数,对应X要 置 1 的位,该数的对应位为 1,其余位为零。此数与 X 相或 可使 X 中的某些位 - 置1

例:将 X=10100000 的低 4 位 置 1 ,用 X | 0000 1111 = 1010 1111 即可得到

2)强行变成最接近的偶数

变为偶数,即:把二进制最末位变成 0

对这个数 or 1 之后再减一就可以了,就能把这个数强行变成最接近的偶数

使 a 的最低位为0,可以表示为:a & ~1。

~1的值为 1111 1111 1111 1110,再按"与"运算,最低位一定为0

三:异或 运算符(^)xor

1、运算规则:

0^0=0;  0^1=1;  1^0=1;   1^1=0;

即:参加运算的两个对象,如果两个位为“异”(值不同),则该位结果为1,否则 值相同,则为 0

两个位 值相同得 0,不同得 1

2、例如:3^5

=  0000 0011 | 0000 0101 =0000 0110,因此,3^5 = 6

3、用途:

1)使特定位翻转找一个数

对应X要翻转的个位,该数的对应位为1,其余位为零,此数与X对应位异或即可。

例:X=10101110,使X低4位翻转,用X ^0000 1111 = 1010 0001即可得到。

2)与 0 相异或,保留原值

X ^ 00000000 = 1010 1110。

异或其实就是不进位加法,如1+1=0,,0+0=0,1+0=1。

异或的几条性质:

1、交换律

2、结合律(即 (a^b)^c == a^(b^c) )

3、对于任何数 x,都有 x^x=0,x^0=x

4、自反性:  a^b^b = a^0 = a;

3)多项式除法

不过它最重要的性质还是自反性:A XOR B XOR B = A

即:对给定的数 A,用同样的运算因子(B)作两次 异或 运算后仍得到 A 本身。

0 和任意数字进行 异或 操作结果为 数字本身

两个相同的数字进行 异或 的结果为 0

两次 异或 同一个数 最后结果不变

这是一个神奇的性质,利用这个性质,可以获得许多有趣的应用。

4)简单的加密

比如我想对我 MM 说1314520,但怕别人知道,于是双方约定拿我的生日19880516作为密钥。

1314520 xor 19880516 = 20665500,我就把 20665500 告诉MM。

MM 再次计算 20665500 xor 19880516 的值,得到 1314520,于是她就明白了我的企图

5)要交换两个变量的值 - 不用引入中间变量

所有的程序教科书都会向初学者指出,要交换两个变量的值,必须要引入一个中间变量。

但如果使用异或,就可以节约一个变量的存储空间:

设有A,B两个变量,存储的值分别为a,b,则以下三行表达式将互换他们的值 表达式 (值) :

a=a^b;

b=b^a;

a=a^b;

四:左移运算符(<<)

将一个运算对象的各二进制位全部左移若干位(左边的二进制位丢弃,右边补0)。

例:a = a<< 2将a的二进制位左移2位,右补0,

左移1位后a = a *2; 

若左移时舍弃的高位不包含1,则每左移一位,相当于该数乘以2

五:右移运算符(>>)

将一个数的各二进制位全部右移若干位,正数左补0,负数左补1,右边丢弃。

操作数每右移一位,相当于该数除以2

例如:a = a>> 2 将a的二进制位右移2位,

左补0 or 补1得看被移数是正还是负。

六:面试题实战

1、小算法:

1)乘 2 的非负整数次幂

int mulPowerOfTwo(int n, int m) {  // 计算 n*(2^m)return n << m;
}

2)除以 2 的非负整数次幂

int divPowerOfTwo(int n, int m) {  // 计算 n/(2^m)return n >> m;
}

注意:

我们平常写的除法是向 0 取整,而这里的右移是向下取整(注意这里的区别),

即当数大于等于 0 时两种方法等价,当数小于 0 时会有区别,如: -1 / 2 的值为 0 ,而 -1 >> 1 的值为 −1 。

3)判断一个数是不是 2 的正整数次幂

bool isPowerOfTwo(int n) { return n > 0 && (n & (n - 1)) == 0; }

4)对 2 的非负整数次幂取模

int modPowerOfTwo(int x, int mod) { return x & (mod - 1); }

5)取绝对值

int Abs(int n) {return (n ^ (n >> 31)) - (n >> 31);/* n>>31 取得 n 的符号,若 n 为正数,n>>31 等于 0,若 n 为负数,n>>31 等于 -1若 n 为正数 n^0=n, 数不变,若 n 为负数有 n^(-1)需要计算 n 和 -1 的补码,然后进行异或运算,结果 n 变号并且为 n 的绝对值减 1,再减去 -1 就是绝对值 */
}

6)取两个数的最大/最小值

// 如果 a>=b,(a-b)>>31 为 0,否则为 -1
int max(int a, int b) { return b & ((a - b) >> 31) | a & (~(a - b) >> 31); }
int min(int a, int b) { return a & ((a - b) >> 31) | b & (~(a - b) >> 31); }

7)判断符号是否相同

bool isSameSign(int x, int y) {  // 有 0 的情况例外return (x ^ y) >= 0;
}

8)获取一个数二进制的某一位

// 获取 a 的第 b 位,最低位编号为 0
int getBit(int a, int b) { return (a >> b) & 1; }

2、1-1000 放在含有 1001 个元素的数组中,只有唯一的一个元素值重复,其它均只出现一次。

每个数组元素只能访问一次,设计一个算法,将它找出来;不用辅助存储空间,能否设计一个算法实现?

解法一:

显然已经有人提出了一个比较精彩的解法,将所有数加起来,减去1+2+...+1000的和。
这个算法已经足够完美了,相信出题者的标准答案也就是这个算法,唯一的问题是,如果数列过大,则可能会导致溢出。

解法二:

异或就没有这个问题,并且性能更好。
将所有的数全部异或,得到的结果 再与 1^2^3^...^1000 的结果进行 再异或,得到的结果就是重复数。

2'、变种:给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现了三次。找出那个只出现了一次的元素。

分析:

这题和上一题的思路略有不同,这题其他数字出现了3次,那么我们如果直接使用位运算异或操作的话无法直接找到结果

需要巧妙的运用二进制的其他特性:判断整除求余操作。

即判断所有数字二进制 1 的总个数和 0 的总个数一定有一个不是三的整数倍,

如果 0 不是三的整数倍那么就说明结果的该位二进制数字为0,同理否则为1.

在具体的操作实现上,问题中给出数组中的数据在int范围之内,那么我们就可以在实现上可以对int的32个位每个位进行依次判断该位1的个数求余3后是否为1,如果为1说明结果该位二进制为1可以将结果加上去。最终得到的值即为答案。

具体代码为:

class Solution {public int singleNumber(int[] nums) {int value=0;for(int i=0;i<32;i++){int sum=0;for(int num:nums){if(((num>>i)&1)==1){sum++;}}if(sum%3==1)value+=(1<<i);}return value;}
}

3、一系列数中,除两个数外其他数字都出现过两次,求这两个数字 (综合&和^):

( 题目链接:http://gdutcode.sinaapp.com/problem.php?cid=1051&pid=7 )

限制条件:按照从小到大的顺序输出.例如 2 2 1 1 3 4.最后输出的就是 3 和 4

这个题就是首先在输入的时候一直异或,就可以把这两个数异或的乘积找出来,就比如上例中 x=3^4;

然后找一个变量 tmp 来分开这两个数。

按位 与 的话可以发现会分开这两个数,分别存在 num1 和 num2 中

 思路:

具体思路就是想办法 将数组逻辑上一分为二

先异或一遍到最后得到一个数,得到的肯定是 a^b (假设两个数值分别为a和b)的值。

在看异或^的属性:不同为1,相同为0.

也就是说最终这个结果的二进制为 1 的位置上 a 和 b 是不相同的。

而我们可以找到这个第一个不同的位,然后将数组中的数分成两份,

该位为 0 的进行异或求解得到其中一个结果 a,该位为 1 的进行异或求解得到另一个结果 b。

图示:

代码示例:

#include <cstdio>
#include <algorithm>
#include <iostream>
#include <cstring>
using namespace std;
#define N 1000010
int a[N];int main()
{//freopen("why.in", "r", stdin);//freopen("why.out", "w", stdout);int t;scanf("%d", &t);while(t--) {int n;scanf("%d", &n);int x = 0;for(int i = 1; i <= n; i++) {scanf("%d", &a[i]); x ^= a[i];}int num1 = 0, num2 = 0;int tmp = 1;while(!(tmp & x)) tmp <<= 1;cout<<tmp<<endl;for(int i = 1; i <= n; i++) {if(tmp & a[i]) num1 ^= a[i];else num2 ^= a[i];}printf("%d %d\n", min(num1, num2), max(num1, num2));}return 0;
}

4、计算数值

int a=729;
int b=271;
printf("%d \n",(a & b) +(a ^ b)>>1);
printf("%d  \n",(a & b) +(a | b));

输出结果:500;1000

(1)这道题咋看上去是位运算,一步一步进行位运算,不会吧,很 low 的。那么有什么捷径呢?

(2)好的,我们来读懂这道题的含义。先来说说各种位运算的本质吧。

&运算:

相当于十进制 相同位做加法的1/2

0101 & 0011   结果:二进制0001             十进制 (2^0 +2^0)/2          这里的"^"代表次幂

|运算:

相当于十进制 相同位做加法的1/2与不同位做加法求和

0101 | 0011   结果:二进制0111             十进制 (2^0 +2^0)/2 +(2^2 +2^1)

^运算:

相当于十进制不同位做加法

0101 ^ 0011  结果:二进制0110           十进制(2^2 + 2^1 )

(3)好了,我们用(2)所示的方法再来求解这道题试试

第一个输出结果的含义:729、271相同位做加法的1/2 与 729、271不同位做加法的1/2(右移1位相当于除2)求和

哎呀,这个含义不就是729与271求平均数吗,OK,结果就是500。

第二个输出结果的含义:729、271相同位做加法的1/2,729、271相同位做加法的1/2,729、271不同位做加法求和,三个结果的和

哎呀呀,这个含义不就是729与271求和吗,OK结果就是1000啦。

(4)好啦,掌握了&(与运算)、|(或运算)、^(异或运算)的本质,以后涉及这类题便可瞬间秒杀

5、二进制枚举

在遇到子集问题的处理时候,我们有时候会借助二进制枚举来遍历各种状态(效率大于dfs回溯)。

这种就属于排列组合的问题了,对于每个物品(位置)来说,就是使用和不使用的两个状态,而在二进制中刚好可以用1和0来表示。

而在实现上,通过枚举数字范围分析每个二进制数字各符号位上的特征进行计算求解操作即可。

二进制枚举的代码实现为:

for(int i = 0; i < (1<<n); i++) //从0~2^n-1个状态
{for(int j = 0; j < n; j++) //遍历二进制的每一位 共n位{if(i & (1 << j))//判断二进制数字i的第j位是否存在{//操作或者输出}}
}

6、不用加减乘除做加法

题目:

写一个函数,求两个整数之和,要求在函数体内不得使用+、-、*、/四则运算符号。

分析

这道题咋一听可能没啥思路,简单研究一下位运算还是能独立推出来和理解的。

当然,解决这题前,需要了解上面的四种位运算。还要知道二进制的运算:0+0=0,0+1=1,1+1=0(进位)

对于加法的一个二进制运算。如果不进位那么就是非常容易的。这时候相同位都为0则为0,0和1则为1.满足这种运算的异或(不相同取1,相同取0)和或(有一个1则为1)都能满足.

看到上面操作的不足之后,我们肯定还需要解决进位的问题对于进位的两数相加,这种核心思想为

1、用两个数,一个正常m相加(不考虑进位的)。用异或a^b就是满足这种要求,先不考虑进位(如果没进位那么就是最终结果)。另一个专门考虑进位的n。两个1需要进位。所以我们用a&b与记录需要进位的。但是还有个问题,进位的要往上面进位,所以就变成这个需要进位的数左移一位。

2、然后就变成m+n重新迭代开始上面直到不需要进位的(即n=0时候)。

实现代码为:

public class Solution {public int Add(int num1,int num2) {/**  5+3   5^3(0110)   5&3(0001) *  0101    *  0011 */int a=num1^num2;int b=num1&num2;b=b<<1;if(b==0)return a;else {return Add(a, b);}        }
}

当然,这里也可以科普一下二进制求加法:average = (a&b) + ((a^b)>>1) ;

7、二进制中1的个数

这是一道经典题,在剑指offer上也有对应题目,其具体题目要求 输入一个整数,输出该数二进制表示中1的个数(其中负数用补码表示)

对于这个问题,不用位运算将它转成二进制字符串直接枚举字符 '1' 的个数也可以直接求出来,但是这样做是没有灵魂的并且效率比较差。

这里提供两种解决思路:

法一:与(&)运算 与 左移(<<)运算相结合

大家知道每个类型的数据它的背后实际都是二进制操作。大家知道 int 的数据类型的范围是 (-231,231 -1)。

并且 int 有 32 位。但是并非 32 位全部用来表示数据。真正用来表示数据大小的也是 31 位。最高位用来表示正负

首先要知道:

1<<0=1 =00000000000000000000000000000001
1<<1=2 =00000000000000000000000000000010
1<<2=4 =00000000000000000000000000000100
1<<3=8 =00000000000000000000000000001000
. . . . . .
1<<30=2^30 =01000000000000000000000000000000
1<<31=-2^31 =10000000000000000000000000000000

其次还要知道

位运算 & 与。两个十进制与运算.每一位同1为1

所以我们用 2 的正数次幂与知道的数分别进行与运算操作。

如果结果不为 0,那么就说明这位为 1.

(前面 31 个都是大于 0 的最后一个与结果是负数但是如果该位为 1 那么结果肯定不为 0)

具体代码实现为:

public int NumberOf1(int n) {int va=0;for(int i=0;i<32;i++){if((n&(1<<i))!=0){	        		va++;}}return va;	      
}

法二:是运用 n&(n-1)

n 如果不为 0,那么 n-1 就是二进制第一个为 1 的变为 0,后面全为 1.

这样的 n&(n-1) 一次运算就相当于把最后一个 1 变成 0.

这样一直到运算的数为 0 停止计算次数就好了,如下图共进行三次运算那么 n 的二进制中就有三个 1。

 

实现代码为:

public class Solution {public int NumberOf1(int n) {int count=0;while (n!=0) {n=n&(n-1);count++;}return count;}
}

 

 


http://chatgpt.dhexx.cn/article/Ev3L2khU.shtml

相关文章

Python与或运算

今天碰到一道有意思的题目&#xff0c;看了之后发现自己对Python与或的理解还是欠缺&#xff0c;如下。 题目&#xff1a;求12…n 来源&#xff1a;Leetcode 如果不加限制&#xff0c;我们有很多方法计算该值&#xff0c;例如高斯公式&#xff0c;递归等。 我们思考下递归的解…

sql查询数据表某列的重复值并计数

查询sql为&#xff1a; SELECTdevice_id,count( device_id ) AS number FROMcms_sticker_member GROUP BYdevice_id HAVINGcount( device_id ) > 1 ORDER BYnumber DESC; 结果&#xff1a;

查询多个字段同时重复2次以上的记录的sql的次数

表数据如上图&#xff0c; 1.筛选 type、pid 重复的数据的次数大于等于2的 次数和对应的数据值 SELECT COUNT(*),TYPE,pid FROM AREA GROUP BY TYPE,pid HAVING COUNT(*)>2; 2.筛选 type、pid 重复的数据的次数大于等于2&#xff0c;并且对应的 pid和type值相反的重复的数…

sql查询、删除重复相同数据的语句或只保留一条数据

1、查询&#xff08;字段1, 字段2, 字段3&#xff09;全部重复相同的数据 SELECT * FROM 表 WHERE (字段1, 字段2, 字段3) IN (SELECT 字段1, 字段2, 字段3 FROM 表 GROUP BY 字段1, 字段2, 字段3 HAVING COUNT(*) > 1) ORDER BY 排序字段2、过滤&#xff08;字段1, 字段…

分享SQL重复记录查询的几种方法

SQL重复记录查询的几种方法&#xff0c;需要的朋友可以参考一下 1、查找表中多余的重复记录&#xff0c;重复记录是根据单个字段&#xff08;peopleId&#xff09;来判断 代码如下: select * from people where peopleId in (select peopleId from people group by peo…

SQL查询重复记录

1、查找表中多余的重复记录&#xff0c;重复记录是根据单个字段&#xff08;peopleId&#xff09;来判断 select * from people where peopleId in (select peopleId from people group by peopleId having count(peopleId) > 1) 2、删除表中多余的重复记录&#xff0c;重…

为什么int无法转换为Double????

规律&#xff1a;拆、装箱和升、降级两者可以在同一条语句中进行&#xff0c;但是一定要先拆箱或装箱再升级或者降级。。。 一条语句中&#xff0c;int无法转换为Double&#xff0c;因为这里涉及到先升级再装箱子&#xff0c;拆装箱一定要在升降级前面。。。。。 一条语句中&…

C++中int或double与string的相互转换

一、int转string 1.c11标准增加了全局函数std::to_string: string to_string (int val); string to_string (long val); string to_string (long long val); string to_string (unsigned val); string to_string (unsigned long val); string to_string (unsigned long …

java byte[]转int和double

一般无需java来处理byte字节的数据转成 int , C语言更适合干这事. 但是无奈遇到了这种需求. 网上百度了一小部分代码, 发现好多错误代码… 干脆自己手写了一遍… byte[]数据的格式协议文档如下: 先上使用代码 byte[] hex Base64.getDecoder().decode(data); int head Read…

详细讲解int、float与double的区别

最近为了看一下float的精确度仔细看了一下这三种数据在内存中的样子&#xff0c;看了一下别人的博客发现大家对精度都有这不同的定义&#xff0c;我自己也简单画了一下。 下面来主要讲解一下int、float与double三者的区别与详解 一、int&#xff08;最简单的一种&#xff09;…

c++ string转int, double,int,double转string

c string与常用数值变量互转 写了几个字符串与数值变量互相转换的几个函数&#xff0c;每次用到都要上网查一堆&#xff0c;耽误时间&#xff0c;写好放到这里备用。方法有很多&#xff0c;这里列出来测试过能用的&#xff0c;其他方法慢慢添加。以下函数自动判断int或者double…

C++下string类型转double类型

最近coding的时候随手写了std::stod()函数来进行类型的转换&#xff0c;发现输出的时候自动做了小数位的截取 尝试使用std::stold()函数转换成long double类型之后&#xff0c;依旧不能解决问题&#xff0c;输出依旧不是想要的 发现网上对这个问题的解答没有&#xff0c;于是…

double类型转换成int类型

1、案例演示 public class test09 {public static void main(String[] args) {double a 5000.44;double b 100.12;double v a / b;int i new Double(v).intValue();System.out.println(i);System.out.println(v);} } 运行结果&#xff1a; 49 49.944466640031955 2、源…

移动光猫超级管理员密码获取

我的型号是ZN-M142G 一 、开启telnet 1.电脑开启telnet &#xff08;自行百度&#xff09; 2.登录192.168.1.1 3.把路由器后台网址替换&#xff08;如果光猫已经开启telnet请忽略&#xff09;​​​​​​http://192.168.1.1/getpage.gch?pid1002&nextpagetele_sec_ts…

移动光猫只有一个lan口?其他是电视用,如何增lan口

输入192.168.1.1&#xff0c;用超级账号登陆&#xff0c;&#xff08;如何得到超级账号呢&#xff0c;可以让维护小区的移动工作人员给你&#xff09;我发现家里的光猫只有一个lan口&#xff0c;通过超级账号登陆发现&#xff0c;其他三个&#xff08;lan1~lan3&#xff09;都分…

移动光猫 烽火HG6145F 获取管理员密码 启用USB存储功能

总结一下移动光猫烽火HG6145F获取管理员密码的过程&#xff0c;从网上没有搜到这个型号&#xff0c;但是可以参考一下相关类似型号&#xff0c;参考链接附后。 1、连接光猫&#xff1a;首先连接好光猫的WIFI&#xff0c;确认可以访问路由默认网关192.168.1.1 2、获取光猫MAC&…

移动光猫连接移动硬盘变成超小型nas【HS8545M5

移动硬盘连接光猫usb接口 一.第一步 先准备好移动硬盘和光猫 我的移动硬盘装了个盒子1TB 连接好usb接口 我家的光猫是华为定制版的 HS8545M5 当然肯定不会这么简单的就完成了 2.这个需要登录光猫的超级管理员打开媒体共享功能的 当然移动公司不会简单给你超级账号的密码 可以…

最新中国移动光猫改桥接方式(中兴ZXHN F663NV9)地域:贵州 适用于动态超密

​ 话不多说&#xff0c;直接开干 1.首先拔掉光纤 2.把电脑网线插进光猫1口 输入管理地址192.168.1.1 ​ 3.接着输入管理员密码&#xff0c;就是光猫背面的那个 进入后台后选择 网络——远程管理 然后复制loid和password以及sn备用 4.然后重置光猫 用针恢复孔5秒以上&…

移动光猫(吉比特TEWA-272G)进入高级管理界面的简单方法

参加中国移动光纤宽带升级千兆活动后&#xff0c;之前通过超级用户身份已调成桥接模式的光猫(GS3101)免费更换成了吉比特TEWA-272G(2021-10-30生产&#xff0c;硬件版本号HV1.0.0.0、软件版本号 V1.0.0.0)&#xff0c;只能用新设备后面注明的user和口令进入基础管理界面进行…

移动光猫GM219-s多LAN端口的网络开放

起因 最近&#xff08;2018.08&#xff09;装了移动的50M宽带&#xff08;成都&#xff09;&#xff0c;在默认情况下GM219-S这款光猫总共有4个端口&#xff0c;但是只有LAN1口是可以连接电脑上网&#xff0c;其他的3个端口只能用于电视盒子之类的用途。就是下图这货→_→   …