本系列使用IDEA+LEETCODE EDITOR插件,题目描述统一英文。
题目链接
一、题目描述:
//Given a string path, which is an absolute path (starting with a slash '/') to
//a file or directory in a Unix-style file system, convert it to the simplified ca
//nonical path.
//
// In a Unix-style file system, a period '.' refers to the current directory, a
//double period '..' refers to the directory up a level, and any multiple consecut
//ive slashes (i.e. '//') are treated as a single slash '/'. For this problem, any
// other format of periods such as '...' are treated as file/directory names.
//
// The canonical path should have the following format:
//
//
// The path starts with a single slash '/'.
// Any two directories are separated by a single slash '/'.
// The path does not end with a trailing '/'.
// The path only contains the directories on the path from the root directory to
// the target file or directory (i.e., no period '.' or double period '..')
//
//
// Return the simplified canonical path.
//
//
// Example 1:
//
//
//Input: path = "/home/"
//Output: "/home"
//Explanation: Note that there is no trailing slash after the last directory nam
//e.
//
//
// Example 2:
//
//
//Input: path = "/../"
//Output: "/"
//Explanation: Going one level up from the root directory is a no-op, as the roo
//t level is the highest level you can go.
//
//
// Example 3:
//
//
//Input: path = "/home//foo/"
//Output: "/home/foo"
//Explanation: In the canonical path, multiple consecutive slashes are replaced
//by a single one.
//
//
// Example 4:
//
//
//Input: path = "/a/./b/../../c/"
//Output: "/c"
//
//
//
// Constraints:
//
//
// 1 <= path.length <= 3000
// path consists of English letters, digits, period '.', slash '/' or '_'.
// path is a valid absolute Unix path.
二、思路分析:
首先做这道题先要了解题意中的路径知识,UNIX系统中路径可以用一行命令遍历一个文件的上下级和同级。上级..,同级.,下级/,多个//认为是一个。要求输出最后的结果,注意最后的结果不能为空,至少为/。
看起来还可以,但是别忘了这是道M的题,虽然也是比较容易的,但是考的是你的思维模式。你是傻傻的暴力解,线性思维,还是优雅的打破思维定式,都会被面试官看在眼里,所以如何从线性思维脱离出来,这个才是这期的重点内容。
三、AC 代码:
首先展示下未优化版,可以AC,但是代码行数多,判断条件多,通常当你代码写到这种时候,你自己就会发现容易出BUG,不好DEBUG,那么这多数情况下意味着,你的思路出了问题,过于线性,看不到近路/捷径。切记行数和逻辑判断的数目可以很大程度上反应代码的优劣
线性思维的思路很简单,就是一个个字符读取,和已读取的一起判断,遇到/就判断是要放弃(同级),出栈(去到上级,出栈前务必判空)还是入栈。
class Solution {
public String simplifyPath(String path) {
// 题目描述中已经说了是有效路径,省去了异常判断的麻烦
// 选择数据结构,这种题类似计算器,有效的括号等,使用栈即可
Stack<String> stack = new Stack<>();
// 每次要入栈的内容
String insert = "";
for (int i = 0; i < path.length(); i++) {
// 只需要判断当前字符串是哪种可能,
if (path.charAt(i) == '/') {
// 连续的/无效,只保存一个
if ("/".equals(insert)) {
continue;
}
//如果之前没有内容,加入
if ("".equals(insert)) {
insert += "/";
continue;
}
//如果之前存储了2个点了
if ("/..".equals(insert)) {
// pop前记得检查
if(!stack.isEmpty()){
stack.pop();
}
} else if ("/.".equals(insert)) {
} else {
// 判断之前要入栈的值是否有效,有效则可以入栈了
stack.add(insert);
// 清空之前的要入栈的值,切记此处要加上此时的/
}
insert = "/";
} else if (path.charAt(i) == '.') {
insert += ".";
} else {
// 正常拼接即可,无需处理
insert += path.charAt(i);
}
}
//处理最后一个
if (!"".equals(insert) && !"/".equals(insert)) {
if("/.".equals(insert)){
}else if("/..".equals(insert)){
// pop前记得检查
if(!stack.isEmpty()){
stack.pop();
}
}else{
stack.add(insert);
}
}
StringBuilder sb = new StringBuilder();
while (!stack.isEmpty()) {
sb.insert(0, stack.pop());
}
// 特殊情况处理,如果栈里没有值返回根目录/而不是空
return "".equals(sb.toString()) ? "/" : sb.toString();
}
}
此时执行结果:
解答成功:
执行耗时:12 ms,击败了11.08% 的Java用户
内存消耗:38.5 MB,击败了59.09% 的Java用户
并不令人满意,现在思考如何优化,打破上文一个个字符串判断的线性思维。 思考一下,我们真的需要一个个字符去读取么?我们可以看到规律,要处理的只是2个//之间的内容,比如/../,/A/B/。那么何必一个个判断直接split岂不是就可以取到了? 取到之后的逻辑就跟上面一样判断3种场景即可,AC代码
public String simplifyPath(String path) {
String[] pathItems = path.split("/");
Stack<String> stack = new Stack<>();
for (String pathItem : pathItems) {
if ("".equals(pathItem) || ".".equals(pathItem)) {
continue;
}
if ("..".equals(pathItem)) {
// pop前记得检查
if (!stack.isEmpty()) {
stack.pop();
}
continue;
}
stack.add("/" + pathItem);
}
StringBuilder sb = new StringBuilder();
while (!stack.isEmpty()) {
sb.insert(0, stack.pop());
}
// 特殊情况处理,如果栈里没有值返回根目录/而不是空
return "".equals(sb.toString()) ? "/" : sb.toString();
}
解答成功:
执行耗时:6 ms,击败了50.25% 的Java用户
内存消耗:38.4 MB,击败了77.40% 的Java用户
四、总结:
- 代码行数和逻辑判断的次数是衡量一个代码优劣的重要标准。
- 打破线性思维看本质。
- 优雅的代码不应该大量逻辑嵌套,逻辑嵌套处可改写成continue/return的方式优化代码。
本文正在参与「掘金 2021 春招闯关活动」, 点击查看 活动详情