- 每日一题:LeetCode:71.简化路径
- 时间:2022-01-06
- 力扣难度:Medium
- 个人难度:Easy
- 数据结构:字符串、栈
- 算法:模拟、双指针
2022-01-06:LeetCode:71.简化路径
1. 题目描述
题目:原题链接
- 给你一个字符串 path ,表示指向某一文件或目录的 Unix 风格的绝对路径 ,以
/
开头,请你将其转化为更加简洁的规范路径 - 在 Unix 风格的文件系统中,一个点
.
表示当前目录本身 - 此外,两个点
..
表示将目录切换到上一级即父目录;两者都可以是复杂相对路径的组成部分 - 任意多个连续的斜杠,如:
//
都被视为单个斜杠/
, 对于此问题,任何其他格式的点(例如,...
)均被视为文件或目录名称 - 请注意,返回的规范路径必须遵循下述格式
- 始终以斜杠
/
开头 - 两个目录名之间必须只有一个斜杠
/
- 最后一个目录名(如果存在)不能 以
/
结尾。 - 此外,路径仅包含从根目录到目标文件或目录的路径上的目录,即不含
.
或..
- 始终以斜杠
- 返回简化后得到的 规范路径
- 给你一个字符串 path ,表示指向某一文件或目录的 Unix 风格的绝对路径 ,以
输入输出规范
- 输入:字符串path
- 输出:简化后的字符串
输入输出示例
- 输入:”/../I/./love//996/../you/“
- 输出:”/I/love/you”
- 输入:”/a/./b/../../c/“
- 输出:”/c”
2. 方法一:字符串模拟+栈
思路
- 本题首先可以根据题目对简化后字符串的要求入手,遍历整个字符串,对
/
、.
、..
等分别进行处理 - 最终将满足的子目录路径拼接到
StringBuilder
中 - 注意对于
..
切换到父目录时,需要判断是否到了根目录,已到根目录的话输出/
- 本题首先可以根据题目对简化后字符串的要求入手,遍历整个字符串,对
复杂度分析:n是path字符串的长度
- 时间复杂度:$O(n)$,遍历了整个path字符串,且最后输出时也遍历了存放子目录的List
- 空间复杂度:$O(n)$,使用了一个List集合存放子路径
题解
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33public String simplifyPath(String path) {
if (path == null || path.length() == 0 || path.charAt(0) != '/') return null;
List<String> pathList = new ArrayList<>();
int n = path.length();
for (int i = 0; i < n; ) {
if (path.charAt(i) == '/') { // 处理 [//]
i++;
} else {
// 记录当前位置
int index = i;
while (i < n && path.charAt(i) != '/') {
i++;
}
// 截取一个子目录
String subPath = path.substring(index, i);
// 处理 [..]
if ("..".equals(subPath)) {
if(!pathList.isEmpty()) {
pathList.remove(pathList.size() - 1);
}
// 处理 [.] [//]
}else if (!".".equals(subPath) && !"".equals(subPath))
// 存入子目录
pathList.add(subPath);
}
}
StringBuilder sb = new StringBuilder();
if (!pathList.isEmpty()) {
for(String subPath : pathList)
sb.append("/").append(subPath);
}
return "".equals(sb.toString()) ? "/" : sb.toString();
}
3. 方法二:字符串分割+栈
思路
- 和方法一类似,除了使用List集合,也可以使用栈结构来存放子目录
- 使用字符串分割方法
split()
,无需遍历整个path字符串,只需要遍历分割后的字符串数组即可 - 由于栈是先进后出(FILO)的,所以使用双端队列,如
LinkedList
,从而方便在最后拼接简化后字符串时的操作
复杂度分析:n是path字符串的长度
- 时间复杂度:$O(n)$,分割方法
split()
实际上也是遍历了整个path字符串 - 空间复杂度:$O(n)$,使用了一个栈结构存放子路径
- 时间复杂度:$O(n)$,分割方法
题解
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19public String simplifyPath(String path) {
if (path == null || path.length() == 0 || path.charAt(0) != '/') return null;
Deque<String> stack = new LinkedList<>();
String[] strs = path.split("/");
for (String str : strs) {
if ("..".equals(str)) {
if (!stack.isEmpty()) {
stack.removeLast();
}
} else if (!".".equals(str) && !"".equals(str)) {
stack.offerLast(str);
}
}
StringBuilder sb = new StringBuilder();
while (!stack.isEmpty()) {
sb.append("/").append(stack.removeFirst());
}
return "".equals(sb.toString()) ? "/" : sb.toString();
}思考与优化
- 思考:如何直接在一个循环中完成对简化后字符串的拼接,而不是将所有子目录保存到一个栈或者集合中,最后在遍历集合来拼接字符串
- 优化:使用两个指针
start
、end
来记录每次拼接的字符串的起始和结尾位置,如果后面遇到了..
,则根据指针来删除字符串的内容 - 由于可能存在多个
..
,所以两个指针start
、end
也分别需要表示为集合的类型
题解:优化版本
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27public String simplifyPath(String path) {
if (path == null || path.length() == 0 || path.charAt(0) != '/') return null;
StringBuilder sb = new StringBuilder();
// 起始和结尾指针
Stack<Integer> start = new Stack<>();
Stack<Integer> end = new Stack<>();
// 初始化
start.push(0);
end.push(1);
String[] strs = path.split("/");
for (String str : strs) {
// 处理 [..]
if ("..".equals(str)) {
if (!start.isEmpty()) {
sb.delete(start.pop(), end.pop());
} else {
sb.delete(0, 1);
}
// 处理 [.] [//]
} else if (!".".equals(str) && !"".equals(str)) {
start.push(sb.length());
sb.append("/").append(str);
end.push(sb.length());
}
}
return "".equals(sb.toString()) ? "/" : sb.toString();
}
发布时间: 2022-01-06 18:56:24
最后更新: 2022-01-21 23:09:05
本文标题: 每日一题:LeetCode:71.简化路径
本文链接: https://chthollists.github.io/post/4e408b73.html
版权声明: 本作品采用 CC BY-NC-SA 4.0 许可协议进行许可。转载请注明作者和出处!
