第19章 位运算与进制转换

内容已经涵盖了“第19章 位运算与进制转换”的核心知识点与典型例题,结构清晰、代码完整,非常适合作为教学笔记或学生自学资料。我可以帮你进一步优化下排版、提炼重点、补充解释或练习题,以下是一个 清晰有条理的重构建议版本


第19章 位运算与进制转换


1. 进制转换

1.1 十进制整数转其他进制(如16进制)

例:
\((114514)_{10} = (BF52)_{16}\)

方法:除基取余法

  • 每次对目标进制取余,记录余数。
  • 将余数反向输出就是转换结果。


1.2 任意进制间转换例题

P1143 进制转换

P1143 进制转换

题目描述

请你编一程序实现两种不同进制之间的数据转换。

输入格式

共三行,第一行是一个正整数,表示需要转换的数的进制 \(n\ (2\le n\le 16)\),第二行是一个 \(n\) 进制数,若 \(n>10\) 则用大写字母 \(\verb!A!\sim \verb!F!\) 表示数码 \(10\sim 15\),并且该 \(n\) 进制数对应的十进制的值不超过 \(10^9\),第三行也是一个正整数,表示转换之后的数的进制 \(m\ (2\le m\le 16)\)

输出格式

一个正整数,表示转换之后的 \(m\) 进制数。

输入输出样例 #1

输入 #1

16
FF
2

输出 #1

11111111
✅ 方法一(C++)
#include<bits/stdc++.h>
using namespace std;

int char_to_int(char a) {
    return isdigit(a) ? a - '0' : a - 'A' + 10;
}
char int_to_char(int a) {
    return a < 10 ? a + '0' : a - 10 + 'A';
}

int main() {
    int n, m, dec = 0, num = 0;
    string input;
    int output[33];
    cin >> n >> input >> m;
    for(char ch : input) dec = dec * n + char_to_int(ch);
    while(dec) {
        output[num++] = dec % m;
        dec /= m;
    }
    for(int i = num - 1; i >= 0; i--)
        cout << int_to_char(output[i]);
    cout << endl;
    return 0;
}

1.3 十进制负数转二进制(补码)

类型 正数 负数 0
原码 符号位 0,数值位直接表示 符号位 1,数值位直接表示 +0(0000...)和 -0(1000...)
反码 与原码相同 符号位不变,其余位取反 +0(0000...)和 -0(1111...)
补码 与原码、反码相同 反码 + 1 唯一表示 0000...

总结记忆口诀
正数:三码(原码、反码、补码)合一,直接表示。
负数:
原码:符号位为 1,数值位直写。
反码:符号位不变,数值位取反。
补码:反码加 1,符号位参与运算。

例: $-20$

  • 原码(8位):10010100
  • 反码(首位不变,其它取反):11101011
  • 补码(反码+1):11101100

1.4 十进制小数转二进制

方法: 小数部分不断乘以 2,取整数部分。

例:
\((3.14)_{10} = (11.00100011...)_2\)

\((0.8125)_{10} = (0.1101)_2\)


1.5 十进制数转负进制

注意: 余数可能为负,需调整:

while(input != 0) {
    int k = input % r;
    int c = input / r;
    if (k < 0) {
        k -= r;
        c++;
    }
    input = c;
    output[num++] = k;
}

2. 位运算(C/C++)

位运算是对二进制位直接进行操作的运算方式。

2.1 按位与 &

  • 同真为真(1 & 1 = 1)

2.2 按位或 |

  • 一真为真(1 | 0 = 1)

2.3 按位异或 ^

  • 相同为0,不同为1
  • 性质:a ^ b ^ a = b(常用于找唯一一个出现奇数次的数)

2.4 取反 ~

  • ~a 相当于对所有二进制位取反(有符号数会变成负数)

2.5 左移 <<

  • a << n 相当于 a × 2^n

2.6 右移 >>

  • a >> n 相当于 a ÷ 2^n

3. 典型练习

3.1 高低位交换(16位)

P1100 高低位交换

P1100 高低位交换

题目描述

给出一个小于 \(2^{32}\) 的非负整数。这个数可以用一个 \(32\) 位的二进制数表示(不足 \(32\) 位用 \(0\) 补足)。我们称这个二进制数的前 \(16\) 位为“高位”,后 \(16\) 位为“低位”。将它的高低位交换,我们可以得到一个新的数。试问这个新的数是多少(用十进制表示)。

例如,数 \(1314520\) 用二进制表示为 \(0000\,0000\,0001\,0100\,0000\,1110\,1101\,1000\)(添加了 \(11\) 个前导 \(0\) 补足为 \(32\) 位),其中前 \(16\) 位为高位,即 \(0000\,0000\,0001\,0100\);后 \(16\) 位为低位,即 \(0000\,1110\,1101\,1000\)。将它的高低位进行交换,我们得到了一个新的二进制数 \(0000\,1110\,1101\,1000\,0000\,0000\,0001\,0100\)。它即是十进制的 \(249036820\)

输入格式

一个小于 \(2^{32}\) 的非负整数
一个小于 \(2^{32}\) 的非负整数。

输出格式

将新的数输出
将新的数输出。

输入输出样例 #1

输入 #1

1314520

输出 #1

249036820
unsigned int n;
cin >> n;
cout << ((n << 16) + (n >> 16)) << endl;

3.2 负进制转换(如 base -2)

#include<bits/stdc++.h>
using namespace std;

char int_to_char(int a) {
    return a <= 9 ? '0' + a : a - 10 + 'A';
}
int main() {
    int output[105], input, r, num = 0;
    cin >> input >> r;
    cout << input << "=";
    while(input) {
        int k = input % r;
        int c = input / r;
        if(k < 0) {
            k -= r;
            c++;
        }
        input = c;
        output[num++] = k;
    }
    for(int i = num - 1; i >= 0; i--) cout << int_to_char(output[i]);
    cout << "(base" << r << ")" << endl;
}

3.3 找筷子(异或找只出现一次的数)

P1469 找筷子(位运算)异或的性质

P1469 找筷子

题目描述

经过一段时间的紧张筹备,电脑小组的“RP 餐厅”终于开业了,这天,经理 LXC 接到了一个定餐大单,可把大家乐坏了!员工们齐心协力按要求准备好了套餐正准备派送时,突然碰到一个棘手的问题:筷子!

CX 小朋友找出了餐厅中所有的筷子,但遗憾的是这些筷子长短不一,而我们都知道筷子需要长度一样的才能组成一双,更麻烦的是 CX 找出来的这些筷子数量为奇数,但是巧合的是,这些筷子中只有一只筷子是落单的,其余都成双,善良的你,可以帮 CX 找出这只落单的筷子的长度吗?

输入格式

第一行是一个整数,表示筷子的数量 \(n\)

第二行有 \(n\) 个整数,第 \(i\) 个整数表示第 \(i\) 根筷子的长度 \(a_i\)

输出格式

输出一行一个整数表示答案。

输入输出样例 #1

输入 #1

9
2 2 1 3 3 3 2 3 1

输出 #1

2

说明/提示

数据规模与约定

  • 对于 \(30\%\) 的数据,保证 \(n \leq 10^5\)
  • 对于 \(100\%\) 的数据,保证 \(1 \leq n \leq 10^7 + 1\)\(1 \leq a_i \leq 10^9\)

提示

  • 请注意数据读入对程序效率造成的影响。
  • 请注意本题的空间限制为 \(4\) Mb。
#include<bits/stdc++.h>
using namespace std;
int main() {
    int n, x, ans = 0;
    cin >> n;
    while(n--) {
        cin >> x;
        ans ^= x;
    }
    cout << ans << endl;
}

八进制转十六进制的步骤总结(以(3703)₈为例)

方法一:八进制 → 二进制 → 十六进制

步骤1:将八进制每位转换为3位二进制
八进制的每1位对应二进制的3位:

  • 3011
  • 7111
  • 0000
  • 3011

拼接后得到二进制数:
011 111 000 011 → 即 0111 1100 0011(按4位分组)

步骤2:将二进制按4位分组,转换为十六进制
每4位二进制对应1位十六进制:

  • 01117
  • 1100C
  • 00113

最终结果:(3703)₈ = (7C3)₁₆

方法二:八进制 → 十进制 → 十六进制

步骤1:八进制转十进制
按权展开求和:
[
3 \times 8^3 + 7 \times 8^2 + 0 \times 8^1 + 3 \times 8^0 = 3 \times 512 + 7 \times 64 + 0 + 3 = 1536 + 448 + 3 = 1987
]

步骤2:十进制转十六进制
不断除以16取余数:

  • (1987 \div 16 = 124) 余 3
  • (124 \div 16 = 7) 余 12 (C)
  • (7 \div 16 = 0) 余 7

倒序排列余数得:7 C 3 → 即 (7C3)₁₆

验证过程示例

八进制 (3703)₈ → 二进制 → 十六进制
[
\begin{align}
3 &\rightarrow 011 \
7 &\rightarrow 111 \
0 &\rightarrow 000 \
3 &\rightarrow 011 \
\hline
\text{二进制} &= 011\ 111\ 000\ 011 \
&= 0111\ 1100\ 0011 \quad (\text{按4位分组}) \
&= 7\ \ C\ \ 3 \quad (\text{对应十六进制}) \
\end{align
}
]

关键总结

  1. 推荐方法:八进制 → 二进制 → 十六进制(更高效,避免大数运算)
  2. 转换规则
    • 八进制→二进制:1位 → 3位
    • 二进制→十六进制:4位 → 1位
  3. 易错点
    • 二进制分组时需注意位数不足补零(如 011 补为 0011)。
    • 十六进制中 10-15 需用字母 A-F 表示。

通过上述步骤,(3703)₈ 最终转换为 (7C3)₁₆

更多参考:https://blog.csdn.net/m0_46549425/article/details/113954831

posted @ 2025-06-01 17:04  kkman2000  阅读(66)  评论(0)    收藏  举报