71. 简化路径
✨核心逻辑
本题采用 栈(双端队列实现)与字符串分割 的策略:
- 路径分割:利用 Unix 路径以
/为分隔符的特性,使用split("/")将路径字符串切割成若干个字符串组件。 - 栈结构维护:利用栈(或双端队列模拟栈)来维护路径层级。
- 遍历组件并处理:
- 遇到
"."或者空字符串(由连续斜杠产生),说明是当前目录或分隔符,不做任何处理,直接跳过。 - 遇到
"..",说明要返回上一级目录。如果栈不为空,则弹出栈顶元素(丢弃上一级目录)。 - 遇到其他正常目录名,将其压入栈中。
- 遇到
- 拼接结果:最后如果栈为空,说明路径在根目录,直接返回
"/";如果不为空,则从栈底到栈顶依次弹出元素,拼接上/形成规范的简化路径。
🔥代码实现(含详细变量注释)
class Solution {
public String simplifyPath(String path) {
// stack:使用双端队列 ArrayDeque 作为一个栈来保存有效路径的目录名。
// ArrayDeque 比 Stack 类性能更好,且支持 FIFO 和 LIFO 操作。
Deque<String> stack = new ArrayDeque<>();
// split:将原路径按照 "/" 进行分割,分割后数组可能包含空字符串、"."、".." 和真实的目录名
String[] split = path.split("/");
// st:遍历数组中的每一个拆分出来的字符串组件
for (String st : split) {
// 如果遇到的是 "." 或者是空字符串(由多个连续斜杠 "/" 产生),则忽略,直接进入下一次循环
if (st.equals(".") || st.equals("")) {
continue;
}
// 如果遇到的是 "..",代表返回上一级目录
if (st.equals("..")) {
if (!stack.isEmpty()) {
// 出栈操作:丢弃栈顶的元素(即上一级目录)
stack.pollLast();
}
} else {
// 如果不是 "." 和 "..",说明是合法的目录名,将其压入栈中
stack.offerLast(st);
}
}
// 额外判断:如果遍历完成后栈是空的,说明路径被简化到了根目录,直接返回 "/"
if (stack.isEmpty()) {
return "/";
}
// sb:用于构建最终的规范路径字符串
StringBuilder sb = new StringBuilder();
// 从队列头部依次取出元素(保证了目录本身从根到叶子的正确顺序),前缀加上斜杠拼成规范路径
while (!stack.isEmpty()) {
sb.append("/").append(stack.pollFirst());
}
// 返回最终拼接好的简化路径字符串
return sb.toString();
}
}
- ⏱️复杂度分析
时间复杂度:O(N),其中 N 是字符串 path 的长度。分割字符串需要遍历一次,处理分割后的数组也需要遍历一次,均为线性时间。
空间复杂度:O(N),主要消耗在分割出的字符串数组 split 和用于存储目录名的双端队列 stack 上,最坏情况下路径中全是单级目录,栈的大小接近 N。
🔍总结一下:
目录的层级结构天然具有“先进后出”的特点(进入子目录 => 压栈,返回上级目录 => 弹栈),所以用栈来存储路径中的有效目录名
需要注意栈为空的情况,也就是没有有效路径,返回""