1. 问题描述:
以 Unix 风格给出一个文件的绝对路径,你需要简化它。或者换句话说,将其转换为规范路径。
在 Unix 风格的文件系统中,一个点(.)表示当前目录本身;此外,两个点 (..) 表示将目录切换到上一级(指向父目录);两者都可以是复杂相对路径的组成部分。更多信息请参阅:Linux / Unix中的绝对路径 vs 相对路径
请注意,返回的规范路径必须始终以斜杠 / 开头,并且两个目录名之间必须只有一个斜杠 /。最后一个目录名(如果存在)不能以 / 结尾。此外,规范路径必须是表示绝对路径的最短字符串。
示例 1:
输入:"/home/"
输出:"/home"
解释:注意,最后一个目录名后面没有斜杠。
示例 2:
输入:"/../"
输出:"/"
解释:从根目录向上一级是不可行的,因为根是你可以到达的最高级。
示例 3:
输入:"/home//foo/"
输出:"/home/foo"
解释:在规范路径中,多个连续斜杠需要用一个斜杠替换。
示例 4:
输入:"/a/./b/../../c/"
输出:"/c"
示例 5:
输入:"/a/../../b/../c//.//"
输出:"/c"
2. 思路分析:
① 首先根据题目可以知道可以使用模拟的方法来得到最后化简之后的路径,首先是遍历输入的字符串,并且从第二个字符开始遍历,因为一开始这个路径是从/开始的,也就是从根目录开始的,题目的关键是怎么样处理字符串.的问题,也就是涉及到当前路径与上一层路径的问题,在测试代码的过程中发现几个坑,分别如下:
1)字符串中遍历到当前的时候只存在一个点的情况,后面并且跟着的是/,这样表示的意思是是当前目录,所以忽略这个当前的这个点.,并且对循环变量进行自增,continue,比如像这样的路径:/home/.//,输出应该是/home
2)字符串中遍历到当前的时候只存在一个点的情况,但是后面跟着的是不是/,而是英文字母这个时候这个时候这个点是构成目录的一部分,不表示当前目录的意思,比如像这样的路径:/.hidden,输出应该是/.hidden
3)字符串中存在着两个点的情况,并且两个点后面是/,表示这个是需要回到上一层的目录的,并且需要注意的是在返回上一层目录的时候需要判断当前的目录是否是/根目录,假如是根目录的话那么不做任何的操作,假如不是的根目录的话对当前得到的路径进行删减,先是需要去掉/,因为在上一次的时候加入了/这个时候需要去掉,在循环中假如没有遇到/说明这个时候还是没有遇到上一层目录,这个时候需要继续循环,知道遇到了/表示返回到了上一层目录,在循环中删减掉上一层目录的字母
4)字符串中存在着两个点的情况,但是后面跟着的是.或者是其他的英文字母表示的也是目录的名字这个时候也需要通过循环将字符加入到路径中,比如像/home/...,输出应该是:/home/...
② 处理了上面复杂的点的情况之后还需要处理遇到/的情况,假如是一个/那么加入到路径中,这个时候需要使用一个变量来记录,并且通过循环删除掉多余的/,因为可能存在着多个/的情况
同时在每一次截取字符串与判断字符串的某个字符的时候需要先判断是否越界,在字符串的操作过程中很容易出现越界的问题
不过因为是模拟整个过程,所以效率不高,但是吧可以提高一下复杂情况的代码调试能力与细节处理能力,当发现错误的时候找不出来的时候使用idea的debug进行调试可以发现一下错误在哪里
③ 在看了领扣的题解之后发现真的是别人的思路太好了,使用了栈完美解决了这个问题,最核心是认识到/是分割所有目录的,这个时候假如不是上一层目录那么进栈,假如表示的是上一层目录栈非空的情况下直接弹出即可,最后判断栈是否为空假如为空表示最后回到了根目录/,假如不为空栈中的元素就是最好的有效路径,使用栈来解决非常简单明了而且不容易出现错误
3. 代码如下:
我自己写的:
import java.util.Scanner;
public class Solution {
public static String simplifyPath(String path) {
String res = "/";
/*用来记录中间结果因为可能需要回到上一层的目录但是后面发现还是使用这个变量返回上一层的时候还是出现了问题*/
String curfront = "/";
for (int i = 1; i < path.length(); ){
/*注意最后一个字符不是/*/
/*计算/的个数*/
int count = 0;
while (i < path.length() && path.charAt(i) == '/') {
/*注意上一个字符不是/才能够将其加入进来
* 应该写成: res.charAt(res.length() - 1) != '/' 下标不是i才是对的
* */
if (i >= 1 && i < path.length() - 1 && res.charAt(res.length() - 1) != '/' && count == 0) res += "/";
++i;
count++;
}
/*表明是遇到了/*/
if (count > 0) continue;
if (path.charAt(i) == '.'){
/*这段代码是为了避免: /.hidden的数据*/
if (i + 1 < path.length() && path.charAt(i + 1) != '.' && path.charAt(i + 1) != '/'){
res += "." + path.charAt(i + 1);
i = i + 2;
while (i < path.length() && path.charAt(i) != '/'){
res += path.charAt(i);
i++;
}
continue;
}
/*两个点的情况*/
if (i + 1 < path.length() && path.charAt(i + 1) == '.'){
/*两个点之后可能后面不是/的情况的标志*/
int judge = -1;
if (i + 2 < path.length() && (path.charAt(i + 2) == '.' || path.charAt(i + 2) != '/')){
res = res + ".." + path.charAt(i + 2);
/*从第三个位置开始因为*/
i = i + 3;
judge = 1;
while (i < path.length() && path.charAt(i) != '/') {
res += path.charAt(i);
++i;
}
}
/*遇到了不是上一层目录的情况.. 后面跟着的是.或者是英文字母*/
if (judge > 0) continue;
/*这里需要判断一下是否之前是/假如是那么路径没有变化还是在根目录
* */
if (res.length() == 1){
i += 2;
continue;
}
/*这里要特别处理一下返回到上一层的目录*/
res = res.substring(0, res.length() - 1);
int len = res.length();
while (len >= 1 && res.charAt(len - 1) != '/') {
res = res.substring(0, res.length() - 1);
len = res.length();
}
/*两个点并且后面是/ */
i += 2;
continue;
}
/*一个点后面跟着是/ */
++i;
continue;
}
/*下面都是返回上一层路径的操作*/
int pos = i;
String t = "";
/*注意是且在idea中debug的时候找到了错误所在*/
while (pos < path.length() && path.charAt(pos) != '.' && path.charAt(pos) != '/') {
t += path.charAt(pos);
++pos;
}
if (t.length() > 0) {
res += t;
}
i = pos;
}
/*删除最后的/*/
if (res.length() > 1 && res.charAt(res.length() - 1) == '/') res = res.substring(0, res.length() - 1);
return res;
}
}
使用栈解决的代码:
import java.util.Scanner;
import java.util.Stack;
public class Solution {
public static String simplifyPath(String path) {
String[] s = path.split("/");
Stack<String> stack = new Stack<>();
for (int i = 0; i < s.length; i++) {
if (!stack.isEmpty() && s[i].equals(".."))
stack.pop();
/*第二个判断!s[i].equals("..")是为了避免/../的数据*/
else if (!s[i].equals("") && !s[i].equals(".") && !s[i].equals(".."))
stack.push(s[i]);
}
/*栈空表示弹出了所有的元素表示回到了根目录*/
if (stack.isEmpty())
return "/";
StringBuffer res = new StringBuffer();
for (int i = 0; i < stack.size(); i++) {
res.append("/" + stack.get(i));
}
return res.toString();
}
}