上一章节,用栈解决了"删除相邻重复项",这一章节,继续用栈处理真实业务题。
📖 一、题目描述
LeetCode 71. 简化路径
给你一个字符串
path,表示一个 Unix 风格的绝对路径(以'/'开头),请你将其转换为 更简洁规范的路径。在 Unix 风格的文件系统中:
.表示当前目录
..表示上一级目录注意:路径中多个连续的斜杠
/视为 单个斜杠;最后返回的路径必须以/开头,且 目录之间用单个/分隔,且路径末尾 不能有斜杠(路径只有根目录/的情况除外)。
示例
示例 1:
输入:"/home/" 输出:"/home" 解释:末尾多余的斜杠要去掉
示例 2:
输入:"/../" 输出:"/" 解释:从根目录向上一级是回不去的
示例 3:
输入:"/home//foo/" 输出:"/home/foo" 解释:连续的斜杠合并为单个
示例 4:
输入:"/a/./b/../../c/" 输出:"/c"
提示
1 <= path.length <= 3000路径由英文字母、数字、
'.'、'/'或'_'组成路径保证是一个有效的 Unix 路径
🧠 二、思路讲解:为什么"栈"是这题的灵魂?
关键观察
题目其实就 3 种情况:
| 遇到的内容 | 含义 | 操作 |
|---|---|---|
""(空字符串)或 / | 没东西 / 分隔符 | 跳过 |
.(一个点) | 当前目录 | 跳过 |
..(两个点) | 返回上一级 | 弹栈(前提:栈非空) |
其他(如 home、a、b) | 真实目录名 | 压栈 |
是不是和 1047 删除相邻重复项一个套路?
没错!栈就是用来"配对/上下文消除/进退场景"问题的万能钥匙:
上一题:相邻字母相同 → 配对消除(pop) 这一题:遇到 ".." → 回退一格(pop)
栈就像"逛文件夹的足迹"
想象你在逛电脑文件夹:
-
进入
home→ 📂 栈里压入home -
进入
user→ 📂 栈里压入user -
进入
.→ ⏭️ 当前目录,原地不动 -
进入
..→ ⬆️ 回到上一级,把user弹出去 -
最终栈里留下的,就是你"真正到达的位置"。
栈就 = 你走过的路径脚印。
💻 三、代码实现
🔵 JavaScript 实现
用数组模拟栈(最常用,最清晰)
/**
* @param {string} path
* @return {string}
*/
function simplifyPath(path) {
const stack = [];
// 把路径按 '/' 切分 → 自动过滤掉多余的空字符串
for (const dir of path.split('/')) {
if (dir === '' || dir === '.') {
continue; // 空字符串 / 当前目录 → 跳过
}
if (dir === '..') {
stack.pop(); // 上一级 → 弹栈
} else {
stack.push(dir); // 正常目录名 → 入栈
}
}
return '/' + stack.join('/'); // 拼回规范路径
}
核心思路:
path.split('/')自动把空段滤掉(虽然这里我们手动continue也滤,更稳)遇到
..→ 弹栈(如果栈空,就什么都不做,对应"回不去根目录")遇到真实目录名 → 入栈
最后用
/拼接,前面补/
输入:"/a/./b/../../c/" 图文详解
文字版理解:
-
进入a文件夹,. 代表当前, 不需要写,
-
那么就是/a/b
-
../ 代表回退到上一层,目录变为 /a
-
../回退上一层,目录就是当前文件夹了
-
继续c进来,所以最后的目录是当前文件夹下的c === /c
对应数组一下解析图:

🐍 Python 实现
和 JavaScript 一样的两种思路,Python 写法更简洁。
用 list 模拟栈(最常用,最清晰)
class Solution:
def simplifyPath(self, path: str) -> str:
stack = []
for dir in path.split('/'):
# 空字符串 / '.' → 跳过
if dir in ('', '.'):
continue
# '..' → 弹栈(栈空就不弹,等价于返回根目录)
if dir == '..':
if stack:
stack.pop()
# 真实目录名 → 入栈
else:
stack.append(dir)
return '/' + '/'.join(stack)
Python 特性:
path.split('/')自动拆分,空字符串会保留
if stack:比if len(stack) > 0更 Pythonic
'/'.join(stack)拼接目录,优雅又高效核心逻辑 5 行代码,秒杀 90% 的提交
⏱️ 四、复杂度分析
时间复杂度:代码跑得有多快(看循环跑了几次)
空间复杂度:代码占了多少额外内存(除了输入,还申请了啥)
split + 数组栈
| 维度 | 结论 | 小白版解释 |
|---|---|---|
| ⏱️ 时间复杂度 | O(n) | 字符串长 n,split 切一遍 O(n),再扫一遍栈 O(n) |
| 💾 空间复杂度 | O(n) | split 拆出 O(n) 个段,stack 最坏装 O(n) 个目录 |
怎么数时间复杂度?
stack = [] # O(1)
for dir in path.split('/'): # 字符串多长,这个 for 就跑几次 → O(n)
if dir in ('','.'): continue # 判断是 O(1)
if dir == '..': stack.pop() # pop 是 O(1)
else: stack.append(dir) # append 是 O(1)
return '/' + '/'.join(stack) # join 把栈拼回去 → O(n)
总体 = O(n) + O(n) = O(n),这就是时间复杂度。
怎么数空间复杂度?
不用想复杂,只数额外申请了啥:
| 申请的东西 | 算不算额外空间 |
|---|---|
输入的字符串 path | ❌ 不算 |
split 拆出来的数组 | ✅ 算 |
自己 new 的 stack 列表 | ✅ 算 |
几个临时变量(i、start 等) | ❌ 不算(就几个) |
额外申请了一个 split 数组 + 一个 stack 列表,最坏装 n 个目录 → O(n)。
🎁 五、彩蛋:这题还能怎么拓展?
🔗 拓展题推荐
1️⃣ LeetCode 20. 有效的括号 —— 栈的入门题(上一篇已讲解) 2️⃣ LeetCode 1047. 删除相邻重复项 —— 栈的"消除"用法(上一篇已讲解) 3️⃣ LeetCode 1190. 反转每对括号间的子串 —— 栈 + 字符串构造 4️⃣ LeetCode 394. 字符串解码 —— "栈的栈"经典题 5️⃣ LeetCode 224. 基本计算器 —— 栈 + 表达式求值
核心套路: 遇到"路径 / 括号 / 嵌套 / 进退"字眼,第一反应是栈!
💼 真实业务场景
这题看似冷门,实则 大厂面试常客 + 实战必备:
-
📂 Git 操作:
git log --graph输出路径简化 -
🐳 Docker:容器路径解析
-
☁️ 云服务:对象存储的 OSS Key 处理
-
🖥️ 前端路由:Vue/React 路由匹配时简化 URL
学会了这一题,等于打通了一类问题的任督二脉。
💡 面试加分项
如果你面试时碰到这题,编编建议你:
先说思路:这题是"路径进退"问题,栈是天然解法
先写常规写法:split + 数组模拟栈,清晰易懂
提一句边界:路径可能以多个
/结尾、从根目录..出不去、栈空要保护举一反三:提一句"这类路径/进退问题都能用栈做"
📌 关注编编,下期带你:
下期,继续拆解算法 🚀

266

被折叠的 条评论
为什么被折叠?



