Simplify Path 4ms Java Solution
這是leetcode中一道線上程式設計題,原問題連結為
原問題描述很簡單,如下:
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風格目錄的分隔符)的字串,且該字串不為“.”和“..”。因為“.”代表當前目錄,可省略;而“..”表上級目錄,可以進一步簡化,即“/#/#/..”可以簡化為“/#/”,這就需要用到棧
所以,我們演算法可描述如下:
首先對原始的路徑字串path以“/”為分隔符,將其分為字串陣列。分割的方法可以是遍歷字串path的每個字元並以“/”為分割標誌,也可以用Java中String自帶的方法split()來分割。注意這兩種方法存在效率差異,後面會有說明。
然後,按順序從左到右對字串陣列進行棧操作,可能的字串有{“”, “.”, “..”, 其他形式}, 其中
- 若字串為“”或“.”,不做任何處理;
- 若字串為“..”,則執行出棧 操作(注意,若棧為空,不做任何操作);
- 否則, 執行壓棧 操作。
最後,就是還原最終的路徑了,將棧底至棧頂的字串從左到右填進“/#/#/…. /#”格式中的“#”中去。
演算法時間的複雜度為遍歷字串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由於支援正則表示式,所以效率會比較低,呼叫頻率太高將會耗費大量資源。