位运算总结

目录

一:按位与运算(&)

1.概念和运算规则

2.按位与的优点和应用

1)优点

2)应用

①掩码操作

②清零特定位

③判断奇偶性

二:按位或运算(|)

1.概念和运算规则

2.按位或优点和应用

1)优点

2)应用

①设置特定位

②合并标志位

三:左移 (<<)和右移(>>)

1.概念和规则

2.移位运算的优点和应用

1)优点

①性能

②代码清晰度

③节省空间

2)应用

①乘除法的优化

②位操作

③位掩码

④图形处理和位图操作

四:按位取反(~)

五:按位异或(^)

1.概念和规则

2.按位或的应用

1)交换两个变量的值

2)查找缺失的数字

3)加密和解密

六:位运算的性质和一些技巧

1.运算符的优先级

2.位运算的单向性

1)位运算中“与”、“或”会造成信息丢失,他们是单向运算,不可逆运算。

2) "左移"“右移”在数据溢出时也会信息丢失

3) “异或”不会造成信息丢失,而且具有很好的性质

3.运算规律

① 交换律

②结合律

4.一些技巧

1)输出int的最大值,最小值

2)  取int的绝对值

3)比较两个数是否相等

4)判断符号是否相同

5)取出第i+1位

6)i+1位 置1

7)i+1位 置0

8)i+1位 反转

9)  在对应i+1位,插入b的对应位

10)  保留最后i-1位

11)  清零最后i-1位

12)  删除最后的1

13)其他



一:按位与运算(&)

1.概念和运算规则

按位与运算是计算机中的一种基本的位运算,它对两个二进制数的相应位执行逻辑与操作。按位与运算的本质在于执行逻辑与(AND)操作其规则是对于每一对相应的位,如果两个位都是1,则结果位为1;否则,结果位为0。比如:

A:   1 0 1 0 1 1 0 1
B:   0 1 1 0 1 0 1 1
--------------------
A&B: 0 0 1 0 1 0 0 1

2.按位与的优点和应用

1)优点

位运算是最靠近计算机底层的,所以计算效率特别高,尤其在数据量超大的情况下,优势很明显。

2)应用

掩码操作

通过按位与运算,可以使用掩码来提取或保留一个数中的特定位。

比如:假设一个二进制数X= 1010 1011

a.如果我们想要提取该数的低四位,则可以与上一个二进制数 Y=0000 1111,即X&Y=1010 1011 & 0000 1111=0000 1011.

b.如果我们想要提取该数的第4位(从低位向高位开始数,即从右边开始数),则我们可以给X与上一个二进制的数0000 1000,即X&Y=0000 1000.

这个二进制数Y呢,也常被称作掩码。程序举例:

#include <stdio.h>

// 自定义函数,以二进制形式输出整数
void printBinary(unsigned short num) {
    for (int i = sizeof(num) * 8 - 1; i >= 0; i--) {
        printf("%d", (num >> i) & 1);
    }
}

int main() {
    unsigned short num = 0b1101101001011011; // 16位二进制数
    unsigned short mask = 0b0000000011111111; // 用于提取低8位的掩码

    unsigned short result = num & mask; // 按位与运算,提取低8位

    printf("原始数: ");
    printBinary(num);
    printf("\n提取低8位的结果: ");
    printBinary(result);
    printf("\n");

    return 0;
}

  运行输出:

原始数: 1101101001011011
提取低8位的结果: 0000000001011011
②清零特定位

比如:假设一个二进制数X= 1010 1011                                                                                      

如果我们要想要将其第2位和第6位清零,则可将X与上一个二进制Y=1101 1101.                            即X&Y=1000 1001 。

③判断奇偶性

任何一个数位与1,则1会在前面补上相应个0,然后和那个数进行位与计算,所以结果不是0就是1,因此,我们常常用一个数&1来判断一个数是奇数或偶数。

例如:1010 0110 & 1=1010 0110 & 0000 0001=0,则说明该数为偶数;

1011 1111 & 1=1011  1111&0000 0001 =1,说明该二进制数的最低位为1,则该数一定为奇数。

二:按位或运算(|)

1.概念和运算规则

按位或运算是对两个二进制数的每一位执行或运算,其结果的每一位是两个操作数对应位上值的逻辑或。这个运算符用于整数的每一位,从最低位(最右边)到最高位(最左边)。

让我们以一个简单的例子来说明按位或运算,假设有两个二进制数:

  A    1010   (表示为 10 的二进制形式)
  B    1100   (表示为 12 的二进制形式)
  ----------------------------------
 A|B   1110   (表示为 14 的二进制形式)

2.按位或优点和应用

1)优点

靠近计算机底层的,所以计算效率特别高。还可以用二进制来标记某些符号。

2)应用
设置特定位

可以使用按位或运算来将特定的二进制位设置为1,而保持其他位不变。这是通过将相应的位设置为1,其余位设置为0来实现的

②合并标志位

例如,如果有两个bool值的标志 flag1flag2,可以使用按位或运算将它们合并成一个标志

bool flag1 = true;
bool flag2 = false;
bool result = flag1 | flag2;  // 合并成一个标志

通常,可以会和移位运算符号"<<",">>"来搭配使用,还没学移位运算符号的先看看后面。

例子1:

#include <iostream>
int main() 
{
    bool flag1 = true;
    bool flag2 = false;

    // 将两个标志合并成一个整数
    int combinedFlags = (flag1 << 1) | flag2;

    // 输出合并后的整数
    std::cout << "Combined Flags: " << combinedFlags << std::endl;

    return 0;
}

这个过程的具体步骤如下:

1.flag1 << 1: 左移一位,将 flag1 的值移到第二位。
2.(flag1 << 1) | flag2: 执行按位或运算,将 flag1 的值放入第二位,同时保留 flag2 的值在第一位。

例子2: leetcode上面的P2506. 统计相似字符串对的数目

这是一道简单题,但是很好的说明了位运算的优点

由于单词只由小写字母构成,所以可以把单词中字符是否出现的状态用int的低26位表示,不同单词经过位运算操作后得到相同的值,那么就可以说明这两个单词由相同类型的字符组成。

例如字符串word=“abcdef”,int型的数据是32位,则该字符串可以表示为

0000 0000 0000 0000 0000 0000 0011 1111

从最低位(最右边)到最高位(最左边),依次对应'a','b','c','d'......

#include<iostream>
#include<string>
using namespace std;
// 自定义函数,以二进制形式输出整数
void printBinary(unsigned int num) {
	int cnt = 0;
	for (int i = sizeof(num) * 8 - 1; i >= 0; i--) {
		cout << ((num >> i) & 1);
		cnt++;
		if (cnt % 4 == 0 && cnt != 0)
		{
			cout<<" ";
		}
	}
}
int main()
{
	string word = "abcdef";
	unsigned int bit = 0;
	for (auto& ch : word) bit |= (1 << (ch - 'a'));
	printBinary(bit);
	return 0;
}

程序输出:

0000 0000 0000 0000 0000 0000 0011 1111

三:左移 (<<)和右移(>>)

1.概念和规则

  将一个二进制数的所有位向左移动指定的位数,右侧用零填充。比如

1001<<1=0001 0010
1001<<2=0010 0100
1001<<3=0100 1000

1001>>1=0100
1001>>2=0010
1001>>3=0001
1001>>4=0000

2.移位运算的优点和应用

1)优点

①性能

移位运算通常比乘除法和其他数学运算更快。在底层硬件中,移位操作是一种基本的操作,执行速度相对较快。

代码清晰度

 移位运算符能够清晰地表达对二进制位的操作,使得代码更易读、更易理解。对于某些位级别的操作,使用移位运算可以提高代码的可读性。

③节省空间

在一些情况下,移位运算可以节省内存空间。例如,左移一位相当于乘以2,右移一位相当于除以2(对于奇数,最低位被舍去,比如3>>1=0001,不会有小数),这在某些算法和数据结构中可以用于优化空间利用。

2)应用

①乘除法的优化

 移位运算可以用来优化乘法和除法操作。左移一位相当于乘以2的幂,右移一位相当于除以2的幂。这在需要对数字进行倍增或减小时是很有用的。在一些需要高性能的场景下,可以使用移位运算来替代乘法和除法,以提高代码执行效率。

②位操作

 移位运算是进行位操作的一种有效手段。通过左移、右移和按位与、按位或、按位异或等操作,可以方便地对二进制数据的特定位进行设置、清除、检查等操作。

③位掩码

移位运算在创建位掩码(bitmask)时非常有用。通过将1左移至特定位置,可以创建一个具有特定位设置的二进制掩码。

图形处理和位图操作

在图形处理和图像处理中,移位运算常用于像素值的处理和位图操作。例如,通过位移和按位操作,可以实现图像的缩放、旋转等操作。

四:按位取反(~)

对一个二进制数的每一位执行取反操作,将0变为1,将1变为0。

int a = 5;  // 二进制表示:0101
int result = ~a;  // 结果二进制表示:1010,即十进制的-6

五:按位异或(^)

1.概念和规则

按位异或 (^) 是一种位运算符,它对两个二进制数的相应位执行异或运算。异或运算的规则如下:

1.相同位的异或结果为0

 0 ^ 0 = 0
 1 ^ 1 = 0

2.不同位的异或结果为1

 0 ^ 1 = 1
 1 ^ 0 = 1

举个栗子

  10101010
^ 01010101
-----------
  11111111

2.按位或的应用

1)交换两个变量的值

使用按位异或可以在不使用额外变量的情况下交换两个变量的值。这是因为按位异或具有自反性,即 a ^ b ^ b 等于 a。对任何数 a,与 0 进行按位异或运算的结果都是 a。位运算满足结合律。结合律指的是在一个运算符连续作用于三个操作数时,无论怎样加括号,得到的结果都是相同的。(a op b) op c 等于 a op (b op c),其中 op 代表任意位运算符。

int a = 5;
int b = 10;

a = a ^ b;
b = a ^ b;
a = a ^ b;

2)查找缺失的数字

在一组数字中,如果除了一个数字之外,其他数字都出现两次,可以使用按位异或来找到缺失的数字。这是因为相同的数字异或结果为0。

int findMissingNumber(vector<int>& nums) {
    int result = 0;
    for (int num : nums) {
        result ^= num;
    }
    return result;
}

3)加密和解密

异或运算在加密和解密算法中也有广泛应用。在某些简单的加密算法中,通过异或运算可以实现简单的加密和解密操作。

六:位运算的性质和一些技巧

1.运算符的优先级

位运算符的优先级是相对较低的,低于算术运算符和关系运算符。以下是位运算符的一般优先级(从高到低):

①按位取反 ~
②左移 <<,右移 >>
③按位与 &
④按位异或 ^
⑤按位或 |

在编写代码时,为了确保表达式的清晰和正确,最好使用括号来明确运算顺序,提高代码可读性。

2.位运算的单向性

1)位运算中“与”、“或”会造成信息丢失,他们是单向运算,不可逆运算。


1 & x = y;
知道x 可以求出y,
知道y 不可能求出x。
当y = 0, x 可能等于0,2,6……

2) "左移"“右移”在数据溢出时也会信息丢失

3) “异或”不会造成信息丢失,而且具有很好的性质

正因为异或不会造成信息丢失,异或可以进行算杂的交换运算

比如:x ^ a = y,当a确定时,只需知道x,y两者中的一个,就可求出另外一个。

等式两边同时异或上a,则等式为x ^ a ^ a =y ^ a ,即 x=y ^ a;

3.运算规律

① 交换律


按位与 (&):
a & b 等于 b & a。
按位或 (|):
a | b 等于 b | a。
按位异或 (^):
a ^ b 等于 b ^ a


②结合律


按位与 (&):
(a & b) & c 等于 a & (b & c)。
按位或 (|):
(a | b) | c 等于 a | (b | c)。
按位异或 (^):
(a ^ b) ^ c 等于 a ^ (b ^ c)

4.一些技巧

1)输出int的最大值,最小值

int 最大值 (1<<31)-1 ,但是不能这么写,在C++里会溢出,要写出 (1ll<<31)-1,表示以long long类型的左移操作

int 最小值1<<31  

2)  取int的绝对值

a ^ (a>>31) - (a>>31)

①右移位运算 (a >> 31):

对 a 进行算术右移31位,取得 a 的符号位,将其扩展到整个32位。

②按位异或运算 a ^ (a >> 31):

如果 a 是正数或零,这个操作会将所有位保持不变(因为右移出来的符号位是0,异或0得到原数);
如果 a 是负数,这个操作会将 a 的所有位取反。

③减去 (a >> 31):

对于正数或零,这个操作相当于减去0;
对于负数,由于之前异或操作的影响,相当于加上 a 的绝对值。

综合起来,这个表达式的效果是将带符号整数 a 转换为它的绝对值。这是通过先将 a 取反(如果是负数),然后加上 1(如果是负数)来实现的。
值得注意的是,这个表达式在负溢出的情况下可能会产生未定义的行为,因为右移负数的行为取决于具体的编译器实现。在实际应用中,通常会使用更加清晰和可靠的方式来取绝对值,比如使用条件表达式:a < 0 ? -a : a。

3)比较两个数是否相等

a ^ b ==0

4)判断符号是否相同

a ^ b >= 0

5)取出第i+1位

a & (1<<i)

6)i+1位 置1

a |=1<<i

7)i+1位 置0

a &=~(1<<i)

8)i+1位 反转

a ^ (1<<i)

9)  在对应i+1位,插入b的对应位

a |=1<<i; (a的bit位置1)
a & (b & 1<<i) (与b的bit位相与)
(置1后相与=置0后相或)

10)  保留最后i-1位

a & ((1<<i)-1)

11)  清零最后i-1位

a & ~((1<<i)-1)

12)  删除最后的1

 a& (a-1) (可用于判断2的幂数)

13)其他

a & a = a
a | a = a
a ^ a = 0

a & 0 = 0
a | 0 = a
a ^ 0 = a 

今天的学习笔记就写到这,最后,希望大家多多指教,文章有什么错误可在评论区说明。

  • 51
    点赞
  • 46
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值