Intuition
Split the path on '/' and process each piece with a stack: a real name is pushed, '..' pops the last name (go up), and '.' or empty pieces are ignored. Joining the stack with slashes gives the canonical path.
Explanation
Idea
A canonical path is just the sequence of directories you end up in after following the raw path. A stack models 'the current directory chain' perfectly: entering a directory pushes it, and .. (go to parent) pops the most recent one.
Splitting on / naturally collapses multiple slashes into empty pieces we can skip, so we don't special-case //.
Algorithm
- Empty
stack. - For each
partinpath.split('/'):''(from//or leading/trailing/) or'.'→ skip.'..'→ pop the stack if non-empty (go up a level; ignore if already at root).- otherwise → push
part(a real directory/file name).
- Return
'/' + '/'.join(stack).
Worked example: /a/./b/../../c/
Split → ['', 'a', '.', 'b', '..', '..', 'c', ''].
| part | action | stack |
|---|---|---|
| '' | skip | [] |
| a | push | [a] |
| . | skip | [a] |
| b | push | [a, b] |
| .. | pop | [a] |
| .. | pop | [] |
| c | push | [c] |
| '' | skip | [c] |
Result: /c.
Complexity
- Time: O(n) — one split plus one pass.
- Space: O(n) — the directory stack.
Implementation
import java.util.ArrayDeque;
import java.util.Deque;
import java.util.Iterator;
class Solution {
public String simplifyPath(String path) {
Deque<String> stack = new ArrayDeque<>();
for (String part : path.split("/")) {
if (part.isEmpty() || part.equals(".")) {
continue;
} else if (part.equals("..")) {
if (!stack.isEmpty()) {
stack.pop();
}
} else {
stack.push(part);
}
}
StringBuilder sb = new StringBuilder();
Iterator<String> it = stack.descendingIterator();
while (it.hasNext()) {
sb.append("/").append(it.next());
}
return sb.length() == 0 ? "/" : sb.toString();
}
}