Simplify Path 4ms Java Solution

来源:互联网 发布:centos 软件安装 编辑:程序博客网 时间:2024/05/07 16:48

这是leetcode中一道在线编程题,原问题链接为

https://leetcode.com/problems/simplify-path/

原问题描述很简单,如下:
Given an absolute path for a file (Unix-style), simplify it.
For example,
path = “/home/”, => “/home”
path = “/a/./b/../../c/”, => “/c”

也就是简化Unix风格的文件的绝对路径,该问题难度不大,解决方案主要用 来解决,但是这里面存在的一些细节问题,使得快速编写出正确的solution也并不是那么轻松,接下来的任务就是整理这一过程,尽量使该问题的解决方案条理清晰。

首先对问题进行分析,Unix文件的绝对路径最简单的形式是“/#/#/…. /#”,其中#代表任意不包含‘/’(‘/’是Unix风格目录的分隔符)的字符串,且该字符串不为“.”和“..”。因为“.”代表当前目录,可省略;而“..”表上级目录,可以进一步简化,即“/#/#/..”可以简化为“/#/”,这就需要用到 的性质了。此外,需要注意的一点就是绝对路径最终的形式总是要以“/”为开头,否则为无效路径。

所以,我们算法可描述如下:

  1. 首先对原始的路径字符串path以“/”为分隔符,将其分为字符串数组。分割的方法可以是遍历字符串path的每个字符并以“/”为分割标志,也可以用Java中String自带的方法split()来分割。注意这两种方法存在效率差异,后面会有说明。

  2. 然后,按顺序从左到右对字符串数组进行栈操作,可能的字符串有{“”, “.”, “..”, 其他形式}, 其中

    • 若字符串为“”或“.”,不做任何处理;
    • 若字符串为“..”,则执行出栈 操作(注意,若栈为空,不做任何操作);
    • 否则, 执行压栈 操作。
  3. 最后,就是还原最终的路径了,将栈底至栈顶的字符串从左到右填进“/#/#/…. /#”格式中的“#”中去。

算法时间的复杂度为遍历字符串path中字符的时间,为O(n),n为path的长度。空间复杂度亦是O(n),因为最终需要返回至多为n长度的简单路径。
下面贴一下我的Java代码:

public class Solution {    public String simplifyPath(String path) {        int len = path.length();        String[] stack = new String[len];        int pop = -1;        int index = 0;        while (index < len) {            if (path.charAt(index) == '/') {                continue;            }            int tail = index;            while (tail < len && path.charAt(tail) != '/')                tail++;            String sub = path.substring(index, tail);            if (sub.equals("..")){                if (pop >= 0) --pop;            } else if (!sub.equals(".")) {                stack[++pop] = sub;            }            index = tail + 1;        }        if(pop == -1) return "/";        StringBuilder simplifiedPath = new StringBuilder();        for (int i = 0; i <= pop; i++) {            simplifiedPath.append("/");            simplifiedPath.append(stack[i]);        }        return simplifiedPath.toString();    }}

上述代码没有用String自带的方法split()来分割字符串,而是通过直接遍历字符串中字符实现,在leetcode官网其完成252个测试用例用时4ms。

下边与用split()方法分割字符串对比一下性能。其代码实现如下:

public class Solution {    public String simplifyPath(String path) {        int len = path.length();        String[] stack = new String[len];        int pop = -1;        String[] subs = path.split("/");        for (int i = 0; i < subs.length; i++) {            if (subs[i].equals("") || subs[i].equals(".")) continue;            if (subs[i].equals("..")) {                if (pop >= 0) --pop;            }else                 stack[++pop] = subs[i];        }        if(pop == -1) return "/";        StringBuilder simplifiedPath = new StringBuilder();        for (int i = 0; i <= pop; i++) {            simplifiedPath.append("/");            simplifiedPath.append(stack[i]);        }        return simplifiedPath.toString();    }}

在leetcode官网其完成252个测试用例用时8ms,速度要慢上一倍。因此,从性能优化的角度来考虑,除非必要,应该尽量避免使用split,split由于支持正则表达式,所以效率会比较低,调用频率太高将会耗费大量资源。

0 0