Docker 常用使用教程:从入门到 Compose 实战(含 save/load 与权限排障)

标题 Docker 常用使用教程:从入门到 Compose 实战(含 save/load 与权限排障) 副标题 / 摘要 很多人会 docker run,但一到离线交付和挂载权限就卡住。 本文按“能直接落地”的顺序,带你走完 Docker 常用命令、Dockerfile、Compose、save/load,以及最常见的 UID/GID 权限问题。 目标读者 想系统掌握 Docker 日常用法的开发者 需要用 Docker Compose 跑本地或测试环境的工程师 经常遇到“挂载目录写不进去”权限问题的人 背景 / 动机 在“我的机器能跑、你的机器跑不起来”的场景里,Docker 的价值不是概念,而是交付稳定性。 但实际使用中,常见断点通常在三处: 命令会用,但不知道整套流程怎么串起来 需要离线迁移时,不清楚 save/load 怎么和 Compose 配合 挂载宿主目录后,容器用户与宿主目录属主不一致导致 Permission denied 核心概念 镜像(Image):应用运行模板,分层存储,可复用 容器(Container):镜像的运行实例 仓库(Registry):镜像分发中心(如 Docker Hub) 卷(Volume):由 Docker 管理的数据持久化目录 Bind Mount:把宿主机目录直接挂载进容器 Compose:用一个 compose.yaml 管理多容器应用 一、安装与最小验证 先确保 Docker CLI 与 daemon 可用: docker --version docker info docker run --rm hello-world 如果 hello-world 能成功输出欢迎信息,说明基础环境已就绪。 ...

2026年2月12日 · 3 分钟 · map[name:Jeanphilo]

LeetCode 146:LRU 缓存设计(O(1))哈希表 + 双向链表实战

副标题 / 摘要 这题不是“背答案题”,而是缓存系统的基本功:如何在常数时间内同时满足“快速访问”和“按最近最少使用淘汰”。本文从朴素方案推到最优结构,并给出可运行的多语言实现。 预计阅读时长:14~18 分钟 标签:LRU、哈希表、双向链表、系统设计 SEO 关键词:LRU Cache, LeetCode 146, 哈希表, 双向链表, O(1) 元描述:通过哈希表 + 双向链表实现 LRU 缓存,get/put 平均 O(1),附工程场景、常见坑与六语言实现。 A — Algorithm(题目与算法) 题目重述 设计并实现一个满足 LRU 约束的数据结构 LRUCache: LRUCache(int capacity):用正整数容量初始化 int get(int key):若 key 存在返回 value,否则返回 -1 void put(int key, int value): key 已存在:更新 value,并视作最近使用 key 不存在:插入新键值对 若超出容量:淘汰最久未使用的 key 并要求 get 和 put 平均时间复杂度为 O(1)。 示例 1(操作序列) LRUCache cache = new LRUCache(2) cache.put(1, 1) // 缓存: {1=1} cache.put(2, 2) // 缓存: {1=1, 2=2} cache.get(1) // 返回 1,且 1 变成最近使用 cache.put(3, 3) // 容量满,淘汰 key=2 cache.get(2) // 返回 -1 cache.put(4, 4) // 淘汰 key=1 cache.get(1) // 返回 -1 cache.get(3) // 返回 3 cache.get(4) // 返回 4 示例 2(更新已有键) LRUCache cache = new LRUCache(2) cache.put(1, 10) cache.put(1, 99) // 更新 value,且 1 视作最近使用 cache.get(1) // 返回 99 目标读者 正在刷 LeetCode 中等题、想吃透“数据结构组合技”的同学 做后端/中间件,需要实现或优化本地缓存的工程师 面试中经常被问到 LRU,但只记住结论、没掌握细节的人 背景 / 动机 缓存是“空间换时间”,但空间是有限的。 当缓存满了,必须淘汰一些键。LRU(Least Recently Used,最近最少使用)假设: ...

2026年2月11日 · 13 分钟 · map[name:Jeanphilo]

LeetCode 138:随机链表的复制(Copy List with Random Pointer)深拷贝全解析

副标题 / 摘要 这道题的难点不是遍历链表,而是正确复制 random 指针所形成的“跨节点引用关系”。本文从朴素思路推导到哈希映射法,讲清为什么它稳定、可维护、易工程落地。 预计阅读时长:12~16 分钟 标签:链表、深拷贝、哈希表、随机指针 SEO 关键词:LeetCode 138, Copy List with Random Pointer, 随机链表复制, 深拷贝, 哈希映射 元描述:用两趟遍历 + 映射表完成随机链表深拷贝,系统讲解正确性、复杂度、工程实践与六语言实现。 A — Algorithm(题目与算法) 题目重述 给定一个长度为 n 的链表,每个节点有: val next random(可指向任意节点或 null) 要求构造该链表的深拷贝并返回新头节点。 新链表中的任何指针都不能指向原链表节点。 输入 / 输出表示 题面常用 [val, random_index] 表示每个节点: val:节点值 random_index:random 指向的节点下标;若为空则为 null 你的函数入参只有 head,输出复制链表的头节点。 示例 1 输入: [[7,null],[13,0],[11,4],[10,2],[1,0]] 输出: [[7,null],[13,0],[11,4],[10,2],[1,0]] 解释: 输出与输入的“值与引用关系”一致,但节点是全新对象。 示例 2 输入: [[1,1],[2,1]] 输出: [[1,1],[2,1]] 解释: 第一个节点 random 指向第二个节点,第二个节点 random 指向自己。 目标读者 刷 LeetCode 时对 random 指针题目不够稳的开发者 想厘清“浅拷贝 vs 深拷贝”差异的同学 希望把算法思路迁移到工程对象复制场景的工程师 背景 / 动机 普通链表只要复制 val 和 next,逻辑很直观; 但随机链表多了一个 random 指针,它可能: ...

2026年2月11日 · 9 分钟 · map[name:Jeanphilo]

LeetCode 19:删除链表的倒数第 N 个结点(双指针一趟扫描)ACERS 全解析

副标题 / 摘要 这题的核心不是“删除节点”,而是“如何在单链表里定位倒数第 N 个节点的前驱”。本文从朴素思路推导到一趟双指针解法,用 ACERS 结构讲透正确性、边界处理与工程迁移。 预计阅读时长:12~15 分钟 适用场景标签:链表基础、双指针、面试高频 SEO 关键词:LeetCode 19, Remove Nth Node From End of List, 删除链表倒数第 N 个结点, 快慢指针, 哨兵节点 元描述(Meta Description):删除链表倒数第 N 个结点的完整 ACERS 解析:从暴力到一趟双指针,含复杂度、常见坑、工程示例与 Python/C/C++/Go/Rust/JS 代码。 A — Algorithm(题目与算法) 题目重述 给你一个链表,删除链表的倒数第 n 个结点,并返回链表的头结点。 输入输出 项目 类型 含义 head ListNode 单链表头结点 n int 倒数第 n 个位置 返回值 ListNode 删除目标节点后的头结点 示例 1 输入: head = [1,2,3,4,5], n = 2 输出: [1,2,3,5] 解释:倒数第 2 个节点是 4,删除后得到 [1,2,3,5]。 ...

2026年2月11日 · 11 分钟 · map[name:Jeanphilo]

LeetCode 2:两数相加(Add Two Numbers)链表进位从朴素到最优解

副标题 / 摘要 这题本质是把「小学竖式加法」搬到链表:同位相加、处理进位、走到末尾后可能还要补一个新节点。文章将从朴素思路推到最优单遍解法,并给出工程场景与多语言实现。 预计阅读时长:12~15 分钟 标签:链表、进位、模拟、LeetCode 2 SEO 关键词:Add Two Numbers, 两数相加, 逆序链表, 进位, LeetCode 2 元描述:用哨兵节点 + 单遍遍历在 O(max(m,n)) 时间完成两条逆序数字链表求和,附常见坑、工程应用和六语言代码。 A — Algorithm(题目与算法) 题目重述 给你两个非空链表,表示两个非负整数。 数字按逆序存储,且每个节点存储一位数字。 请将两个数相加,并返回同样逆序存储的结果链表。 题目保证除数字 0 外,这两个数都不会以 0 开头。 输入输出描述 项目 含义 输入 两个链表 l1、l2,每个节点值在 0~9 输出 一个新链表,表示 l1 + l2 的结果(逆序) 示例 1 输入: l1 = [2,4,3], l2 = [5,6,4] 解释: 342 + 465 = 807 输出: [7,0,8] 示例 2 输入: l1 = [9,9,9,9,9,9,9], l2 = [9,9,9,9] 解释: 9999999 + 9999 = 10009998 输出: [8,9,9,9,0,0,0,1] 目标读者 刚开始刷链表题,想建立稳定解题模板的同学 对「进位」和「边界处理」容易写错的中级开发者 希望把算法思维迁移到工程数据流处理的工程师 背景 / 动机 看似只是 LeetCode 入门题,但它练的能力非常实用: ...

2026年2月11日 · 12 分钟 · map[name:Jeanphilo]

先定不变量与契约,再写实现:Evans/Fowler 实战法

标题 先定不变量与契约,再写实现:Evans/Fowler 实战法 副标题 / 摘要 很多人理解“先定不变量与契约”时,会觉得只是“多写几行校验”。这篇文章给出更精确的答案:它的本质是固定责任归属,让调用方可以依赖行为语义,而不是猜测实现细节。 目标读者 正在做业务系统设计、代码评审的工程师 觉得“代码能跑,但改需求总出坑”的团队 想把 DDD/契约思想落到日常开发的人 背景 / 动机 常见开发顺序是“先把功能跑通,再补规则”。短期看速度快,长期会出现三个问题: 业务规则散落在多个 service/controller 里 调用方只能通过读实现猜行为 改一个需求会牵动大量分支判断 Evans/Fowler 这一脉的核心不是“写得更学术”,而是先明确系统必须成立的事实,再让实现为这些事实服务。 核心概念 不变量(Invariant):无论任何路径,始终为真的业务规则。 例如:已支付订单不能再次支付。 契约(Contract):对外可依赖的行为承诺,至少包含前置条件、后置条件、失败语义。 例如:cancel(order) 只接受可取消状态,成功后状态必须是 CANCELLED,否则抛明确异常。 接口 vs 契约:接口是签名,契约是语义保证。 同一个函数签名,可以有强契约,也可以完全没有契约。 契约分层(建议团队统一术语) 前面的 cancel(order) 示例主要覆盖了行为契约与失败契约。 在真实项目里,建议把契约至少拆成下面 6 类,一起设计: 数据契约:输入/输出的数据形状、类型、取值范围、单位、精度、是否可空。 例:金额必须 > 0,币种必须是 ISO 4217,时间必须是 UTC。 状态契约:状态机允许哪些迁移,不允许哪些迁移。 例:订单只能 CREATED -> PAID -> SHIPPED,不能 SHIPPED -> CREATED。 不变式契约:跨方法、跨状态始终成立的事实。 例:订单总额 = 明细金额之和 + 运费 - 优惠;库存不可为负。 行为契约:调用成功时,调用方可以依赖什么结果与语义。 例:reserve_stock() 成功后,一定返回预留记录 ID,且库存已被占用。 失败契约:违约/异常时返回什么错误、错误是否可重试、是否有副作用残留。 例:重复请求返回 409;超时返回 503 且标记 retryable=true。 副作用契约:方法会修改哪些外部状态(DB、缓存、消息、文件),顺序如何,失败如何补偿。 例:先写 DB 再写 outbox;缓存删除失败不影响主事务提交。 实践指南 / 步骤 先写目的,不写实现 明确本次功能要改变什么业务结果。 列不变量清单 逐条写出“绝对不能被破坏”的规则。 定义契约 为核心行为定义前置条件、后置条件、失败语义,并补齐数据/状态/副作用契约。 再落实现 数据库、框架、缓存、消息等实现细节后置。 用测试锁契约 测试验证的是契约,不是某一版实现细节。 可运行示例 示例 1:无契约(可运行,但语义模糊) class Order: def __init__(self, status): self.status = status def cancel(order: Order) -> Order: if order.status != "CREATED": return order order.status = "CANCELLED" return order if __name__ == "__main__": order = Order("PAID") after = cancel(order) print(after.status) 问题:失败是“静默返回”,调用方必须自己猜“这次到底算成功还是失败”。 ...

2026年2月11日 · 4 分钟 · map[name:Jeanphilo]

Hot100:排序链表(Sort List)链表归并排序 ACERS 解析

副标题 / 摘要 LeetCode 148 的核心不是“会排序”,而是“在链表结构里选对排序算法”。数组可随机访问适合快排/堆排,而单链表最匹配的是归并排序:找中点、递归分治、线性归并。 预计阅读时长:12~16 分钟 标签:Hot100、链表、归并排序、分治 SEO 关键词:Sort List, 排序链表, 链表归并排序, LeetCode 148, Hot100 元描述:用链表归并排序在 O(n log n) 内完成排序,覆盖思路推导、工程迁移、复杂度分析和多语言可运行实现。 A — Algorithm(题目与算法) 题目还原 给你链表头节点 head,请将其按升序排序并返回排序后的链表。 要求时间复杂度为 O(n log n)。 输入输出 名称 类型 描述 head ListNode 单链表头节点(可能为空) 返回 ListNode 升序排序后的头节点 示例 1 输入: 4 -> 2 -> 1 -> 3 输出: 1 -> 2 -> 3 -> 4 示例 2 输入: -1 -> 5 -> 3 -> 4 -> 0 输出: -1 -> 0 -> 3 -> 4 -> 5 目标读者 正在刷 Hot100,想把链表题模板系统化的同学 做链表题经常在“切分和拼接”环节出错的开发者 想搞清楚“为什么链表排序优先用归并”而不是快排的人 背景 / 动机 链表排序在工程里并不罕见: ...

2026年2月10日 · 8 分钟 · map[name:Jeanphilo]

Hot100:合并K个升序链表(Merge k Sorted Lists)分治归并 O(N log k) ACERS 解析

副标题 / 摘要 这题本质是“k 路归并”。如果直接一条条串行并入,性能会退化;用分治按二叉树方式两两合并,能把复杂度优化到 O(N log k)。本文按 ACERS 模板把思路推导、工程映射和多语言实现一次讲透。 预计阅读时长:12~16 分钟 标签:Hot100、链表、分治、归并 SEO 关键词:Merge k Sorted Lists, 合并K个升序链表, 分治归并, LeetCode 23, O(N log k) 元描述:从串行归并到分治归并,系统讲解 LeetCode 23 的最优复杂度解法与工程实践。 A — Algorithm(题目与算法) 题目还原 给你一个链表数组 lists,每个链表都按升序排列。 请将所有链表合并到一个升序链表中,并返回合并后的头节点。 输入输出 名称 类型 描述 lists ListNode[] k 条升序链表,元素可为空 返回 ListNode 合并后的升序链表头节点 示例 1 输入: lists = [[1,4,5],[1,3,4],[2,6]] 输出: [1,1,2,3,4,4,5,6] 示例 2 输入: lists = [] 输出: [] 目标读者 正在刷 Hot100,已经掌握 LeetCode 21 的同学 想把“双链表归并”升级为“k 路归并模板”的开发者 在服务端做多路有序流合并(日志、时间线、分片结果)的工程师 背景 / 动机 LeetCode 23 是 LeetCode 21 的自然升级版: ...

2026年2月10日 · 11 分钟 · map[name:Jeanphilo]

Hot100:K 个一组翻转链表(Reverse Nodes in k-Group)分组反转 ACERS 解析

副标题 / 摘要 LeetCode 25 是“整链反转(206)”与“区间反转(92)”的组合升级:你要按组切分、组内反转、组间拼接,并正确处理不足 k 的尾组。本文用 ACERS 模板给出工程可复用解法。 预计阅读时长:14~18 分钟 标签:Hot100、链表、分组反转、哑节点 SEO 关键词:Reverse Nodes in k-Group, K 个一组翻转链表, 分组反转, LeetCode 25, Hot100 元描述:K 组链表原地反转模板:分组扫描 + 区间反转 + 安全拼接,含复杂度分析、常见坑与多语言代码。 A — Algorithm(题目与算法) 题目还原 给你链表头节点 head 和整数 k,每 k 个节点一组进行翻转,返回修改后的链表。 若剩余节点数不足 k,则保持原顺序不变。 要求只能改节点指针,不允许只改节点值。 输入输出 名称 类型 描述 head ListNode 单链表头节点 k int 每组大小(k >= 1) 返回 ListNode 分组翻转后的新头节点 示例 1 输入: head = 1 -> 2 -> 3 -> 4 -> 5, k = 2 输出: 2 -> 1 -> 4 -> 3 -> 5 示例 2 输入: head = 1 -> 2 -> 3 -> 4 -> 5, k = 3 输出: 3 -> 2 -> 1 -> 4 -> 5 目标读者 已掌握 206 / 92,希望攻克“多区间连续反转”的 Hot100 学习者 链表题常在边界和拼接步骤出错的中级开发者 需要构建稳定“链表分段处理”模板的工程师 背景 / 动机 在工程里,链式结构的批处理并不少见: ...

2026年2月10日 · 9 分钟 · map[name:Jeanphilo]

反转链表 II(Reverse Linked List II)哑节点+头插法 ACERS 解析

副标题 / 摘要 反转链表 II 的关键不在“会反转”,而在“只反转中间一段且不破坏两端连接”。本文用 ACERS 结构讲清哑节点定位、头插法重排与边界处理,给出可复用模板与多语言代码。 预计阅读时长:12~15 分钟 标签:链表、区间反转、哑节点 SEO 关键词:Reverse Linked List II, 反转链表 II, 区间反转, 哑节点, 头插法, LeetCode 92 元描述:单链表区间反转的工程化模板:哑节点 + 头插法,O(n)/O(1),附推导、常见坑与多语言实现。 目标读者 已会 206 反转链表,想进一步掌握“局部反转”的同学 经常在链表题里卡边界(left=1、right=n)的中级开发者 希望把链表指针操作做成稳定模板的工程师 背景 / 动机 Reverse Linked List(206)是“整条反转”,而 92 要求“只反转一个闭区间”。 这类“局部重排”在工程里非常常见: 任务链中的某个分段要逆序重放 事件日志只对一段做回滚重连 数据结构需要在不重建节点的前提下原地调整 难点并非复杂算法,而是: 找准区间前驱与区间首节点 反转过程中不丢失后续链路 区间反转后把前后两端重新接回去 核心概念 哑节点(dummy):统一处理 left = 1 场景,避免头节点特判地狱 前驱指针 prev:最终停在第 left-1 个节点(若 left=1 则停在 dummy) 当前指针 cur:初始为区间首节点 prev.next 头插法(head insertion):每次把 cur 后面的一个节点摘下,插到 prev 后面 A — Algorithm(题目与算法) 题目还原 给你单链表的头节点 head 和两个整数 left、right(1 <= left <= right <= n), 请你反转从位置 left 到位置 right 的链表节点,返回反转后的链表。 ...

2026年2月10日 · 7 分钟 · map[name:Jeanphilo]