Hot100:缺失的第一个正数(First Missing Positive)原地索引定位 ACERS 解析

副标题 / 摘要 缺失的第一个正数是经典的“原地哈希/索引定位”题:把值放回它应该在的位置,再线性扫描即可找到答案。本文按 ACERS 拆解思路、工程应用与多语言实现。 预计阅读时长:12~15 分钟 标签:Hot100、数组、原地哈希 SEO 关键词:First Missing Positive, 缺失的第一个正数, 原地哈希, 索引映射, O(n) 元描述:O(n) 时间、O(1) 额外空间的原地索引定位解法,含工程场景与多语言代码。 目标读者 正在刷 Hot100 的学习者 想掌握“原地索引定位”模板的中级开发者 需要在原数组内做高效重排与定位的工程师 背景 / 动机 “找最小缺失正数”本质是一个定位问题: 如果能把值 x 放在索引 x-1 上,那么答案就是第一个不匹配的位置。 题目还要求 O(n) 时间和 O(1) 额外空间,逼迫我们放弃排序与哈希表, 转而使用原地置换的技巧。 核心概念 概念 含义 作用 原地哈希 用数组下标充当哈希桶 O(1) 额外空间 索引定位 值 x 应放到 x-1 构造可扫描的结构 置换交换 不断交换直到就位 线性时间完成 A — Algorithm(题目与算法) 题目还原 给你一个未排序的整数数组 nums,找出其中没有出现的最小正整数。 请实现 O(n) 时间复杂度并且只使用 常数级别额外空间的解决方案。 输入输出 名称 类型 描述 nums int[] 未排序整数数组 返回 int 最小缺失的正整数 示例 1(官方) 输入: nums = [1,2,0] 输出: 3 示例 2(官方) 输入: nums = [3,4,-1,1] 输出: 2 思路概览 对每个位置 i,把 nums[i] 放到它应该去的位置 nums[i]-1。 完成“就位”后,从左到右找到第一个 nums[i] != i+1 的位置。 该位置对应的正整数 i+1 即为答案;若全部匹配则答案为 n+1。 C — Concepts(核心思想) 关键模型 值 x 应该放在索引 x-1 方法归类 原地哈希(Index-as-Hash) 数组置换 / 位置归位 线性扫描验证 不变量 当置换结束时: 如果 nums[i] == i+1,说明正整数 i+1 存在; 第一个不匹配的 i,就是最小缺失正整数的位置。 ...

2026年1月24日 · 6 分钟 · map[name:Jeanphilo]

Active Record vs Data Mapper:差异、优缺点与选型

副标题 / 摘要 Active Record 把数据与持久化绑定在一起,Data Mapper 把持久化隔离为独立层。本文对比二者并给出选型建议。 目标读者 使用 ORM 的后端工程师 设计领域模型的开发者 需要做架构取舍的团队 背景 / 动机 项目变复杂时,持久化模型往往开始“侵入”业务逻辑。 理解 Active Record 与 Data Mapper 的差异,是避免架构污染的关键。 核心概念 Active Record:对象自己保存/加载(数据与持久化耦合) Data Mapper:持久化逻辑在独立映射层 领域模型纯度:业务模型是否被 ORM 污染 实践指南 / 步骤 小型项目可用 Active Record 复杂领域建议 Data Mapper 明确领域边界,避免 ORM 侵入 用 Repository 隔离持久化 测试业务逻辑时替换存储层 可运行示例 # Active Record 风格 class UserAR: def __init__(self, name): self.name = name def save(self): print("save", self.name) # Data Mapper 风格 class User: def __init__(self, name): self.name = name class UserMapper: def save(self, user: User): print("save", user.name) if __name__ == "__main__": UserAR("Alice").save() UserMapper().save(User("Bob")) 解释与原理 Active Record 简单直观,但把持久化耦合进领域模型。 Data Mapper 更复杂,但让业务逻辑更纯粹、更易测试。 ...

2026年1月24日 · 1 分钟 · map[name:Jeanphilo]

迪米特法则(最少知识原则):违例与修复示例

副标题 / 摘要 迪米特法则强调“只和直接朋友说话”。本文用示例说明违规写法,并给出修复方式。 目标读者 想降低耦合的工程师 负责代码评审与重构的开发者 需要维护大型系统的团队 背景 / 动机 深层链式调用让对象之间依赖过强,改动一个结构就影响一大片。 迪米特法则就是用来控制这种耦合的。 核心概念 最少知识原则:对象只了解直接依赖 消息委托:把内部结构封装在对象内 耦合控制:减少“链式访问” 实践指南 / 步骤 识别链式调用(a.b.c.d) 让中间对象提供必要方法 封装内部结构 避免跨层访问内部字段 可运行示例 class Wallet: def __init__(self, balance): self.balance = balance def has_enough(self, amount): return self.balance >= amount class User: def __init__(self, wallet): self.wallet = wallet def can_pay(self, amount): return self.wallet.has_enough(amount) def checkout(user, amount): # 违例:user.wallet.balance # 修复:user.can_pay return user.can_pay(amount) 解释与原理 通过让 User 暴露 can_pay 方法,调用方无需知道 wallet 的内部结构。 这样 wallet 内部变化时,调用方不需要改动。 ...

2026年1月24日 · 1 分钟 · map[name:Jeanphilo]

空对象模式的目的:消除空指针分支

副标题 / 摘要 空对象模式用“可用但无效果”的对象替代 null,减少分支判断与空指针风险。本文给出适用场景与示例。 目标读者 频繁处理空指针的工程师 需要简化分支逻辑的开发者 关注代码可读性的团队 背景 / 动机 到处写 if obj is None 会让代码变得难读且易遗漏。 空对象模式通过提供“默认实现”,让调用方无需关心空值。 核心概念 空对象:实现相同接口但执行空操作 统一接口:调用方不区分真实对象与空对象 可替代性:替代 null 而不破坏逻辑 实践指南 / 步骤 定义统一接口 实现真实对象与空对象 在创建阶段选择真实/空对象 调用方不写 null 分支 可运行示例 class Notifier: def send(self, msg: str) -> None: raise NotImplementedError class EmailNotifier(Notifier): def send(self, msg: str) -> None: print("email:", msg) class NullNotifier(Notifier): def send(self, msg: str) -> None: pass def get_notifier(enabled: bool) -> Notifier: return EmailNotifier() if enabled else NullNotifier() if __name__ == "__main__": notifier = get_notifier(False) notifier.send("hello") # 不需要 if 判断 解释与原理 空对象模式把“缺失”变成一个合法对象。 这样调用方无需分支判断,避免空指针错误。 ...

2026年1月24日 · 1 分钟 · map[name:Jeanphilo]

全局对象为何危险:隐藏依赖与测试失控

副标题 / 摘要 全局对象让依赖变隐式,导致难以测试、难以演进。本文用例子说明其危害,并给出可行替代方案。 目标读者 需要提高可测试性的工程师 经常处理“隐式依赖”的开发者 负责代码质量的团队 背景 / 动机 全局对象看似方便,但会让模块互相耦合,导致“改一个地方牵一大片”。 这在大型系统里是灾难。 核心概念 隐式依赖:调用方不明确传入依赖 共享状态:多个模块写同一对象 测试隔离难:全局状态污染测试 实践指南 / 步骤 识别全局状态 用依赖注入替代 通过参数显式传递依赖 在测试中替换依赖 消除跨模块共享写入 可运行示例 # 反例:全局对象 CONFIG = {"rate": 0.1} def calc(price): return int(price * (1 - CONFIG["rate"])) # 改进:显式注入 def calc_with_config(price, config): return int(price * (1 - config["rate"])) if __name__ == "__main__": print(calc(100)) print(calc_with_config(100, {"rate": 0.2})) 解释与原理 全局对象让依赖隐藏在模块内部,测试时很难替换。 显式传递依赖可以让函数可复用、可测试。 常见问题与注意事项 配置放全局不是很方便吗? 方便但危险,建议在初始化阶段注入。 全局常量也危险吗? 常量问题不大,主要问题在可变全局状态。 如何迁移? 从最核心模块开始逐步消除全局依赖。 最佳实践与建议 用依赖注入替代全局对象 把配置集中在启动入口 尽量避免可变全局状态 小结 / 结论 全局对象是隐藏依赖的温床。 显式依赖是系统可测试与可维护的基础。 ...

2026年1月24日 · 1 分钟 · map[name:Jeanphilo]

为什么组合优于继承:灵活性、可测试性与演进成本

副标题 / 摘要 继承容易让系统变脆,组合让系统更灵活。本文解释为什么组合更适合工程演进,并给出实用示例。 目标读者 写面向对象代码的工程师 负责模块演进与重构的开发者 做代码评审与架构设计的团队 背景 / 动机 继承会把父类的实现细节暴露给子类,容易导致“脆弱基类问题”。 组合通过“把能力作为对象注入”来降低耦合,更易测试与替换。 核心概念 继承(Inheritance):is-a 关系,强耦合 组合(Composition):has-a 关系,弱耦合 脆弱基类问题:父类改动导致子类行为改变 实践指南 / 步骤 优先建接口,延后继承 把可变行为抽成组件 用组合注入行为 通过依赖替换实现测试 只在“稳定共性”时使用继承 可运行示例 class Logger: def log(self, msg: str) -> None: print(msg) class FileSaver: def save(self, data: str) -> None: print("save", data) class ReportService: def __init__(self, logger: Logger, saver: FileSaver): self.logger = logger self.saver = saver def run(self, data: str) -> None: self.logger.log("start") self.saver.save(data) self.logger.log("done") if __name__ == "__main__": svc = ReportService(Logger(), FileSaver()) svc.run("report") 解释与原理 组合让行为可替换(如 Logger 可以换成 Mock)。 继承则把依赖固定在父类上,一旦父类变化,子类难以控制影响。 ...

2026年1月24日 · 1 分钟 · map[name:Jeanphilo]

Hot100:除自身以外数组的乘积(Product of Array Except Self)前后缀乘积 ACERS 解析

副标题 / 摘要 除自身以外数组的乘积是典型的前后缀乘积题:不使用除法,在 O(n) 时间内完成。本文按 ACERS 结构拆解题意与算法,并给出工程迁移场景与多语言实现。 预计阅读时长:10~12 分钟 标签:Hot100、数组、前缀乘积 SEO 关键词:Product of Array Except Self, 除自身以外数组的乘积, 前缀乘积, 后缀乘积, O(n) 元描述:用前后缀乘积在 O(n) 时间内解决除自身以外数组的乘积问题,含工程场景与多语言代码。 目标读者 正在刷 Hot100 的学习者 想掌握“前后缀乘积”模型的中级开发者 需要做序列因子组合与乘积聚合的工程师 背景 / 动机 很多业务需要“排除自身的整体乘积”: 例如组合指标、冗余度评估、批量权重计算等。 若直接对每个位置做一次全数组相乘,复杂度会退化为 O(n^2); 而题目还明确禁止使用除法,因此必须依赖前后缀乘积的线性解法。 核心概念 前缀乘积:prefix[i] = nums[0] * ... * nums[i-1] 后缀乘积:suffix[i] = nums[i+1] * ... * nums[n-1] 无除法:只允许乘法与遍历 空间优化:用结果数组承载前缀,再用后缀补乘 A — Algorithm(题目与算法) 题目还原 给定一个整数数组 nums,返回数组 answer, 其中 answer[i] 等于 nums 中除了 nums[i] 之外其余各元素的乘积。 题目保证任意元素的前缀/后缀乘积都在 32 位整数范围内。 要求:不使用除法,并在 O(n) 时间内完成。 ...

2026年1月24日 · 5 分钟 · map[name:Jeanphilo]

第一方 Cookie vs 第三方 Cookie:差异、风险与政策

副标题 / 摘要 第一方 Cookie 与第三方 Cookie 的核心差异在于“上下文”和“隐私风险”。本文解释浏览器为何区别对待它们。 目标读者 前端与后端工程师 需要理解隐私策略的开发者 做广告与分析系统的团队 背景 / 动机 第三方 Cookie 曾是广告跟踪的核心,但带来了严重隐私问题。 因此浏览器逐步限制第三方 Cookie。 核心概念 第一方 Cookie:与当前域名一致 第三方 Cookie:来自嵌入内容的其他域 SameSite:限制跨站请求携带 Cookie 隐私合规:GDPR、CCPA 等法规 实践指南 / 步骤 区分业务场景:认证优先第一方 设置 SameSite 属性 避免依赖第三方 Cookie 评估替代方案(Server-Side Tracking) 遵守隐私法规 可运行示例 Set-Cookie: session=abc; Path=/; HttpOnly; Secure; SameSite=Lax 解释与原理 第三方 Cookie 允许跨站跟踪用户行为,隐私风险高。 浏览器限制第三方 Cookie 是为了减少用户被追踪。 常见问题与注意事项 第一方 Cookie 就安全吗? 不一定,仍需防止 XSS/CSRF。 第三方 Cookie 会完全消失吗? 趋势是限制,但不会立刻彻底消失。 SameSite 应该怎么选? 默认 Lax,只有必要时才用 None。 ...

2026年1月24日 · 1 分钟 · map[name:Jeanphilo]

如何维护单体架构:模块化、边界与演进

副标题 / 摘要 单体架构不是原罪。关键在于模块化、边界清晰与可演进。本文给出维护单体系统的实践策略。 目标读者 正在维护单体系统的工程师 需要评估拆分成本的团队 负责架构演进的技术负责人 背景 / 动机 许多系统在“过早拆分”后陷入分布式复杂性。 单体如果维护得当,反而更稳定、成本更低。 核心概念 模块化:内部清晰分区 边界控制:禁止跨模块直连 演进路径:可拆分而不必拆分 实践指南 / 步骤 建立模块边界(包/目录/接口) 禁止跨模块直接访问数据层 模块间只通过接口通信 建立集成测试与契约测试 识别可拆分的候选模块 可运行示例 class OrderRepo: def get(self, oid): return {"id": oid, "price": 100} class OrderService: def __init__(self, repo): self.repo = repo def total(self, oid): order = self.repo.get(oid) return order["price"] 解释与原理 单体架构的问题通常不是“体量”,而是“边界模糊”。 清晰的模块边界能让单体具备类似微服务的可维护性。 常见问题与注意事项 单体是否一定要拆分? 不一定,先优化结构再决定。 如何判断是否需要拆分? 看团队协作边界与部署频率。 模块化会影响性能吗? 一般影响极小,维护收益更大。 最佳实践与建议 用清晰目录与接口表达边界 保持核心模块独立 先做“逻辑拆分”,再考虑“物理拆分” 小结 / 结论 单体架构可以长期健康运行,只要边界清晰、模块可演进。 不要把拆分当作唯一答案。 参考与延伸阅读 Monolith to Microservices (Sam Newman) Modular Monolith Patterns 元信息 阅读时长:7~9 分钟 标签:单体架构、模块化、演进 SEO 关键词:Monolith, 模块化 元描述:维护单体架构的工程策略与演进方法。 行动号召(CTA) 为你的单体系统画一张模块边界图,检查哪些依赖是违规的。

2026年1月24日 · 1 分钟 · map[name:Jeanphilo]

设计、架构、功能与美学:它们分别解决什么问题?

副标题 / 摘要 设计关心方案与体验,架构关心结构与演进,功能关心“能做什么”,美学关心“好不好看、好不好用”。本文给出清晰区分与落地方法。 目标读者 负责产品与工程协作的开发者 需要做系统设计的工程师 希望减少沟通成本的团队负责人 背景 / 动机 很多团队在沟通时把“功能、设计、架构、美学”混在一起,导致讨论失焦。 明确它们的职责边界,是跨职能协作的前提。 核心概念 功能(Functionality):系统能做什么,输出什么结果 设计(Design):满足需求的方案与交互流程 架构(Architecture):系统结构、组件边界与可演进性 美学(Aesthetic):视觉与体验层面的感知质量 实践指南 / 步骤 先定义功能边界:输入/输出与业务规则 再做设计方案:交互流程与用户路径 确定架构结构:模块划分、接口与扩展方式 补齐美学细节:视觉层级与一致性 建立协作节奏:设计评审与架构评审分开 可运行示例 下面用一个简单例子展示“功能 vs 美学”的分离: def calc_total(items): return sum(price for _, price in items) def render_receipt(total, theme="minimal"): if theme == "minimal": return f"Total: {total}" return f"*** TOTAL ***\n{total}\n***********" if __name__ == "__main__": items = [("apple", 3), ("milk", 5)] total = calc_total(items) # 功能 print(render_receipt(total, theme="minimal")) # 美学 解释与原理 功能是“正确性”,设计是“可用性”,架构是“可演进性”,美学是“感知质量”。 把它们混在一起会造成目标冲突、决策混乱。 常见问题与注意事项 美学是不是不重要? 不是,它影响使用意愿与信任感。 架构是不是过度设计? 不是,架构关注长期演进与成本控制。 设计和架构可以同时定吗? 可以并行,但要保持职责边界。 ...

2026年1月24日 · 1 分钟 · map[name:Jeanphilo]