71. 简化路径

中等 含交互动画

学完这道你应该能: 说清它为什么用「栈」,而不是死记步骤; 合上页面,自己默写出核心代码; 用 30 秒把思路讲清楚。

AI 私教 · 就着本题动画讲
卡住了不用硬扛,按 8 步把这题真正走通。
从读题、试解、找法到手写代码,边问边打勾,适合第一次系统学算法的同学。

题目描述

化简 Unix 绝对路径:. 表示当前目录、.. 表示上一级、多个斜杠合并、结尾不留斜杠。

path = "/a/./b/../c"
输出 = "/a/c"

思路解析

直觉是「找到 a/.. 这种模式就把两段一起抹掉」,但删完可能又冒出新的可消模式,要反复扫;连续斜杠、结尾斜杠、根目录的 .. 还要各种特判。难点是回退要精确作用到"最近进入的那一层",纯字符串替换抓不住这个"最近"。

转折:目录是「进一层 / 退一层」的层级结构,而栈天生管层级——栈顶永远是「最近进入的目录」。先按 / 切成几段:普通目录名就入栈(深入一层);遇 .. 就弹出栈顶(退回最近那层);遇 . 或空串就忽略。.. 该退谁?正是栈顶,所以不用满世界找。

准备 · 按 / 切分:"/a/./b/../c" 按 / 切开,得到上面这串段:a、.、b、..、c(开头的空段已忽略)。下面是空栈,逐段处理。

读 "a" · 入栈:第一段是普通目录 a,深入一层——入栈。栈:[a]。

读 "." · 忽略:读到 .,表示「当前目录」,原地不动,啥也不做。栈仍是 [a]。

读 "b" · 入栈:普通目录 b,再深入一层——入栈。栈:[a, b],栈顶是 b(最近进入的)。

读 ".." · 先看栈顶:读到 ..,要退回上一级。先确认栈非空:栈顶 b 正是刚进入的最近一层,退回上级就是把它弹掉

读 ".." · 弹出 b:弹出栈顶 b,退回到 a。栈变回 [a]。这一步演示了 .. 为什么只动栈顶——它精确退掉「最近进入的那层」。

读 "c" · 入栈:普通目录 c,入栈。栈:[a, c]。注意刚才的 .. 只退掉了 b,没动到 a——这就是为什么结果是 a、c 两层。

拼接结果:所有段处理完,把栈里 [a, c] 用 / 连起来、前面补根 /,得 "/a/c"

反例 · 空栈遇 "..":负例提醒:如果路径开头就是 "/.."(栈还空着),.. 没有上一级可退——根目录的上一级还是根。这时必须直接跳过,不能 pop 空栈,否则报错。

凡是「进入一层 / 退回一层」的结构(目录、括号、撤销操作),栈都是最自然的工具:入栈深入,弹栈回退,栈顶永远是「最近那一层」。

参考代码(Python)

Python
1stack = []
2for part in path.split(class="cl-str">"/"):          # 按 / 切开
3    if part == class="cl-str">"" or part == class="cl-str">".":     # 空段 / 当前目录 → 跳过
4        continue
5    elif part == class="cl-str">"..":                # 回上级
6        if stack: stack.pop()         # 栈空就别弹
7    else:
8        stack.append(part)            # 普通目录入栈
9return class="cl-str">"/" + class="cl-str">"/".join(stack)

复杂度分析

  • 时间复杂度:O(n) —— n 为路径长度,切分加每段一次进出栈
  • 空间复杂度:O(n) —— 栈最多存下所有目录段

套路模板

记住骨架:切分 → 忽略项跳过 / 回退项弹栈(空栈不弹) / 其余入栈 → 拼接。括号匹配、撤销重做都是同一类栈应用。

Python
1stack = []
2for token in 切分(input):
3    if 忽略(token): continue              # 如 class="cl-str">"" / class="cl-str">"."
4    elif 回退(token):                     # 如 class="cl-str">".."
5        if stack: stack.pop()             # 空栈别弹
6    else: stack.append(token)             # 深入一层
7return 拼接(stack)

易错点

  • 错误写法:遇 ".." 不判栈空就 stack.pop()正确写法:if stack: stack.pop()(像 "/.." 开头时栈是空的,pop 空栈会抛 IndexError;根的上一级仍是根)
  • 错误写法:忘了跳过连续 // 产生的空段正确写法:空串 "" 也要 continue(split("/") 对 "//a" 会切出空字符串,不跳过会把它当目录入栈)

以上文字与上方动画完全同一思路:先看动画建立直觉 → 读文字巩固 → 关掉页面自己默写一遍代码,能写出来才算真的掌握

下一题 →120. 三角形最小路径和 ← 返回题库