第1章|数值与哈希

Unit 00 · 数值表示、哈希与状态记录基础

仅本次页面有效
U00
Number & Hash FundamentalsDigit · Key · State · Query · Update

哈希表不是“背一个 dict 模板”,
而是给历史状态设计可查询的表示。

本单元先拆开整数数位,再建立 set、dict、键和值、频次、一一映射、规范化键、前缀状态与查询—更新顺序。后续每道题都会回到同一个问题:当前需要记住什么,怎样把它变成稳定的键?

检测到上次学习进度阶段、动画、答题、代码、迁移槽位和编辑器高度均可恢复。

Unit 00 先清除后续章节的术语障碍

  • 整数数位:%、//、基数与进位;
  • 哈希结构:set、dict、键、值、默认值与平均 O(1);
  • 状态语义:频次、索引、映射、一一映射、规范化表示;
  • 区间状态:前缀和、历史频次以及“先查询后更新”。

贯穿本章的状态记录模板

for item in data: key = build_key(item) old_state = table.get(key) # 先查历史 use(old_state) # 再产生答案 table[key] = new_state # 最后更新
Hash Key

键是状态的“地址标签”。相同键意味着算法认为两个对象在当前任务下等价。

State Meaning

dict 的值可以是索引、次数、配对对象或候选集合;变量名和不变量决定它的语义。

Query Before Update

先查询历史,再写入当前状态,常用于避免当前对象错误地匹配自己。

Canonical Key

把不同外观但本质等价的对象转成同一个、稳定且可哈希的表示。

CHAPTER 01 / THE COGNITIVE ATLAS

第 1 章 · 认知梯度全景图

数组与哈希:从数值表示出发,练习用集合、映射与频次记录状态。

12 个后续单元
贯穿本章的统一框架观察对象 → 表示状态 → 查询历史 → 更新记录。先决定要记住什么,再选择数位变量、集合或映射。

从基础到复合任务,每一步增加一种决策

这里的梯度描述认知任务的递进,不等同于题库的难度标签。点击题目可进入对应单元。

起点 · Unit 00 数值表示、哈希与状态记录。先理解状态、操作与不变量,再沿下列路线练习。
  1. 01

    数位与进位

    先用少量变量表示未处理数位和进位,理解逐步消费输入。

  2. 02

    存在与索引

    从“出现过没有”升级为“在哪里出现”,建立先查询、后写入的顺序。

  3. 03

    频次与一致性

    值的含义从索引变成数量或映射关系,学习检查多种状态。

  4. 04

    等价类与状态图

    主动设计规范化键,或用集合判断起点与循环。

  5. 05

    组合与多维约束

    将配对、区间与行列宫关系转成可以查询的历史状态。

每道题在框架中的具体形式

用同一组问题定位每个单元,再观察迁移题改变了哪个条件。进入写代码环节后,学习地图会收起,留出独立思考的空间。

UNIT 01数位与进位

#9 回文数

保存的状态
未消费的高位整数与已经反转的低位。
查询与更新
逐次取最低位,消费原数位,把低位积累为反向表示。
正确性契约
两部分分别表示同一整数尚未处理与已经处理的数位。
边界与增量
负数和非零尾零先排除;奇数位中心不能重复比较。
迁移 · #7 整数反转迁移到整数反转时处理全部数位,并检查有符号整数范围。
先说清契约,再独立实现进入本单元 ↗
UNIT 02数位与进位

#66 加一

保存的状态
数位数组、当前下标与进位。
查询与更新
从最低位向左传播进位,进位消失后可结束。
正确性契约
当前位置右侧已成为最终结果,剩余影响仅由进位表示。
边界与增量
连续的 9 与最高位新增数位;本题不需要哈希。
迁移 · #67 二进制求和二进制求和增加第二个输入和不同长度,基数从 10 改为 2。
先说清契约,再独立实现进入本单元 ↗
UNIT 03存在与索引

#217 存在重复元素

保存的状态
集合保存已经扫描过的元素值。
查询与更新
先判断当前值是否已出现,再记录当前值。
正确性契约
集合恰好表示当前元素之前的历史值。
边界与增量
空历史不产生重复,不能先写入再查询自己。
迁移 · #219 存在重复元素 II重复元素 II 要记录最近索引,并检查索引差是否不超过 k。
先说清契约,再独立实现进入本单元 ↗
UNIT 04存在与索引

#1 两数之和

保存的状态
映射保存历史元素值对应的索引。
查询与更新
先查询目标补数,再写入当前值和索引。
正确性契约
候选搭档来自之前的元素,不会让同一索引匹配自己。
边界与增量
两个相同数可以来自不同索引;重复键仍须保证合法配对。
迁移 · #167 两数之和 II - 输入有序数组有序输入可改用相向指针,利用单调性排除整批数对。
先说清契约,再独立实现进入本单元 ↗
UNIT 05频次与一致性

#242 有效的字母异位词

保存的状态
字符到剩余数量的频次表。
查询与更新
建立第一个字符串的库存,再消耗第二个字符串的字符。
正确性契约
处理过程中,表中数量表示尚未匹配的字符余额。
边界与增量
长度不同或消耗不足即失败;数量不能只用存在性代替。
迁移 · #383 赎金信赎金信允许库存剩余,条件从数量相等改为库存足够。
先说清契约,再独立实现进入本单元 ↗
UNIT 06频次与一致性

#205 同构字符串

保存的状态
两个方向的字符映射。
查询与更新
检查历史配对是否冲突,再同时建立正向与反向关系。
正确性契约
每个源字符只对应一个目标字符,目标字符也不能被不同源占用。
边界与增量
仅维护单向映射不能保证一一对应。
迁移 · #290 单词规律单词规律把目标字符改成单词,仍需双向一致性。
先说清契约,再独立实现进入本单元 ↗
UNIT 07等价类与状态图

#49 字母异位词分组

保存的状态
规范化字符频次键到单词列表的映射。
查询与更新
为每个单词构造稳定的等价类键,再归入对应分组。
正确性契约
同组单词的字符数量完全相同,不同组的键不同。
边界与增量
键必须可哈希,不能把不同字符或频次混在一起。
迁移 · #890 查找和替换模式模式匹配的等价性改为首次出现位置结构,而非字符数量。
先说清契约,再独立实现进入本单元 ↗
UNIT 08等价类与状态图

#128 最长连续序列

保存的状态
所有不同数值组成的集合。
查询与更新
仅从不存在前驱的数值出发,沿连续后继扩展。
正确性契约
每段连续整数链只从唯一的最小值开始处理。
边界与增量
重复值不增加长度,输入中的相邻位置不等于数值连续。
迁移 · #674 最长连续递增序列连续递增序列依赖原数组的相邻顺序,因此改为线性段扫描。
先说清契约,再独立实现进入本单元 ↗
UNIT 09等价类与状态图

#202 快乐数

保存的状态
当前整数状态与已经出现过的状态集合。
查询与更新
生成各位平方和;到达 1 则成功,遇到历史状态则检测到循环。
正确性契约
每次转换都是确定性的,重复状态意味着未来轨迹重复。
边界与增量
记录的是完整数值状态,而非某一个数位。
迁移 · #141 环形链表环形链表把数值转换换成 next 后继,可用快慢指针检测循环。
先说清契约,再独立实现进入本单元 ↗
UNIT 10组合与多维约束

#454 四数相加 II

保存的状态
左侧两个数组的配对和及其出现频次。
查询与更新
先统计左半配对和,再查询右半配对和所需的相反数。
正确性契约
每次命中增加的是索引组合数量,而非只增加一个不同数值。
边界与增量
重复值仍代表不同索引组合;计数和去重是不同任务。
迁移 · #15 三数之和三数之和要求返回唯一数值三元组,需要排序与去重。
先说清契约,再独立实现进入本单元 ↗
UNIT 11组合与多维约束

#560 和为 K 的子数组

保存的状态
当前前缀和与历史前缀和频次。
查询与更新
查询与当前前缀相差 k 的历史数量,再记录当前前缀。
正确性契约
历史表包含虚拟零前缀及当前元素之前的所有前缀。
边界与增量
负数使普通滑窗失效;先记录当前前缀会误计空区间。
迁移 · #974 可被 K 整除的子数组整除子数组改为按余数分组,统一负数余数的表示。
先说清契约,再独立实现进入本单元 ↗
UNIT 12组合与多维约束

#36 有效的数独

保存的状态
每一行、每一列、每一宫已经出现的数字。
查询与更新
跳过空格,同时检查三个范围,再同步记录数字。
正确性契约
所有已处理的非空格在行、列、宫中均无重复。
边界与增量
校验已有数字不代表棋盘一定能求解。
迁移 · #37 解数独解数独增加候选选择与撤销,从静态校验升级为约束搜索。
先说清契约,再独立实现进入本单元 ↗
认知任务 1
把整数看成可以逐位消费的状态
点击下一步,观察 1234 如何通过 % 10 取出最低位,再通过 // 10 删除最低位。
数值与哈希实验台状态 1 / 5
1234 仍是一个整体;下一步将执行 digit = number % 10。
补全一个同时维护 seen 与 frequency 的扫描器只填 4 个关键表达式
from typing import List, Optional, Tuple, Dict def summarize(values: List[int]) -> Tuple[Optional[Tuple[int, int]], Dict[int, int]]: seen = set() frequency = {} first_duplicate = None for index, value in enumerate(values): appeared_before = frequency[value] = frequency.get(value, 0) + if appeared_before and first_duplicate is None: first_duplicate = (value, ) return first_duplicate, frequency
代码—输入—seen—frequency 联动1 / 1
index / value尚未扫描
appeared_beforeNone
seenset()
frequency{}
first_duplicateNone
进入函数,尚未读取任何元素。

迁移目标:LeetCode #9 回文数

这不是直接要求你从空白写完整代码。先连接 Unit 00 的“数位提取”,再识别新增状态,随后用槽位拼接算法,最后运行拼接结果观察变量轨迹。

LeetCode #9|回文数|Palindrome Number

标签:数学 · 数位处理
简单

原题

给你一个整数 x。如果 x 正序和倒序读都相同,返回 true;否则返回 false。

  • 不能改变回文数的定义;
  • 进阶要求:不把整数转换成字符串;
  • 负数不是回文数,非零且末位为 0 的整数也不可能是回文数。
class Solution: def isPalindrome(self, x: int) -> bool: pass

示例与约束

输入:x = 121
输出:true
输入:x = -121
输出:false
输入:x = 10
输出:false

-2³¹ ≤ x ≤ 2³¹ - 1

先复用已经学过的动作

Unit 00 已掌握

x % 10 取出最低位;x //= 10 删除最低位。

→
#9 新目标

把取出的右半数位逆序拼成 reversed_half,再与剩余左半比较。

迁移不是换数字重新做,而是复用同一个“逐位消费”操作,并新增“右半部分状态”。

本题相对数位实验新增了什么?

Unit 00 数位实验LeetCode #9
number:尚未消费的数位x:尚未消费的左半部分
digit:本轮最低位digit:加入反转右半部分
只观察提取结果reversed_half:保存已提取数位的逆序
直到 number == 0只处理到 x <= reversed_half

先反转一半可以减少工作。奇数位数字最终需要忽略 reversed_half 的中间位。

把算法片段放入正确槽位

先点击左侧片段,再点击右侧目标槽位。每个槽位只接收一个片段;可随时清空重拼。

代码片段
执行顺序

运行你拼出的算法,再补关键表达式

当前 x—
reversed_half—
本轮 digit—
判断结果尚未运行
槽位拼接正确后可运行。

关键表达式补全

右下角可直接拖拽调高;高度会自动保存
阶段 1 / 6 · 可随时点击顶部阶段返回

Unit 00 Skill:先设计状态,再选择数据结构

当题目要求反复判断历史信息时,不要先问“是不是哈希题”,而要先写清楚:键代表什么、值代表什么、相同键意味着什么,以及查询与更新谁先发生。

数位触发信号处理整数每一位、反转数位、传播进位
set 触发信号只需判断某值以前是否出现过
dict 触发信号还要保存索引、次数、配对对象或候选集合
规范化键不同输入需要被归入同一等价类
前缀状态区间性质可写成两个历史状态的关系
关键顺序Query Old State → Produce Result → Update State
完成后页面会停留在这里

不会倒计时,也不会自动进入 Unit 01。你可以复习任一阶段、继续运行迁移轨迹,或在准备好后手动进入下一题。

手动进入 Unit 01 →
已保存