深入建议 50 分钟学习等级 3/8

PYTHON LESSON 042

掩码大师进阶

掌握 (n>>k)&1 取位、n|(1<<k) 置位、n&~(1<<k) 清位三大掩码套路,会用异或解决“找落单数”问题。

00 · 学习目标

这一课要解决什么?

先想一想只用一个整数,怎样精准地读取和修改其中的某一个二进制位?
完成任务字节级开关控制台
学习顺序正式定义 → 机制推演 → 边界与反例 → 迁移

学完后,你应该能够

  • (n >> k) & 1 取出第 k 位
  • n | (1 << k) 置位、n & ~(1 << k) 清位
  • x ^ x = 0:异或能抵消成对的数

01 · 核心概念

取位、置位、清位与异或神技

取位、置位、清位与异或神技:上一课我们整片地操控 8 盏灯,考级更爱考“精准手术”:只看第 3 位是几、只改第 1 位、只灭第 5 位。三大手术套路都围绕一个核心:用 1 << k 造出“只有第 k 位是 1”的掩码,再选择运算符。

考级对应GESP Python 3 级 · 位运算GESP Python 3 级 · 数据编码
学习边界

浮点数遵循 IEEE 754 的实现细节,不使用本章的普通整数进制模型直接推导。

02 · 语法与规则

先记住这 3 条,再开始写程序

01(n >> k) & 1 取出第 k 位

先准确读出这条写法的结构与作用。

02n | (1 << k) 置位、n & ~(1 << k) 清位

换一组最小数据,手工推演一次结果。

03x ^ x = 0:异或能抵消成对的数

再用边界值或反例确认它的适用条件。

03 · 完整实例

代码、运行结果和解释放在一起看

g3-bit-mask-master.pyPYTHON 3.12
# 掩码大师:取出、点亮、熄灭某一位
flags = 0b10110100
print("原始字节:", bin(flags))

# 取出第 3 位(从右数,从 0 开始)
bit3 = (flags >> 3) & 1
print("第 3 位的值:", bit3)

# 点亮第 1 位(置 1)
flags = flags | (1 << 1)
print("点亮第 1 位后:", bin(flags))

# 熄灭第 5 位(清 0)
flags = flags & ~(1 << 5)
print("熄灭第 5 位后:", bin(flags))

# 判断奇偶:只看最低位
print("45 是奇数吗?", 45 & 1 == 1)
print("46 是奇数吗?", 46 & 1 == 1)

# 异或神技:找出只出现一次的数
nums = [4, 2, 1, 2, 1]
lonely = 0
for x in nums:
    lonely = lonely ^ x
print("数组里只出现一次的数:", lonely)
运行结果OUTPUT
原始字节: 0b10110100
第 3 位的值: 0
点亮第 1 位后: 0b10110110
熄灭第 5 位后: 0b10010110
45 是奇数吗? True
46 是奇数吗? False
数组里只出现一次的数: 4

所有手术都围绕一个核心:1 << k 造出“只有第 k 位是 1”的掩码。

在新标签运行和修改这个实例已装入本课代码 · 可自定义输入 · 可提交判题

04 · 逐步理解

每一步只解决一个问题

  1. 01

    从整字节到单个位

    上一课我们整片地操控 8 盏灯,考级更爱考“精准手术”:只看第 3 位是几、只改第 1 位、只灭第 5 位。三大手术套路都围绕一个核心:用 1 << k 造出“只有第 k 位是 1”的掩码,再选择运算符。

    PYTHON
    mask = 1 << 3
  2. 02

    取位:(n >> k) & 1

    想看第 k 位?先 >> k 把它挪到最右边,再 & 1 把其他位全部清零,剩下的就是这一位的值。0b10110100 右移 3 位变成 0b10110,& 1 得 0——第 3 位是 0。两步一组合,任何位都无处遁形。

    PYTHON
    bit3 = (flags >> 3) & 1
  3. 03

    置位与清位

    置位用 |:flags | (1 << 1) 把第 1 位强制变 1,其他位不受影响。清位用 & ~:~(1 << 5) 是“只有第 5 位是 0、其余全是 1”的反向掩码,& 上去正好把第 5 位按成 0。记口诀:或置一、与清零。

    PYTHON
    flags = flags | (1 << 1)
    flags = flags & ~(1 << 5)
  4. 04

    奇偶判断:最快的 & 1

    奇数的二进制最低位一定是 1,偶数一定是 0,所以 n & 1 == 1 就是奇数。这个写法和 n % 2 == 1 等价,但在阅读程序题里经常出现,看到要立刻认识。

    PYTHON
    print(45 & 1 == 1)
  5. 05

    异或神技:找落单的数

    异或有两条性质:x ^ x = 0(相同的数互相抵消)、x ^ 0 = x(和 0 异或保持原样)。数组里其他数都出现两次、只有一个数出现一次时,把所有数异或一遍,成对的全抵消,剩下的就是落单的那个。4^2^1^2^1 = 4,像变魔术一样。

    PYTHON
    lonely = 0
    for x in nums:
        lonely = lonely ^ x
  6. 06

    掩码思维总结

    取位、置位、清位、翻转(^ mask),四种手术对应四种运算符。考试时见到“第 k 位”三个字,先写 1 << k,再想该用哪个运算符——这个反射练出来,位运算题就稳了。

05 · 练习与检验

自己写出来,才算真正学会

本课实作步骤
  1. 1运行程序,看懂取位、置位、清位三步
  2. 2改 flags 初值,先心算再核对
  3. 3用 & 1 判断 100 以内所有偶数
  4. 4把 nums 换成自己编的数组,验证异或找落单
打开本课编程实验室编辑、运行、测试、判题都在一个页面完成

06 · 完整知识

继续理解定义、规则和适用边界

第一次学习先完成上面的六个步骤;需要查定义、核对规则、分析误区或理解“为什么”时,再展开对应知识章。

计算机基础进制、整数表示与位运算从位权理解二、八、十六进制,准确使用补码模型、移位、掩码和 Python 大整数。

正式定义

b 进制数按位权 b⁰、b¹…表示数值。进制只是同一个整数的书写方式;位运算直接处理整数的二进制位。Python 整数精度只受可用内存限制,负数位运算按无限长二进制补码语义定义。

必须掌握

  • bin/oct/hex 生成带前缀字符串,int(text, base) 按指定进制解析;0b、0o、0x 是整数字面量前缀。
  • 手工转十进制用位权展开,十进制转其他进制可连续除基取余并逆序。
  • & 取共同为 1 的位,| 合并置位,^ 标记不同位,~x 等于 -x-1。
  • x << k 相当于 x * 2**k;对非负整数 x >> k 相当于 x // 2**k。
  • 掩码可检测、设置、清除或翻转特定位;位编号通常从最低位 0 开始。
  • 固定字长语言的溢出和 Python 大整数行为不同,题目必须明确字长和有无符号。

常见误区

  • 把进制字符串当成不同数值类型
  • 忘记位编号从 0 开始
  • 照搬固定 32 位溢出结论到 Python
  • 对负数右移套用无符号模型

适用边界

  • 浮点数遵循 IEEE 754 的实现细节,不使用本章的普通整数进制模型直接推导。
  • 字符编码把字符映射到整数,但编码知识与整数进制书写是两个不同层次。
打开本章完整示例与独立阅读页 →
Python 基础运算符、表达式与优先级完整区分算术、比较、逻辑、成员、身份和位运算,并用优先级表消除歧义。

正式定义

表达式求值得到一个值。运算符规定如何组合操作数;当一个表达式含多个运算符时,优先级和结合方向决定求值顺序,括号可以明确改变顺序。

必须掌握

  • / 总是得到浮点结果;// 是向负无穷方向取整的整除;% 与 // 满足 a == (a // b) * b + a % b。
  • 比较可以链式书写,如 0 <= x < 10;and/or 会短路并返回最后求值的操作数,不一定返回 bool。
  • == 比较值是否相等,is 比较是否为同一个对象;判断 None 应写 is None。
  • in/not in 做成员测试;对 dict 测试的是键。
  • 位运算作用于整数的二进制位;负整数按无限长二进制补码语义理解。
  • 复杂表达式即使能靠优先级正确运行,也应使用括号表达意图。

常见误区

  • 把 // 当成简单截断
  • 用 is 比较数字或字符串的值
  • 忘记 and 的优先级高于 or
  • 连续位移、比较和逻辑运算却不加括号

适用边界

  • 浮点数比较受二进制表示误差影响,需要按问题选择容差。
  • 运算符可由自定义类重载,因此相同符号对不同类型可能有不同语义。
打开本章完整示例与独立阅读页 →
编码与文本ASCII、Unicode 与字符编码完整表完整查阅标准 ASCII 0–127,并理解 Unicode、编码方案与 Python 字符串之间的关系。

正式定义

标准 ASCII 是 7 位字符编码,只定义十进制 0–127:0–31 与 127 是控制字符,32 是空格,33–126 是可打印字符。Unicode 为字符分配码点,UTF-8/UTF-16 是把码点编码成字节的方案;Unicode 的前 128 个码点与 ASCII 一致。

必须掌握

  • 数字字符 '0'–'9' 是 48–57,大写字母 'A'–'Z' 是 65–90,小写字母 'a'–'z' 是 97–122。
  • ord(ch) 返回单个 Unicode 字符的码点;chr(n) 返回对应码点的字符,它们不限于 ASCII。
  • 字符串比较按 Unicode 码点逐项进行;大小写转换应优先使用 lower()/upper(),不要把“相差 32”推广到所有文字。
  • str 是字符序列,bytes 是 0–255 的字节序列;encode() 从文字得到字节,decode() 从字节恢复文字。
  • 所谓“扩展 ASCII”没有唯一标准,128–255 的含义取决于代码页,不能当成标准 ASCII 表的一部分。
  • 换行符 LF 是 10,回车符 CR 是 13;Windows 文本常见 CRLF,跨平台读写要让文本模式正确处理。

常见误区

  • 把 ASCII 说成所有字符的统一编号
  • 认为 ord() 只能处理 ASCII
  • 把字符个数等同于 UTF-8 字节数
  • 把某个代码页的 128–255 当成统一的扩展 ASCII

适用边界

  • 本页给出完整标准 ASCII 0–127;Unicode 有十多万个已分配字符,不适合平铺成一张儿童课程长表,应按码点和字符数据库检索。
  • “扩展 ASCII”没有唯一标准,128–255 的解释必须同时注明代码页;字符显示还依赖字体,有合法码点也不等于当前字体一定有对应字形。
打开本章完整示例与独立阅读页 →
工程能力异常、文件、测试与调试读懂报错、缩小问题、设计测试,并安全地打开、读取和关闭文本文件。

正式定义

异常是在运行期间表示错误或特殊情况的对象。调试是用可复现输入和证据定位实际行为与预期行为差异的过程;文件对象连接程序与持久化字节数据。

必须掌握

  • 先读 traceback 最后一行的异常类型与消息,再从最靠近自己代码的栈帧向上追踪。
  • try 只包可能失败的最小代码;except 捕获具体异常;else 处理成功路径;finally 做必需清理。
  • raise 主动报告不满足的前置条件;assert 用于开发期内部假设,不用于校验不可信用户输入。
  • with open(...) as file 会在退出代码块时可靠关闭文件。文本模式必须明确编码,本站统一推荐 encoding='utf-8'。
  • 测试至少包含正常值、边界值、空数据、极端值和反例;每个测试只应有明确目的。
  • 定位错误时一次只改一个假设,保留能稳定复现问题的最小输入。

常见误区

  • 使用 except: 吞掉所有错误
  • 只测题目样例就认为程序正确
  • 文本文件不写 encoding
  • 修复报错表象却不验证根因

适用边界

  • 在线判题的学生代码由独立 Worker 执行;文件系统、网络和资源权限必须受平台限制。
  • 二进制文件、JSON/CSV 和数据库各有专门格式与错误处理方式,不能按普通文本随意拆分。
打开本章完整示例与独立阅读页 →

完成检查

确认自己会解释、会编写、会验证