蓝桥备赛(八)- 位运算和操作符属性(下)

目录

一、位运算的应用

1.1 判断奇数和偶数

1.2保留二进制位中的指定位

1.3 获取二进制位中的指定位

1.4 将指定二进制位设置为1

1.5 将指定二进制位设置为0

1.6 反转指定二进制位

1.7 将二进制位最右边的1变0

1.7.1 练习一:位1的个数

1.7.2 练习二:2的幂

1.7.3 应用场景

1.8 只保留二进制位最右边的1变0

1.9 异或的巧用

练习一:交换两个整数的值

练习二:找出单身狗

练习三:

二、操作符的属性

2.1 优先级

2.2 结合性


一、位运算的应用

1.1 判断奇数和偶数

所有偶数2 进制表示中,最低位⼀定是 0
所有奇数2 进制表示中,最低位⼀定是 1
所以将⼀个数字与 1 进行按位与运算,即可判断这个数是奇数还是偶数。

 B2037 奇偶数判断 - 洛谷

#include <iostream>
#include <cstdio>
using namespace std;

int main()
{
	int n;
	cin >> n;
	if( n & 1)
		cout << "odd" << endl;
	else
		cout << "even" << endl; 
	return 0;
}

1.2保留二进制位中的指定位

有时候需要从⼀个整数 x 2 进制中取出某个位或者某几个位 (想取位)使取出的位置上保留原来的值,其他位置为 0 ;
方法 : 这时候可以使用⼀个值 m ,使 m 2 进制位中对应取出的位置为 1 ,其他位为 0 。 然后使这两个数按位与( x & m )即可。
简单点就是 , 造一个二进制数m( 想取那几位 , 那几位就为1 , 其余都为0 ) ,m & 原二进制数 , 就好了。 

下面有几个例子:
 

1.3 获取二进制位中的指定位

当我们需要获取⼀个整数 x 的二进制中第 i 位( 从低到高,以最低位为第 0 位 )是 1 还是 0 的时候,我们可以对 x 做这样的运算: (x >> i) & 1
结果是 0  --->   i 位是
结果是 1  --->  i 位是

练习:

190. 颠倒二进制位 - 力扣(LeetCode)

public class Solution {
    public int reverseBits(int n) {
        int ret = 0;
        for(int i = 0; i < 32 ; i++)
        {
            int b = (n >> i) & 1 ;
            b <<= (31 - i);
            ret |= b;
        }
        return ret;
    }
}

1.4 将指定二进制位设置为1

将⼀个整数 x 的⼆进制表示中的某⼀位(几位)设置为 1 其余位置保留原值:
方法:
设⼀个数 m ,使 m 的二进制上对应位置为 1 ,其余位置为 0 。然后使两个数进行按位或运算 ( x | m ),即可得到想要的数。

当然也可以只设置 x 二 进制中的某 1 位,也就是将 x 二 进制中的第 i (从低到高,以最低位为第 0 位)置为 1
方法:    x |= (1<<i) ;

练习 :数字的补救

476. 数字的补数 - 力扣(LeetCode)

class Solution {
public:
    int findComplement(int num) {
        int ret = 0;
        int i = 0;
        while(num)
        {
            if((num & 1) == 0)
                ret |= (1 << i);
            num >>= 1;
            i++;
        }
        return ret;
    }
};

1.5 将指定二进制位设置为0

将⼀个整数 x 的二进制表示中 的某 1 位设置为 0 ,其余位置保留原值
也就是将 x 二 进制中的第 i 位(从低到高,以最低位为第 0 位)置为 0 ,其他位保持不变,
方法:  x &= ~(1<<i) ;

应用场景:

在嵌⼊式开发的时候,假设我们要控制⼀个LED灯的开关,某⼀个寄存器的第2位用于控制这个LED灯 的开关。我们可以通过将该位设置为0来关闭LED。
#include <cstdio>
#define LED_CONTROL_BIT 0x04 // 00000100,第2位控制LED
 // 11111011
int main() 
{
 unsigned char Register = 0x04; // 初始状态为⾼电平,LED亮
 Register &= ~LED_CONTROL_BIT; // LED灭
 printf("Register: 0x%02X\n", Register ); // 输出: 0x00
 return 0;
}

1.6 反转指定二进制位

将⼀个整数 x 的⼆进制表示中的第 i 位反转( 从低到高,以最低位为第 0 位 ),也就是原来是 1 的变成 0 ,原来是 0 的变成 1
1)用⼀个数 m 使得 m 的二进制中第 i 位为1 ,其余位置为 0
2 )两个数进行按位 异或运算( x ^ m )

1.7 将二进制位最右边的1变0

有时候,我们需要将一个整数 x 的⼆进制表示中最右边的 1 变为 0 ,这时候就可以使用
x & ( x- 1)  来得到想要的数字。

应用场景:这种运算通常应用   到求一个数的二进制序列中有几个 1 。

为啥?

因为 x = x & (x-1) 这种运算,每计算 一次就会将 x 的二进制表示中最右边的 1 置为 0 ,那么不断的执行 x = x & (x-1) 这样的运算,就会将 x 的二进制中的 1 逐个都置 0 ,这时候 x 也就变成了 0 ,当 x 变成 0 之前,这个运算被执行多少次就说明x的二进制中有多少个 1 

1.7.1 练习一:位1的个数

191. 位1的个数 - 力扣(LeetCode)

方法一:借助 x & ( x - 1 )  , 有多少个1 , 就执行几次 

class Solution {
public:
    int hammingWeight(int n) {
        int c = 0;
        while(n)
        {
            n = n & (n - 1 );
            c++;
        }
        return c;
    }
};

方法二:取二进制的每一位 , 然后判断是否为1 。

---> 为1 , 则计数器c 加一下

但是这个方法的效率没有这么高 , 因为无论你有多少个1 , 程序都会循环32次 ,并且判断32次 ( 就算一个1都没有)

class Solution {
public:
    int hammingWeight(int n) {
        int c = 0;
        for(int i = 0 ;i < 32 ; i++ )
        {
            if((n >> i) & 1 == 1)
                c++;
        }
        return c;
    }
};

1.7.2 练习二:2的幂

231. 2 的幂 - 力扣(LeetCode)

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

记住该加括号要加括号  , 这里涉及操作符优先级的问题 , 想深入了解 , 继续往下看噢~

1.7.3 应用场景

1) 位图算法:分配和释放资源
位图(bitmap)是操作系统、文件系统等经常使用的数据结构,用于高效管理资源,如内存块、磁盘块等。通过将最右边的1变为0,可以快速释放资源。 假设位图用于管理内存块 ,每个位表示⼀个内存单元的状态:1表示已分配,0表示未分配。要释放最右侧已分配的块,只需将最右边的1变为0。
2) 计数算法:统计二进制数中的1的个数
在统计⼆进制数中1的个数时,通过不断将最右边的1变为0,可以显著优化计算的速度。这种方法被称为 Brian Kernighan 算法 ,广泛用于计算机科学的各种优化场景。

1.8 只保留二进制位最右边的1变0

将一个整数 x 的二进制中 最右边的 1 保留下来,其他位都置为 0  :
  x & -x 就可以得到想要的数字。

回过来 , 看一下这道题:

231. 2 的幂 - 力扣(LeetCode)

class Solution {
public:
    bool isPowerOfTwo(int n) {
        return ((n >= 1) &&( n & ( -n )) == n) ;       
    }
};

1.9 异或的巧用

异或运算符的特点 :

1) x ^ x = 0 , 相同的数字异或的结果是0

2)0 ^ x = x , 0 和 x 异或还是 x

3) a ^ b ^ a = a ^ a ^ b , 异或是支持交换律的。

练习一:交换两个整数的值

方法一:创建一个临时变量

#include <iostream>
#include <cstdio>
using namespace std;

int main()
{
	int a = 0;
	int b = 0;
	cin >> a >> b;
	//交换
	int t = 0;
	cout << "交换前:a = " << a << ", b = "   << b << endl;
	t = a;
	a = b;
	b = t;
	cout << "交换后:a = " << a << ", b = "   << b << endl;
	return 0;
}

如果不允许创建临时变量 , 那如何交换呢?

方法二:

a = a + b ;   //此时的a 是和 , b 不动 ( a = a + b , b = b )

b = a - b ;    //此时 b = a+ b - b = a , b 的值变了 ( b = a ) ,此时a还是和   ( a = a + b ) 

a = a - b ;    // 此时a 是 a = a+ b -a =  b 

这样就实现了两个数字交换 , 不使用变量 

#include <iostream>
#include <cstdio>
using namespace std;

int main()
{
	int a = 0;
	int b = 0;
	cin >> a >> b;
	//交换
	cout << "交换前:a = " << a << ", b = "   << b << endl;
	a = a + b;
	b = a - b;
	a = a - b;
	cout << "交换后:a = " << a << ", b = "   << b << endl;
	return 0;
}

这种方法 仅仅可以适用部分情形 , 如果数据太大 , 可能存在溢出的风险

方法三:使用异或

a = a ^ b;

b = a ^ b;

a = a ^ b;

使用异或交换两个数的值 , 只能使用于整形类型 , 因为  异或运算仅适用于整型类型

#include <iostream>
#include <cstdio>
using namespace std;

int main()
{
	int a = 0;
	int b = 0;
	cin >> a >> b;
	//交换
	cout << "交换前:a = " << a << ", b = "   << b << endl;
	a = a ^ b;
	b = a ^ b;
	a = a ^ b;
	cout << "交换后:a = " << a << ", b = "   << b << endl;
	return 0;
}

练习二:找出单身狗

136. 只出现一次的数字 - 力扣(LeetCode)

int singleNumber(int* nums, int numsSize) {
    int singer = 0;
    for(int i = 0 ; i < numsSize ; i++)
    {
        singer ^= nums[i];
    }
    return singer;
}

练习三:丢失的数字

268. 丢失的数字 - 力扣(LeetCode)

int missingNumber(int* nums, int numsSize) {
    int miss = 0;
    //产生nums 的数据异或在一起
    for(int i = 0 ; i < numsSize ; i++)
    {
        miss ^= nums[i];
    }
    //将0~numsSize的数字依次异或到 miss 上
    for(int i = 0; i<= numsSize ; i++)
    {
        miss ^= i;
    }
    return miss;
}

二、操作符的属性

C/C++ 语言的操作符有两个重要的属性 : 优先级 、 结合性

---> 这两个属性 , 决定了表达式求值的计算顺序

2.1 优先级

优先级指的是,如果一个表达式包含多个运算符,哪个运算符应该优先执行。 优先级是相对于相邻的操作符 , 如果两个操作符相隔十万八千里 , 这... 

如果在写代码的时候 , 不清楚操作符的优先级 , 建议改加括号!!!

2.2 结合性

如果两个运算符优先级相同,优先级没办法确定先计算哪个了,这时候就看结合性了。
则根据运算符是左结合,还是右结合,决定执行顺序。大部分运算符是左结合(从左到右执⾏),少数运算符是右结合(从右到左执行),比如赋值运算符( = )。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值