2026-03-13 22:24:00
![]()
在笔试(ACM模式)中,Python 的 input() 有时较慢,推荐使用 sys.stdin.readline。
|
|
很多题目第一行是 Case 数 T,后面跟 T 行数据。
推荐写法 (Standard Way):
利用 sys.stdin 是一个迭代器的特性,先手动读取第一行消耗掉,再循环。
|
|
题目说“输入 N 个整数”,但数据可能写在同一行,也可能分多行。
千万不要只用 sys.stdin.readline().split(),因为它只读一行,如果数据换行了就会报错。
万能写法 (Recommended):
使用 sys.stdin.read().split() 读取所有剩余输入,它会自动忽略所有空白符(空格、换行、制表符)。这是 ACM 模式处理复杂输入的神器。
|
|
如果确定输入即为一行空格分隔的整数(如:1 2 3 4 5),直接使用 map。
|
|
题目输入 n 行 m 列的矩阵。
|
|
print(" ".join(map(str, ans)))
f"{x:.3f}" (保留 3 位小数,自动四舍五入) -> 1.235
cnt.zfill(5) 或 f"{cnt:05d}" -> 00123
f"{x:.2%}" -> 12.34%
print 可能超时。
|
|
|
|
range() 函数: range(start, stop, step) 左闭右开。
range(5) -> 0, 1, 2, 3, 4
range(1, 4) -> 1, 2, 3
range(5, 0, -1) -> 5, 4, 3, 2, 1 (倒序遍历常考)enumerate(): 同时获取索引和值 (推荐)。
|
|
|
|
|
|
arr = [] (空列表)arr = [0] * n (初始化 n 个 0, 比如 DP 数组)matrix = [[0] * m for _ in range(n)] (n 行 m 列二维数组,千万别用 [[0]*m]*n)arr.append(x): 末尾添加 O(1)arr.pop(): 弹出末尾 O(1)arr.insert(i, x): 在 i 处插入 O(N) (慎用)len(arr): 长度 O(1)arr.sort(): 原地排序 O(N log N)arr.reverse(): 原地翻转 O(N)arr[start:end:step]
arr[::-1]: 翻转列表s = set() (空集合,注意不是 {},{} 是空字典)s = {1, 2, 3}s = set(arr) (列表转集合去重)s.add(x): 添加 O(1)s.remove(x): 删除 O(1) (不存在报错)x in s: 判断存在 O(1)s1 | s2 (并), s1 & s2 (交), s1 - s2 (差)d = {} (空字典)d = {'a': 1, 'b': 2}d['k'] = v: 设置/更新val = d.get('k', default): 安全获取 (不存在返回 default)key in d: 判断 key 是否存在 O(1)del d['k']: 删除 keyfor k, v in d.items(): ... (遍历键值对)for k in d: ... (只遍历键)list.pop(0) 是 O(N) 的(因为要移动数组),而 deque.popleft() 是 O(1) 的。
|
|
|
|
if key not in d 的判断。
|
|
-val。
|
|
处理字符串偏移(如 ‘a’->‘b’)时必用。
ord(char): 字符 -> ASCII 数值。
ord('a') 是 97, ord('A') 是 65, ord('0') 是 48。chr(int): ASCII 数值 -> 字符。
chr(97) 是 ‘a’。
|
|
bin(n): 转二进制字符串 (e.g., '0b101').hex(n): 转十六进制字符串 (e.g., '0xa').int(string, base): 任意进制转十进制。
int('101', 2) -> 5int('A', 16) -> 10int 是任意精度的(自动扩容),不会溢出。不用担心 C++ 中的 int vs long long 问题,可以直接计算超大整数。divmod(a, b): 返回 (a // b, a % b),商和余数。pow(a, b, mod): 快速幂求模 (a ** b) % mod,比 (a**b)%mod 快得多。abs(x): 绝对值。round(x, n): 四舍五入保留 n 位小数。a^b,特别是 (a^b) % mod。O(b),快速幂是 O(log b)。pow(a, b, mod)。
|
|
- **等价关系**: `qpow(a, b, mod) == pow(a, b, mod)`。
math 模块:
math.ceil(x), math.floor(x): 向上/向下取整 (注意: int 默认是向 0 取整)。math.gcd(a, b): 最大公约数。math.lcm(a, b): 最小公倍数 (Python 3.9+)。math.pi: 圆周率 (3.1415926…),比用 3.14 精确。math.sqrt(x): 平方根 (返回 float)。math.isqrt(x): 整数平方根 (返回 int, 向下取整, 比 floor(sqrt) 准)。math.inf: 无穷大 (常用于初始化最小值 min_val = float('inf'))。/ 得到的是 float,整数整除必须用 //。字符串是不可变对象 (Immutable),所有修改操作都会返回新字符串。
大小写:
s.lower(), s.upper(): 转小写/大写s.capitalize(): 首字母大写s.title(): 每个单词首字母大写查找与判断:
s.startswith(prefix), s.endswith(suffix): 前后缀判断s.find(sub): 返回第一个索引,找不到返 -1 (推荐)s.index(sub): 返回第一个索引,找不到报错 (慎用)s.count(sub): 统计子串出现次数s.isdigit(): 是否全数字s.isalpha(): 是否全字母s.isalnum(): 是否全数字或字母拆分与合并:
s.strip(): 去除两端空白 (经常用于处理 input)s.split(sep): 按 sep 分割,默认按空白符 (空格、换行、制表)
line = " a b "; line.split() -> ['a', 'b'] (自动去空,结果无需再 strip)sep.join(iterable): 将列表组合成字符串 (效率高,千万别用 + 循环拼接)
' '.join(['a', 'b']) -> 'a b' (空格连接)"".join(['a', 'b']) -> 'ab' (无缝拼接,替代 s += x)List <-> Str 互转 (修改字符串技巧):
list(s): 字符串转字符列表 (为了修改)。
s = "abc"; chars = list(s) # ['a', 'b', 'c']chars[0] = 'X'"".join(chars): 列表转回字符串。
new_s = "".join(chars) # "Xbc"替换与切片:
s.replace(old, new, count): 替换,count 可选替换次数s[::-1]: 字符串翻转 (最快写法)学会这些,一行代码顶十行。
function 作用于 iterable 中的每一个元素。list() 包裹才能看到完整列表(除非只是用来遍历或求和)。map(int, ['1', '2']) -> [1, 2]
a, b = map(int, input().split())
function(x) 为 True 的元素。nums = [1, 2, 3, 4]evens = list(filter(lambda x: x % 2 == 0, nums)) -> [2, 4]
lambda arguments: expression
sort, max, map, filter 使用。
arr.sort(key=lambda x: x[1])
longest = max(strings, key=lambda s: len(s))
from functools import reduceproduct = reduce(lambda x, y: x * y, [1, 2, 3, 4]) -> 24sum 展平一层(虽然慢点)。sum([1, 2, 3]) -> 6sum([[1, 2], [3, 4]], []) -> [1, 2, 3, 4] (慎用,O(N^2))list.sort() 是原地排序,无返回值。sorted。
sorted("bca") -> ['a', 'b', 'c']
a, b = b, a (交换); x, *rest = [1, 2, 3] (x=1, rest=[2,3]).for x, y in zip(list1, list2):.for i, val in enumerate(list):.if any(x > 0 for x in nums): (是否存在); if all(...) (是否所有).Python 标准库 bisect 非常好用。
bisect.bisect_left(arr, x): 寻找插入点,使得左边都 < x (即第一个 >= x 的位置)。bisect.bisect_right(arr, x): 寻找插入点,使得左边都 <= x (即第一个 > x 的位置)。也可手动实现二分:
|
|
常用于处理数组、字符串子串问题。
|
|
BFS (最短路径/层序遍历):
|
|
DFS (全排列/连通性):
|
|
Tip: 遇到 RecursionError 需要增加递归深度:sys.setrecursionlimit(2000)
处理集合合并、连通分量问题。
|
|
dp[i][j] 和状态转移方程。
|
|
if x in list 是 O(N),改为 if x in set 是 O(1) 平均。s += "a" 在循环中拼接字符串(O(N^2)),使用 "".join(list_of_strings)(O(N))。sys.stdin.readline 和 sys.stdout.write。heapq.nlargest 更优。RecursionError,记得 sys.setrecursionlimit(20000)。[x*2 for x in huge_list] 比 append 快,也会比 map 好调试(虽然 map 有时极微快)。@functools.lru_cache(None) 装饰器自动缓存递归结果 (DP)。(x**2 for x in range(1000000)) 而不是列表 [...],这就只占一个迭代器内存。nums.sort() (in-place) 而不是 sorted(nums) (new list),除非需要保留原数组。dict 或 defaultdict 代替大数组。__slots__ 可以极大减少对象内存占用(笔试通常不用,工程中有用)。itertools.islice, chain, count 等,避免生成中间列表。祝好运!
2026-03-11 14:23:00
![]()
在搭建个人博客的过程中,我一直有一个执念:希望网页底部的音乐播放器能够像网易云音乐那样,在页面切换时永不中断。
传统的静态博客(如 Hugo 生成的站点)每一次点击链接,浏览器都会重新加载整个页面 (Full Page Reload)。这意味着:
为了解决这个问题,我们需要引入 SPA (Single Page Application) 的概念,或者更轻量级的方案 —— Pjax (PushState + Ajax)。
Pjax 的工作原理非常直观:
<a> 标签的点击事件。Ajax 请求新页面的 HTML 内容。.main-container)。history.pushState 修改浏览器的 URL地址栏,使其看起来像正常跳转。通过这种方式,页脚 (Footer) 和 侧边栏 (Sidebar) 可以保持不变,驻留在其中的音乐播放器自然也就不会中断了。
首先在 <head> 中引入 Pjax 库(推荐使用 pjax 库而非老旧的 jquery-pjax):
|
|
这是最关键的一步。为了保证 Stack 主题的正常渲染,如果你直接替换整个 body,播放器还是会挂掉。我们需要精准打击。
我在 layouts/partials/head/custom.html 中进行了如下配置:
|
|
关键点:不仅要通过 CSS 选择器指定更新区域,还要自定义 switch 函数,确保 body 标签只更新属性而不重置内容。
实现 Pjax 只是第一步,真正的挑战在于副作用。
现象:跳转到 Timeline 页面,Mastodon 动态加载不出来。
原因:通过 innerHTML 插入的 HTML 片段中如果包含 <script> 标签,浏览器出于安全和规范考虑,通常不会执行它们。
解决方案:
我们将初始化代码封装为全局函数,并在 Pjax 完成事件 (pjax:complete) 中手动调用。
|
|
现象:虽然使用了 Pjax,但用户有时会习惯性按 F5 刷新,或者 Pjax 请求超时回退到普通跳转,这时候音乐还是会断,且进度归零。
解决方案:状态持久化 (State Persistence)。
利用 localStorage 在播放器每秒更新时记录状态:
|
|
在页面加载时(无论是 Pjax 还是普通加载),尝试恢复状态:
|
|
这里还有一个细节:audio 元素必须在元数据加载后才能 seek,所以需要监听 loadedmetadata 或 canplay 事件。
通过引入 Pjax 并配合精细的生命周期管理,我们成功在静态博客上实现了类似 SPA 的流畅体验:
折腾博客的乐趣往往不在于写文章本身,而在于通过解决这些具体的技术问题,窥探现代 Web 开发的冰山一角。
2026-03-10 10:24:00
![]()
ConfigManager 配置管理器,就必须是一个单例。你在简历上面试时可以说:“为了避免多次读取配置文件造成 I/O 浪费,我用 C++11 的 std::call_once(或局部静态变量)实现了一个线程安全的单例配置中心。”Subject(被观察者),把负责发送网络请求的模块、负责写本地日志的模块作为 Observer(观察者)。显存一旦超过阈值,主动“通知”这些模块报警,而不是让它们写个 while(true) 死循环去轮询。DeviceFactory,传入 “NVIDIA”,它就吐出一个调用 NVML API 的对象;传入 “CPU”,它就吐出一个读取 /proc/cpuinfo 的对象。
|
|
|
|
时间复杂度 (Time Complexity)nums[0],或者在字典/集合中查值 if key in my_dict:。while left < right: 配合 mid)。for 循环,从头到尾扫一遍。nums.sort(),或者用了归并/快速排序。for 循环。面试官通常会让你把它优化到 $O(n)$。空间复杂度 (Space Complexity)left, right),没有开辟随数据规模增大的新空间。Hash (Dict / Set) 用法速查底层皆为哈希表,查找/插入的时间复杂度均摊为 $O(1)$。
dict (字典):存键值对。
初始化:d = {}
增/改:d[key] = value
查:if key in d:
set (集合):存无序不重复元素。
初始化:s = set()
增:s.add(value)
查:if value in s:
Pythonic 循环抛弃 for(int i=0; i<n; i++),只记以下三种:
for num in nums:
for i in range(len(nums)):
for i, num in enumerate(nums):
核心标准库 (绝不重复造轮子)collections.Counter (计数器)count[i] = count[i] + 1 循环。
|
|
collections.deque (双端队列)list 做队列。
|
|
heapq (优先队列 / 堆)std::priority_queue。
|
|
|
|
|
|
ord(char) —— 字符转索引index = ord(char) - ord('a')
0-25,配合数组做计数。chr(code)。ord() 的逆运算,如 chr(97) 返回 'a',常用于将计算后的索引还原为字符。sorted(s) —— 排序法return sorted(s) == sorted(t)
list.sort(reverse=True)。sorted() 返回新列表,支持所有可迭代对象;list.sort() 是列表的原地排序(无返回值)。Counter —— 词频统计return Counter(s) == Counter(t)
Counter(s).most_common(k)。返回前 k 个高频元素,解决 Top K 问题的神器。[0] * n —— 数组快速初始化count = [0] * 26
dict 更快更省空间。[[0] * n for _ in range(m)]。构建二维数组(矩阵)的正确写法,必须用列表推导式以避免引用拷贝错误。
|
|
|
|
|
|
|
|
nums.index(val) —— 列表查找idx = nums.index(target - n)
for 循环里用它,时间复杂度直接退化为 $O(n^2)$。因为它底层是线性扫描。hash_map[key]。面试官想看的是用哈希表将查找降维到 $O(1)$。Two Pointers —— 双指针法l, r = 0, len(nums) - 1,配合 while l < r。nums.sort()。双指针的前置条件通常是数组有序。range(i + 1, n) —— 内层循环起点for j in range(i + 1, len(nums)):
i + 1 开始。(A, B) 又计算 (B, A)。enumerate(nums) —— 枚举遍历for i, n in enumerate(nums):
range(len(nums))(只拿索引)、for n in nums(只拿值)。不要在需要索引时手动维护一个 count 变量,太土。
|
|
|
|
defaultdict(list) —— 默认字典res = defaultdict(list)
if key not in d: d[key] = [] 的判断。defaultdict(int)。用于计数,默认值为 0。tuple(mutable_obj) ——不仅是转换key = tuple(count)
frozenset。不可变集合,也可以做 Key。list(d.values()) —— 字典值导出return list(res.values())
d.values() 返回的是一个视图对象 (View),必须用 list() 强转才能符合题目要求的返回类型 List[List[str]]。''.join(sorted(s)) —— 字符串标准化key = "".join(sorted(s))
sorted(s) 返回的是列表 ['a', 'e', 't'](可变,不可做 Key),必须用 join 拼回字符串 "aet"。tuple(sorted(s))。如果不拼回字符串,转成元组也可以做 Key。1. 语言与阵地
2. 路线与开销
3. 核心刷题 SOP (15分钟法则)
4. 兜底退路与国内大厂补丁 (Pivot 方案)
5. NeetCode 的正确打开方式
NeetCode 的 Visualize the algorithm step by step 功能非常便于理解算法流程,至少对我来说,可视化地过一遍算法流程,就很清晰了。
2026-03-06 13:50:00
![]()
经历了一学期的研究生生活,lihan 变得保守了很多,也不再执着于读博与当教授,外企的技术岗是当前的首选目标了。兼顾当前课业与课题组压力以及身心健康,制定了一个目标明确的学习计划,计划的核心是提升自己的技术能力,兼顾项目开发与实习经历以及英语能力,以健康的身体和积极的心态迎接秋招。
🎯 终极目标 (The North Star): 2027年秋招/暑期实习斩获一线外企(Microsoft, Amazon, NVIDIA等)核心 Infra/后端 开发岗 Offer,实现 965 工作制、高时薪,拥有绝对的生活主导权。
主线任务由算法与 C++/AI Infra 工程双轮驱动。前端开发仅作为前置 3 天的工具测试,不占主线资源。
阶段一:基建与探路期 (研一下,至 2026 年 8 月)
算法:Python 刷通 LeetCode 经典 150 题(按分类),训练全英文“边写边说”能力。
工程:启动「异构 GPU 算力监控系统」。用 C++ 配合 NVML 写出轻量级数据采集探针,跑通本地与远端 5090 服务器的底层硬件状态拉取。
🛑 异常接管 (期末突击):学期末提前划出 3 周 纯粹用于期末复习。此期间【高能输出】状态冻结,一切为了保住 GPA,考完立刻解冻。
阶段二:并发与重构攻坚期 (研二上,2026.09 - 2027.02)
算法:二刷错题本,定向突击目标外企高频题库。
工程:引入 Python FastAPI 或 Go 重构通信网关,解决跨网络并发与数据持久化。
🛑 异常接管:同样预留 3 周 应对研二上期末考试。
阶段三:狙击与收网期 (研二下,2027.03 - 2027.09)
动作:精修全英文简历,开启 BQ(行为面试)的英文 Mock Interview。海投外企暑期实习。
终局:依靠 3 个月的暑期实习斩获 Return Offer,或携极具深度的 C++ 底层项目降维打击秋招。
你的每一天只会处于以下五种状态之一,绝不允许出现“边学边玩”的模糊中间态。
针对每周只有四节课的现状,将上课时间直接视为【🔵 防御敷衍】模块。如果在课上,则挂机听讲,大脑后台构思代码或复习计网/OS理论。
| 时间块 | 状态机 | 核心执行逻辑与输入/输出 | 时长 |
|---|---|---|---|
| 08:30 - 09:30 | 系统冷开机 | 洗漱、早餐。 浏览开源社区或科技资讯,平缓启动大脑。 | 1.0h |
| 09:30 - 11:30 | 🟢 高能输出 | 算法与理论底座 (Python/CS理论) | |
| 精力峰值时段。刷 1-2 道 LeetCode,强制英文口述思路。 | 2.0h | ||
| 11:30 - 14:00 | 物理断电 | 午餐 + 深度午休。 雷打不动睡 40-60 分钟。 | 2.5h |
| 14:00 - 17:00 | 🟢 高能输出 | AI Infra 底层工程实战 (C++) | |
| 操作本地高配机器与远端服务器,死磕内存管理与网络并发。 | 3.0h | ||
| 17:00 - 19:00 | 硬件维护 | 体能训练与晚餐。 健身房或操场,切换轨道,缓解颈椎压力。 | 2.0h |
| 19:00 - 21:30 | 🔵 防御敷衍 | 导师横向与学校课业 | |
| 降维处理杂活与作业。到点准时停手。(若此时在上课,则此模块平移至上课时段) | 2.5h | ||
| 21:30 - 23:00 | 🟢 / 🟡 判定 | 复盘或触发合法狂欢 | |
| 若当日高能任务 100% 达成,立刻启动游戏;未达成则做最后冲刺与英文 Bug 复盘。 | 1.5h | ||
| 23:00 - 24:00 | 内存清理 | 降温与休眠。 洗澡、看闲书、刷 B 站。24:00 强制熄灯休眠。 | 1.0h |
以下记录就业相关信息,以备查阅:
利用 AI 同时熟悉现代前端开发框架,快速搭建一个 Todo Web APP,作为本计划的状态流转记录工具。
2026-02-06 01:00:00
![]()
| 技术 | 作用 |
|---|---|
| Windows | 操作系统与 GUI 编辑器环境 |
| WSL2 | Linux 核心环境,和服务器一致 |
| nvm | Node 版本管理器,保证项目 Node 版本一致 |
| Node.js | JS/TS 运行环境 |
| pnpm | 高效包管理器,安装依赖与管理脚手架 |
| Next.js + React + TS | 现代前端框架,支持组件化与页面路由 |
| Docker | 插件化工具容器化,保证隔离和可移植性 |
| 阶段 | 学习目标 | 依赖 |
|---|---|---|
| 技术工具 | [x] 开发环境搭建:Windows + WSL2 + VS Code Remote-WSL,Docker 容器化思维 | WSL2 安装与配置、VS Code Remote-WSL、Docker&&K8s学习笔记
|
| [ ] Node.js + TypeScript 入门:Node 运行机制、nvm 版本管理、npm / pnpm / yarn 使用、TypeScript 基础 | Nodejs+TypeScript_技术工具学习记录 |
|
| [ ] Next.js 现代前端开发:React + Next.js + TS、组件化开发、页面路由与 API 路由、插件化平台思维 | Next.js 官方文档、React 官方文档、UI 组件库(Ant Design / Tailwind UI)、Docker 容器化工具 | |
| 基础知识 | 了解前后端解耦与现代 WEBAPP 架构 | HTTP/HTTPS、RESTful API、GraphQL、微服务与插件化服务思维 |
| 掌握模块化与依赖管理概念 | CommonJS / ES Module、模块加载机制、包管理器原理 | |
| 熟悉前端组件化设计与状态管理 | React 组件设计模式、状态管理(Zustand / Redux / Context API) | |
| 产出学习 | 能搭建插件化 WEBAPP 平台,整合前端页面与 Docker 插件工具 | Node + Next.js + Docker API 调用、前端组件化开发、插件化工具集成 |
| 通过小项目练习技术链 | 小工具插件(音乐播放器、计算器、小游戏、AI Agent)容器化 + 前端调用 | |
| 建立完整开发与部署流程能力 | Windows → WSL2 → nvm → Node → pnpm → Next.js → Docker 插件化平台 |
|
|
核心思维:环境统一 → Node 版本稳定 → 高效依赖管理 → 组件化前端 → 容器化服务 → 插件化平台
2026-02-06 01:00:00
![]()
包管理器:推荐 pnpm,安装与验证:
VS Code 配置:
类似于 python 的 conda,nvm(Node Version Manager)是一个用于管理多个 Node.js 版本的工具。它允许你在同一台机器上安装和切换不同版本的 Node.js,非常适合开发者在不同项目中使用不同版本的 Node.js。
类似于 python 的 pip,pnpm 是一个高效的 JavaScript 包管理器,提供了更快的安装速度和更少的磁盘空间占用。它通过使用符号链接来共享依赖项,从而避免了重复安装相同的包。
Node.js 官网 提供了 Node.js 的安装命令,在选择了 系统环境、Node 版本管理器、包管理器和Node.js 版本后,可以直接复制命令在终端中执行。
|
|
值得注意的是,nvm 和 pnpm 的安装步骤可能会因操作系统和环境的不同而有所差异,详见[
nvm 和 pnpm 的安装注意事项](# 7.1-nvm-和-pnpm-的安装注意事项)。
|
|
|
|
|
|
Docker 容器化运行
|
|
实践经验
由于 Node.js 25.x 版本的最新更新,Corepack 在 Node.js 25.x 中已被移除,因此需要手动安装 Corepack 来使用 pnpm 包管理器。
在 Node.js 25.x 中安装 pnpm 的步骤比 Node.js 24.x 多了一步:
``bash
npm install -g corepack
|
|