华为 OD 训练营 · 题解精讲
LC71. 简化路径
题目描述
给你一个字符串 path ,表示指向某一文件或目录的 Unix 风格 绝对路径 (以 '/' 开头),请你将其转化为更加简洁的规范路径。 在 Unix 风格的文件系统中,一个点(.)表示当前目录本身;此外,两个点 (..) 表示将目录切换到上一级(指向父目录);两者都可以是复杂相对路径的组成部分。任意多个连续的斜杠(即,'//')都被视为单个斜杠 '/' 。 对于此问题,任何其他格式的点(例如,'...')均被视为文件/目录名称。 请注意,返回的 规范路径 必须遵循下述格式: 始终以斜杠 '/' 开头。 两个目录名之间必须只有一个斜杠 '/' 。 最后一个目录名(如果存在)不能 以 '/' 结尾。 此外,路径仅包含从根目录到目标文件或目录的路径上的目录(即,不含 '.' 或 '..')。 返回简化后得到的 规范路径 。 示例 1: 输入:path = "/home/" 输出:"/home" 解释:注意,最后一个目录名后面没有斜杠。 示例 2: 输入:path = "/../" 输出:"/" 解释:从根目录向上一级是不可行的,因为根目录是你可以到达的最高级。 示例 3: 输入:path = "/home//foo/" 输出:"/home/foo" 解释:在规范路径中,多个连续斜杠需要用一个斜杠替换。 示例 4: 输入:path = "/a/./b/../../c/" 输出:"/c" 提示: 1 <= path.length <= 3000 path 由英文字母,数字,'.','/' 或 '_' 组成。 path 是一个有效的 Unix 风格绝对路径。
题目解析
题目在说什么
这道题给你一个 Unix 风格的绝对路径(比如 /home//foo/./bar/../),要你把它简化成最简洁的规范路径。简化规则很简单:
- 多个斜杠
//变成一个斜杠/ - 一个点
.表示当前目录,直接忽略 - 两个点
..表示回到上一级目录,如果已经在根目录就停在根目录 - 其他东西(比如
...或目录名)就是正常的目录名 - 最后结果要以
/开头,目录之间只有一个/,结尾不能有/
比如:
/home/→/home(去掉结尾斜杠)/../→/(根目录不能往上走)/home//foo/→/home/foo(合并斜杠)/a/./b/../../c/→/c(.忽略,..回退,最后只剩c)
思路是怎么来的
想象你在一棵文件树里,从根目录开始,根据路径一步步走。路径里的每个部分就是一个指令:
- 普通目录名(比如
home、foo):往前走一步,进入这个目录 .:站在原地不动..:往回退一步,回到上一级目录- 多个斜杠:相当于多个分隔符,没实际意义
所以我们可以用一个“栈”来模拟这个过程。栈就像一叠盘子,你只能从最上面拿或放。每进入一个目录,就把目录名放到栈顶;遇到 ..,就从栈顶拿走一个目录名(回退);遇到 . 或空字符串,什么都不做。
这样,最后栈里从底到顶的顺序,就是简化后的路径目录顺序。然后我们用 / 把它们连起来,前面再加一个 / 表示根目录,就得到答案了。
为什么要用栈?因为 .. 需要回退到上一级,而上一级正好是最近进入的目录,也就是栈顶。栈的“后进先出”特性完美匹配这个需求。
代码逐步拆解
我们来看参考代码,一行一行解释:
public String simplifyPath(String path) {
String[] names = path.split("/");这行在干嘛:把原始路径按 / 切分成一个个小片段。比如 "/home//foo/" 会变成 ["", "home", "", "foo", ""]。注意空字符串是因为连续斜杠或开头结尾的斜杠导致的。
Deque<String> stack = new ArrayDeque<>();这行在干嘛:创建一个双端队列(这里当作栈用)。ArrayDeque 是 Java 里一个高效的栈实现。我们用它来存放路径中的目录名。
for (String name : names) {
if (name.equals("..")) {
if (!stack.isEmpty()) {
stack.pollLast();
}
} else if (!name.isEmpty() && !name.equals(".")) {
stack.offerLast(name);
}
}这个循环在干嘛:遍历每个片段,根据规则处理:
- 如果片段是
"..":表示要回退。只要栈不为空,就从栈顶(pollLast取出最后一个元素)弹出一个目录名。如果栈已经空了(已经在根目录),就什么都不做。 - 如果片段不是空字符串,也不是
".":说明它是一个正常的目录名(比如home、foo、...),就把它放到栈顶(offerLast添加到末尾)。 - 其他情况(空字符串或
"."):直接忽略,不做任何操作。
注意:pollLast 和 offerLast 都是在队列的尾部操作,这样队列的尾部就是栈顶,头部是栈底。我们也可以用 push 和 pop,但这里用 Deque 的 Last 方法更直观。
StringBuilder result = new StringBuilder("/");
Iterator<String> iterator = stack.iterator();
while (iterator.hasNext()) {
result.append(iterator.next());
if (iterator.hasNext()) {