【35天从0开始备战蓝桥杯 -- Day6】

作者:小年糕是糕手日期:2026/3/26

🫧个人主页:小年糕是糕手

💫个人专栏:《C++》《Linux》《数据结构》《C语言》

🎨你不能左右天气,但你可以改变心情;你不能改变过去,但你可以决定未来!


目录

一、进制转换

1.1、二进制转十进制

1.2、十进制转二进制

1.3、二进制转八进制

1.4、二进制转十六进制

1.5、原码、反码、补码

练习

1°10 进制转 x 进制

2°x 进制转 10 进制

3°进制转换1

4°进制转换2

二、位运算操作符

2.1、左移操作符

2.2、右移操作符

2.3、按位与操作符(&)

2.4、按位或操作符(|)

2.5、按位异或操作符(^)

2.6、按位取反操作符(~)

三、位运算的应用

3.1、奇偶数判断

3.2、保留二进制位中的指定位

3.3、获取二进制位中的指定位

3.4、将指定二进制位设置为1

3.5、将指定二进制位设置为0

3.6、反转指定的二进制位

3.7、将二进制中最右边的1变为0

3.8、只保留二进制中最右边的1

3.9、异或的巧用

交换两个整数的值

只出现一次的数字

丢失的数字

总结


一、进制转换

我想大家应该都听过一句话:计算机底层只认识二进制,只有0和1,这里的0和1其实就是二进制,介绍其他进制之前,我们先来说说我们最常用的十进制,他是由0~9组成的,满10进1:

进制进位规则组成数字基数
二进制2进 10、12
八进制8进 10~78
十进制10进 10~910
十六进制16进 10~9、A~F(A=10,B=11…F=15)16

下面我们来介绍一下进制间如何转换:

1.1、二进制转十进制

其实每个进制都有对应的权重,比如十进制中11是怎么来的,15就是10+5,5对应的权重就是5*10^0,所有结果就是5,10就是1*10^1所以就有了15,下面我们先尝试用其他进制来表示一下15:

15 的 2 进制:1111
15 的 8 进制:17
15 的 10 进制:15
15 的 16 进制:F

16进制的数值之前写:0x

8进制的数值之前写:0

所以二进制转十进制假设我们现在二进制是1101,我们按照对应权重进行计算:

二进制数 1101 从右往左的位权是:

  • 最右边第 0 位:数字 1,权重 2^0=1
  • 第 1 位:数字 0,权重 2^1=2
  • 第 2 位:数字 1,权重 2^2=4
  • 第 3 位:数字 1,权重 2^3=8

正确的计算式应该是:1101 ​​= 1×2^3+1×2^2+0×2^1+1×2^0=8+4+0+1=13​​

1.2、十进制转二进制

这里我们看一张图片即可:

下面大家可以自己去尝试写出1206的二进制:

1.3、二进制转八进制

  • 八进制数字范围:每一位只能是 0~7,因为 23=8,所以 0~7 中的每个数字都可以用最多 3 位二进制数表示(例如:7 的二进制是 111)。
  • 二进制转八进制:从二进制序列的右边低位开始向左,每 3 位二进制位为一组,换算成一个八进制位;如果最后剩余不足 3 位,就在左边补 0 后直接换算。
  • 八进制转二进制:将每一位八进制数字,直接替换为对应的 3 位二进制数,拼接后去掉多余前导零即可。

8进制数转换2进制数是相关的过程,每⼀个8进制位转换成3个2进制位就行

1.4、二进制转十六进制

  • 十六进制数字范围:每一位是 0~9a~fA~F),因为 24=16,所以每个数字都可以用最多 4 位二进制数表示(例如:f/F 的二进制是 1111)。
  • 二进制转十六进制:从二进制序列的右边低位开始向左,每 4 位二进制位为一组,换算成一个十六进制位;如果最后剩余不足 4 位,就在左边补 0 后直接换算。
  • 十六进制转二进制:将每一位十六进制数字,直接替换为对应的 4 位二进制数,拼接后去掉多余前导零即可。

16进制数转换2进制数是相关的过程,每⼀个16进制位转换成3个2进制位就行

总结一下就是:八进制转二进制就是3个为一组,从右往左进行拆解转换,十六进制转二进制就是4个为一组,从右往左进行拆解转换,并且八进制前加上0,十六进制前加上0x

1.5、原码、反码、补码

整数的二进制表示方法有三种,即原码、反码和补码;
整数分为有符号整数(signed)和无符号整数 (unsigned)

有符号整数的原码、反码和补码的二进制表示中均由符号位和数值位两部分组成,二进制序列中,最高位的 1 位是被当做符号位,剩余的都是数值位,符号位都是用 0 表示 “正”,用 1 表示 “负”。

正整数的原、反、补码都相同。

负整数的三种表示方法各不相同,需要计算。

原码:直接将数值按照正负数的形式翻译成二进制得到的就是原码。
反码:将原码的符号位不变,其他位依次按位取反就可以得到反码。
补码:反码 +1 就得到补码。
由补码得到原码也可以使用:取反,+1 的操作

1int a = -10
2原码:10000000 00000000 00000000 00001010
3反码:11111111 11111111 11111111 11110101
4补码:11111111 11111111 11111111 11110110
5int a = 10
6原码:00000000 00000000 00000000 00001010
7反码:00000000 00000000 00000000 00001010
8补码:00000000 00000000 00000000 00001010

无符号整数的三种二进制表示相同,没有符号位,每一位都是数值位:

讲清楚了整数表示的原码、反码和补码后,我们还得知道整数在内存中是以 补码 的形式存储的,整数在参与位运算的时候,也都是使用内存中的 补码 进行计算的,计算的产生的结果也是 补码 ,需要转换成 原码 才是真实值

练习

1°10 进制转 x 进制

10 进制转 x 进制

1#include<iostream>
2#include<string>
3using namespace std;
4
5string s = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
6
7void n_to_x(int n, int x)
8{
9	if (n >= x)
10		n_to_x(n / x, x);
11	cout << s[n % x];
12}
13int main()
14{
15	int n, x;
16	cin >> n >> x;
17	n_to_x(n, x);
18	return 0;
19}

2°x 进制转 10 进制

x 进制转 10 进制

这个题目为大家提供一个思路:我们输入x,s求出s的长度,遍历字符串(从后往前遍历),找出每一个字符然后转换成整数再乘以这一位的权重,最后求和即可:

1#include<string>
2#include<iostream>
3#include<cmath>
4using namespace std;
5int main()
6{
7	int x;
8	string S;
9	cin >> x >> S;
10	size_t n = S.size();//求字符串的长度,我们想要的是从后向前遍历
11	long long sum = 0; 
12	int ci = 0;//表示多少次方
13	for (int i = n - 1; i >= 0; i--)
14	{
15		if (S[i] <= '9')
16			sum += (S[i] - '0') * pow(x, ci);
17		else
18			sum += (S[i] - 'A' + 10) * pow(x, ci);
19		ci++;
20	}
21	cout << sum << endl;
22}

这里还有第二种方法运用到了我们之前说过的一个函数stoi,大家不记得的话可以看下面一篇博客回忆一下:

【35天从0开始备战蓝桥杯 -- 补充包】-CSDN博客https://blog.csdn.net/2501_91731683/article/details/158812800?spm=1001.2014.3001.5501下面我们对代码进行改进:

1#include<string>
2#include<iostream>
3using namespace std;
4int main()
5{
6	int x;
7	string S;
8	cin >> x >> S;
9	int ret = stoi(S, NULL, x);//头文件就是string
10	cout << ret << endl;
11	return 0;
12}

3°进制转换1

进制转换1

1#include <iostream>
2#include <string>
3using namespace std;
4string s = "0123456789ABCDEF";
5void x_to_m(int x, int m)
6{
7	if (x >= m)
8		x_to_m(x / m, m);
9	cout << s[x % m];
10}
11int main()
12{
13	int x = 0;
14	int m = 0;
15	cin >> x >> m;
16	x_to_m(x, m);
17	return 0;
18}
19

4°进制转换2

进制转换2

1#include<iostream>
2#include<string>
3using namespace std;
4string str = "0123456789ABCDEF";
5void x_to_m(int x, int m)
6{
7	if (x >= m)
8		x_to_m(x / m, m);
9	cout << str[x % m];
10}
11int main()
12{
13	int n;//原来进制
14	string s;//原来进制表示的数字
15	int m;//转换之后的进制
16	//1.将s中存放的n进制数字转换成10进制数字
17	cin >> n >> s >> m;
18	int x = stoi(s, NULL, n);
19	//2.将x转换成m进制
20	x_to_m(x, m);
21	return 0;
22}

二、位运算操作符

先说一个前提:下面提到的位运算操作符,只适用于整数,不能应用于其他数据类型,char也属于整型家族所有也可以使用:

2.1、左移操作符

左移操作符是双目操作符,形式如下:

1int num = 10;
2num << i; //将num的二进制表示,左移i位 
3

移位操作符,移动的是存储在内存中的补码的二进制序列,下面我们来看看移位规则:

下面我们来举个例子:

1#include<iostream>
2using namespace std;
3int main()
4{
5	int num = 10;
6	//原码 00000000 00000000 00000000 00001010 -- 10
7	//反码 00000000 00000000 00000000 00001010
8	//补码 00000000 00000000 00000000 00001010
9	int n = num << 1;
10	//进行左移
11	//补码 00000000 00000000 00000000 00010100
12	//反码 00000000 00000000 00000000 00010100
13	//原码 00000000 00000000 00000000 00010100 -- 20 
14	cout << "n = " << n << endl;
15	cout << "num = " << num << endl;
16	return 0;
17}
18

众所周知正数的原码、反码、补码都是相同的,下面我们来举个负数的例子看看:

1#include<iostream>
2using namespace std;
3int main()
4{
5	int num = -10;
6	//原码 10000000 00000000 00000000 00001010
7	//反码 11111111 11111111 11111111 11110101
8	//补码 11111111 11111111 11111111 11110110
9	int n = num << 1;
10	//补码 11111111 11111111 11111111 11101100
11	//反码 10000000 00000000 00000000 00010011
12	//原码 10000000 00000000 00000000 00010100 -- -20
13	cout << n << endl;
14	return 0;
15}

这就是左移操作符的基本用法,我们要记住我们操作的是补码,最后看结果是多少要返回原码

2.2、右移操作符

右移操作符是双目操作符,形式如下:

1num >> i;//将num的二进制表示右移i位 
2

移位操作符,移动的是存储在内存中的补码的二进制序列,下面我们来看看移位规则:

右移运算分两种:逻辑右移和算术右移,具体采用哪种右移方式取决于编译器,大部分的编译器采用的是算术右移。两种移位方式的规则如下:

  1. 逻辑右移:左边用 0 填充,右边丢弃
  2. 算术右移:左边用原该值的符号位填充,右边丢弃

下面我们来举个例子看看:

1#include <iostream>
2using namespace std;
3int main()
4{
5	int num = -1;
6	//原码 10000000 00000000 00000000 00000001
7	//反码 11111111 11111111 11111111 11111110
8	//补码 11111111 11111111 11111111 11111111
9	int n = num >> 1;
10	//这里有逻辑右移和算术右移,取决于编译器
11	cout << "n = " << n << endl;
12	cout << "num = " << num << endl;
13	return 0;
14}
15

负1的补码其实是比较特殊的,大家可以了解一下,下面为大家总结了一个结论:

总结一下是不是就是左移一位的结果就是不管正数还是负数都乘以2,右移就是如果是正数,答案就是除以二取整,如果是负数就不一定

警告⚠️:对于移位运算符,不要移动负数位,这个是标准未定义的。

1int num = 10;
2num >> -1; // error
3

补充说明

在 C/C++ 标准中,移位运算符的右操作数必须是非负整数,否则行为是未定义的(UB),编译器可能报错或产生不可预期的结果,所以代码里绝对不要写 num >> -1num << -2 这类写法。

2.3、按位与操作符(&)

按位与操作符是双目操作符,形式如下:

a & b; //a和b按位与运算

规则:进行计算时写出a和b在内存中存储的补码的二进制形式进行计算,对两个数的对应二进制位进行与运算,只有对应的两个二进制都为1时,结果才为1,有一个数为0则为0:

下面为大家举个例子(大家可以自己在电脑上打印来核对结果):

1#include<iostream>
2using namespace std;
3int main()
4{
5	int a = 5;
6	//原码 00000000 00000000 00000000 00000101
7	//反码 00000000 00000000 00000000 00000101
8	//补码 00000000 00000000 00000000 00000101
9	int b = -7;
10	//原码 10000000 00000000 00000000 00000111
11	//反码 11111111 11111111 11111111 11111000
12	//补码 11111111 11111111 11111111 11111001
13	int c = a & b;
14	//a & b,a和b的补码进行按位与
15	//a   00000000 00000000 00000000 00000101
16	//b   11111111 11111111 11111111 11111001
17	//a&b 00000000 00000000 00000000 00000001 -- 补码
18	//我们算出来的是补码,但是这个补码第一个是0所以是正数
19	//正数的原反补相同,所以我们直接读即可 -- 1
20	cout << c << endl;
21	return 0;
22}

2.4、按位或操作符(|)

按位或操作符是双目操作符,形式如下:

a | b; //a和b按位或运算

规则:进行计算时写出a和b在内存中存储的补码的二进制形式进行计算,对两个数的对应二进制位进行或运算,对应的两个二进制位只要有1,或结果就为1,两个都是0才为0:

下面为大家举个例子:

1#include<iostream>
2using namespace std;
3int main()
4{
5	int a = 5;
6	//原码 00000000 00000000 00000000 00000101
7	//反码 00000000 00000000 00000000 00000101
8	//补码 00000000 00000000 00000000 00000101
9	int b = -7;
10	//原码 10000000 00000000 00000000 00000111
11	//反码 11111111 11111111 11111111 11111000
12	//补码 11111111 11111111 11111111 11111001
13	int c = a | b;
14	//a | b,a和b的补码进行按位或
15	//a   00000000 00000000 00000000 00000101
16	//b   11111111 11111111 11111111 11111001
17	//a|b 11111111 11111111 11111111 11111101 -- 补码
18	//    10000000 00000000 00000000 00000010 -- 反码
19	//    10000000 00000000 00000000 00000011 -- 原码(-3)
20	cout << c << endl;
21	return 0;
22}

2.5、按位异或操作符(^)

按位异或操作符是双目操作符,形式如下:

a ^ b; //a和b按位异或运算

规则:进行计算时写出a和b在内存中存储的补码的二进制形式进行计算,对两个数的对应二进制位进行异或运算,对应的两个二进制相同则为0,相异则为1:

下面为大家举个例子:

1#include<iostream>
2using namespace std;
3int main()
4{
5	int a = 5;
6	//原码 00000000 00000000 00000000 00000101
7	//反码 00000000 00000000 00000000 00000101
8	//补码 00000000 00000000 00000000 00000101
9	int b = -7;
10	//原码 10000000 00000000 00000000 00000111
11	//反码 11111111 11111111 11111111 11111000
12	//补码 11111111 11111111 11111111 11111001
13	int c = a ^ b;
14	//a ^ b,a和b的补码进行按位异或
15	//a   00000000 00000000 00000000 00000101
16	//b   11111111 11111111 11111111 11111001
17	//a^b 11111111 11111111 11111111 11111100 -- 补码
18	//    10000000 00000000 00000000 00000011 -- 反码
19	//    10000000 00000000 00000000 00000100 -- 原码(-4)
20	cout << c << endl;
21	return 0;
22}
23

2.6、按位取反操作符(~)

按位取反操作符是单目操作符,形式如下:

1~a; //对a的⼆进制位按位取反
2

规则:进行计算时写出a和b在内存中存储的补码的二进制形式进行计算,对操作数的二进制位进行按位取反运算,二进制是0的变成1,是1的变成0:

下面为大家举个例子:

1#include <iostream>
2using namespace std;
3int main()
4{
5	int a = -7;
6	//原码 10000000 00000000 00000000 00000111
7	//反码 11111111 11111111 11111111 11111000
8	//补码 11111111 11111111 11111111 11111001
9	int b = ~a;
10	//补码 00000000 00000000 00000000 00000110
11	//反码 00000000 00000000 00000000 00000110
12	//原码 00000000 00000000 00000000 00000110
13	//正数原反补相同
14	cout << b << endl;
15	return 0;
16}
17

三、位运算的应用

3.1、奇偶数判断

奇偶数判断

结论: 所有偶数的二进制表示中,最低位一定是0,所有奇数的二进制表示中,最低位一定是1,所以将一个数字与1进行按位与(&)运算,即可判断这个数是奇数还是偶数:

1(x & 1) == 1, 说明x是奇数
2(x & 1) == 0, 说明x是偶数
1#include<iostream>
2using namespace std;
3int main()
4{
5	int n;
6	cin >> n;
7	if ((n & 1) == 1)
8		cout << "odd" << endl;
9	else
10		cout << "even" << endl;
11	return 0;
12}
13

3.2、保留二进制位中的指定位

有时候我们想去保留二进制位中的某一位或者某几位,我们该如何操作呢?实际上也非常简单,我们只需要利用按位与操作符的特性:

1结果 = 原数 & 掩码
2 只有两位都为 1,结果才是 1  保留指定位
3 只要有一个是 0,结果就是 0  其他位清零
4
5  0010 1011
6& 0010 1001
7-----------
8  0010 1001   只保留了指定位,其余全清零
9

这样其实就一目了然了,假设我们想要去保留最后n位,我们只需要按位与上一个000111...(n个1),如果我们想保留其中低2~4位,我们只需要去按位与00001110,结论就是想取哪几位,就按位与哪几位(那几位为1)

3.3、获取二进制位中的指定位

这个我们要区别于上一个,上一个保留的意思是我们将我们不想要保留的二进制位都设置为0,想要的位置不变保留即可,获取二进制中的指定位的意思就是我们想要去知道他在二进制位中的第i位是1还是0,我们学之前要明白他们分别代表什么意思:

核心公式与原理 💡

要获取整数 x 二进制中第 i 位(从低到高,最低位为第 0 位)的值,使用公式:(x>>i) & 1

  • 结果为 0:表示第 i 位是 0
  • 结果为 1:表示第 i 位是 1

举个例子:

确定x=00101011的第3位的值,只需要将x=00101011右移3位,第3位就到最低位,然后和1进行按位与运算即可:

1x:00101011
2右移三位后:
3  00000101
4& 00000001
5-----------
6  00000001
7

颠倒二进制位

1class Solution {
2public:
3    uint32_t reverseBits(uint32_t n) {  // 建议用uint32_t 
4        uint32_t ret = 0;
5        for (int i = 0; i < 32; i++) {
6            // 获取第 i 位是 1 还是 0
7            int b = (n >> i) & 1;
8            //  b 移动到反转后的位置,合并到 ret 
9            ret = ret | (b << (31 - i));
10        }
11        return ret; 
12    }
13};

3.4、将指定二进制位设置为1

核心公式

1x |= (1 << i);
2

意思

x 的第 i 位 强制变成 1 其他位保持不变


一位设置为 1

公式:x |= (1 << i);

作用:将 x 的第 i 位设为 1,其他位不变说明:

  • i 从 0 开始编号
  • 1<<i:生成只有第 i 位为 1 的数
  • |:有 1 则 1,不影响其他位

如果我们想要将二进制位中的某几位设置位1:

核心公式

1x |= 掩码;
2

规则

掩码里哪几位是 1,x 的那几位就会被强制设为 1,其他位不动!


公式:x |= m;

作用:将 x 中 m 为 1 的位全部设为 1说明:

  • m:想设为 1 的位为 1,其余为 0
  • 不改变其他位
1class Solution {
2public:
3    int findComplement(int num) {
4        int ret = 0; // 存放最终的补数结果
5        int i = 0;   // 记录当前处理到第几位(从最低位开始,第0位)
6        while (num) { // 只要 num 还有效位没处理完,就继续循环
7            // 取出 num 的最低位:(num & 1) 只能得到 0  1
8            if ((num & 1) == 0) { 
9                // 如果最低位是 0,取反后就是 1,要把它放到 ret 的第 i 
10                ret |= (1 << i); 
11            }
12            // num 右移一位,丢弃已经处理的最低位,准备处理下一位
13            num >>= 1; 
14            // 位数计数器 +1,下次处理更高一位
15            i++; 
16        }
17        return ret; // 循环结束后,ret 就是所有取反为 1 的位拼接结果
18    }
19};

💡 核心逻辑总结

  1. 只处理有效位while(num) 会自动遍历所有非前导零的二进制位。
  2. 取反的本质:原二进制位是 0 → 补数对应位要设为 1;原二进制位是 1 → 补数对应位保持 0
  3. 位拼接:用 ret |= (1 << i) 把取反后为 1 的位,放到 ret 的正确位置上。

⚠️ 特殊情况:num = 0

题目提示 1 <= num < 2^31,所以 num=0 不会出现,代码不用处理这个边界。

3.5、将指定二进制位设置为0

把 第 i 位 设置为 0

公式

1x &= ~(1 << i);
2

作用

x 的第 i 位变成 0 其他位 完全不变


公式:x &= ~(1 << i);

作用:将 x 的第 i 位设为 0,其他位不变说明:

  • i 从 0 开始编号
  • 1 << i:生成第 i 位为 1 的数
  • ~:按位取反
  • &:有 0 则 0,实现清 0

把 某几位同时设置为 0

公式

1x &= ~m;
2

规则

  • m:想清 0 的位是 1
  • ~m:想清 0 的位变成 0,其他是 1
  • & 一下 → 想清 0 的位就变成 0 了!

公式:x &= ~m;

作用:将 m 为 1 的位清 0说明:

  • m:想清 0 的位写 1
  • 清 0 后不影响其他位

3.6、反转指定的二进制位

反转指定二进制位

公式x ^= (1 << i);作用:将 x 的第 i 位(从低到高,最低位为第 0 位)反转,其余位保持不变。说明

  • 1 << i:生成仅第 i 位为 1 的掩码。
  • ^:异或运算,相同为 0,不同为 1,实现指定位翻转。
  • 多位反转:构造掩码 m(反转位为 1),执行 x ^= m

3.7、将二进制中最右边的1变为0

🔥 公式(直接背,就这一行)

1x &= x - 1;
2

作用

把二进制里最右边的一个 1 变成 0其他位完全不变!

位1的个数

1class Solution {
2public:
3    int hammingWeight(int n) {
4        int count = 0;
5        while (n) {
6            n = n & (n - 1);
7            count++;
8        }
9        return count;
10    }
11};
12

2的幂

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

3.8、只保留二进制中最右边的1

🔥 公式(背这一行)

1x &= -x;
2

作用

只保留二进制最右边的 1,其余所有位全部变成 0

3.9、异或的巧用

们已经学习了异或运算符了,那异或运算符的特点:

  1. x ^ x = 0,两个相同的数字异或结果是 0;
  2. 0 ^ x = x,0 和 x 异或还是 x;
  3. a ^ b ^ a == a ^ a ^ b 异或是支持交换律的;

基于上述的特点,异或有一些非常巧妙的使用。

交换两个整数的值

1#include <iostream>
2using namespace std;
3int main()
4{
5	int a = 0;
6	int b = 0;
7	cin >> a >> b;
8	cout << "交换前:a = " << a << " b = " << b << endl;
9	a = a ^ b; //a' = a ^ b
10	b = a ^ b; //b' = a' ^ b == a ^ b ^ b == a
11	a = a ^ b; //a = a' ^ b' == a ^ b ^ a == b
12	cout << "交换后:a = " << a << " b = " << b << endl;
13	return 0;
14}
15

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

只出现一次的数字

只出现一次的数字

1class Solution {
2public:
3    int singleNumber(vector<int>& nums) {
4        int result = 0;
5        for (int num : nums) {
6            result ^= num;   // 异或运算
7        }
8        return result;
9    }
10};

小提示:

  1. 参数部分的 int * nums,这是一种指针的写法,这里不懂指针没关系,你可以直接理解成数组的形式,这里的 int *nums 等价于 int nums[],直接使用下标操作即可。
  2. 参数 int* nums 直接改成数组的形式 int nums[],提交也没问题。
  3. 形参的名字是可以改的,如果你觉得 numsSize 太长,直接改成 n 也行。
  4. 算法竞赛需要指针的地方,可以有其他的方案,比如转换成数组的形式。
  5. 指针较难,也容易出错,从程序效率的角度和驾驭的难易程度上来看,其实在算法竞赛中指针使用的很少。一般会找其他的替代方案,比如数组。

丢失的数字

丢失的数字

1class Solution {
2public:
3    int missingNumber(vector<int>& nums) {
4        int n = nums.size();
5        int result = 0;
6        // 异或数组中的所有元素
7        for (int num : nums) {
8            result ^= num;
9        }
10        // 异或 0..n 的所有数字
11        for (int i = 0; i <= n; ++i) {
12            result ^= i;
13        }
14        return result;
15    }
16};
17

总结

应用位运算表达式说明示例(假设二进制从低位起,位索引从0开始)
奇偶数判断x & 1如果结果为1,则为奇数;结果为0,则为偶数5 & 1 = 1(奇数) 4 & 1 = 0(偶数)
保留二进制位中的指定位x & maskmask 中为1的位保留,为0的位清零x = 0b1101, mask = 0b1010 → 0b1000
获取指定位的值(x >> k) & 1获取第k位的值(0或1)x = 0b1101, k=2 → (0b1101 >> 2) & 1 = 1
将指定位设置为1x | (1 << k)将第k位设置为1,其余位不变x = 0b1000, k=1 → 0b1010
将指定位设置为0x & ~(1 << k)将第k位设置为0,其余位不变x = 0b1010, k=1 → 0b1000
反转指定位x ^ (1 << k)将第k位取反(0变1,1变0)x = 0b1010, k=1 → 0b1000
将二进制中最右边的1变为0x & (x - 1)消除最低位的1x = 0b1010 → 0b1000
只保留二进制中最右边的1x & -x提取最低位的1(其余位清零)x = 0b1010 → 0b0010
异或的巧用a ^ b ^ a == b a ^ b ^ b == a利用异或的自反性实现交换、找只出现一次的数、变量交换等交换两个数:a ^= b; b ^= a; a ^= b 找唯一出现奇数次的数:全部异或


【35天从0开始备战蓝桥杯 -- Day6】》 是转载文章,点击查看原文


相关推荐


【养虾日记】Openclaw操作浏览器自动化发文
卷福同学2026/3/18

用QClaw操作本地浏览器,登录自媒体平台,实现自动发文 1.更新 Openclaw在3.13版本更新后,加入了Chrome DevTools MCP 官方支持,就是可以控制浏览器了。 Openclaw虽然自带内置浏览器,但是打开后完全没有登录信息和安装的扩展插件,这次升级后就能用上我们自己的浏览器了,比较方便。 这里小卷用QClaw做演示,如何让它操作浏览器干活 2.打开浏览器调试 我们需要用到Chrome浏览器 打开chrome://inspect/#remote-debugging


OpenClaw 卸载教程,一篇讲透
不惑_2026/3/10

有些朋友尝鲜体验后,觉得不太适合自己,想要完全卸载却不知道如何操作。以下是完整的卸载步骤: 1. 打开终端,输入以下命令: openclaw uninstall 2. 使用鼠标上下移动光标,按空格键勾选所有选项,然后按回车键确认。 3. 选择 yes 并按回车,此命令会自动删除 OpenClaw 的工作目录。 4. 卸载 npm 包: 如果使用 npm 安装:npm rm -g openclaw 如果使用 pnpm 安装:pnpm remove -g openclaw 如果使用 bun


弃用html2pdf.js,这个html转pdf方案能力是它的几十倍
刘发财2026/3/2

欢迎转载文章 在前端开发中,“把网页变成 PDF”是个老生常谈的需求。无论是生成发票、报告还是简历,用户总希望点一下按钮就能带走一份格式完美的文档。 目前主流的前端html转pdf方案是通过html2canvas将网页渲染成canvas,再通过jsPDF将canvas转换为pdf。代表方案就是 html2pdf.js,npm包周下载量达到了80万,为广大开发者所接受。但是因为它基于html2canvas和jsPDF,会有一些无法解决的问题,比如: 生成速度慢 生成的pdf文件体积大 生成的pd


【大模型面试突击】03_大模型架构演进与对比
香芋Yu2026/2/21

2026大模型面试:大模型架构演进与对比必考28题(含答案) 精选自176道采集题目,保留最高频最核心的28题 | 难度:⭐基础 ⭐⭐进阶 ⭐⭐⭐深入 一、GPT与LLaMA系列演进(7题) 1. ⭐⭐ [字节/高频] GPT系列从GPT-1到GPT-4的架构演进主要脉络是什么? 一句话秒答: 四代GPT走的是一条"预训练范式→暴力出奇迹→多模态融合"的进化路线,每一步都在重新定义规模的上限。 展开来说: GPT-1其实干了一件很简单但当时很大胆的事——把Transformer Decod


2025 年客户端技术盘点与 2026 年技术展望
陆业聪2026/2/13

摘要:2025 年客户端技术围绕三条主线展开:Apple Liquid Glass 与 Android Material 3 Expressive 引领设计革新,端侧 AI 通过 Apple Foundation Models 框架和 Google Gemini 走向开发者可编程化,Flutter、React Native、KMP 等跨平台框架在性能上全面向原生看齐。2026 年的核心看点在于端侧 AI 生态建设、新设计语言落地及鸿蒙全球化验证。 本文基于 2025 年各平台官方发布的公开信


Rust多线程编程学习笔记
sayang_shao2026/2/4

目录 Rust 多线程基础同步线程编程 基本线程创建线程间通信共享状态线程返回值线程池 异步线程编程 Tokio 异步运行时异步任务异步通道异步共享状态 线程安全 所有权与借用同步原语Send 和 Sync trait 性能优化 线程数量避免竞争异步 vs 同步 最佳实践完整代码示例总结 Rust 多线程基础 Rust 的多线程编程建立在标准库的 std::thread 模块之上。与其他语言不同,Rust 通过其所有权系统和类型系统来保证线程安全,避免了常见的并发问


OoderAgent V0.6.5 Nexus 重磅发布:开启超级智能体开发框架新纪元
OneCodeCN2026/1/26

前言: v0.6.5 使用了一个特别的代号,Nexus(枢纽)她不再是一次简单的技术升级。而是一次重生。cong 从0.6.2到0.6.5我们在AI的驱动先快速的迭代,从从基础架构到核心升级,再到技能统一提升,直到0.6.5 一次质的跃迁。本次版本以“构建个人超级终端、赋能全场景智能开发”为核心,重构技术架构、强化能力体系、拓展生态边界,为开发者提供一套从设备协同到AI能力编排的全链路智能体开发解决方案,标志着SuperAgent向“去中心化超级智能体底座”迈出关键一步。 一、Nexu


【SpringBoot】从学会使用maven开始
那我掉的头发算什么2026/1/17

🎬 那我掉的头发算什么:个人主页 🔥 个人专栏: 《javaSE》《数据结构》《数据库》《javaEE》 ⛺️待到苦尽甘来日 引言 当我们在创建一个新的idea项目时,不知道大家注意过没有 在这个页面中除了IntelliJ选项之外,还有一个Maven选项。而这个Maven恰好就是我们今天这篇文章的重头戏! 文章目录 引言创建Maven项目pom文件项目基本信息GAVproperties依赖管理核心:dependencies与depe


RAG索引流程详解:如何高效解析文档构建知识库
北辰alk2026/1/9

引言:为什么文档解析是RAG的基石? 在RAG(检索增强生成)系统中,文档解析是整个知识库构建的第一步,也是最关键的一步。就像建房子需要打好地基一样,良好的文档解析质量直接决定了后续检索和生成的效果。今天,我们就深入探讨RAG索引流程中的文档解析技术。 一、RAG文档解析的整体架构 首先,让我们通过一个流程图了解完整的解析流程: ┌─────────────────┐    ┌─────────────────┐    ┌─────────────────┐ │                 


Pico裸机2(汇编基础)
fanged2026/1/1

既然都裸机了,还是简单回顾一下汇编吧。。。 1 概念 来自:https://redfoxsec.com/blog/introduction-to-assembly-language/ 汇编基本上就是机器码。汇编语言是一种直接对应处理器指令集的低级语言,它以人类可读的形式表达机器指令,是软件与硬件之间几乎最底层的一层接口;每一条汇编指令几乎都能映射为一条机器指令,能够精确控制寄存器、内存、指令顺序和硬件状态,因此被广泛用于启动代码、中断处理、上下文切换和性能或时序极端敏感的场景。

首页编辑器站点地图

本站内容在 CC BY-SA 4.0 协议下发布

Copyright © 2026 XYZ博客