LeetCode #71 Medium

Simplify Path

Given an absolute Unix-style file path, return its simplified canonical form.

stackstrings
Open on LeetCode ↗
02

Intuition

The tempting shortcut is to special-case anything that looks like dots, but ... is a perfectly ordinary directory name — only the exact token .. means go up, and treating triple-dot as special silently corrupts otherwise-valid paths. A path is really a sequence of directory pushes with .. as a pop, which makes a stack the natural model. Split on /, then classify each piece: empty or . is ignored, .. pops if anything is there, and anything else — however odd it looks — is pushed. Rejoin with / and prefix a slash.

How to spot this pattern

Split on / and let a stack model the directory hierarchy: a name pushes, .. pops, and . or an empty segment is noise. The stack is the resulting path, so joining it produces the canonical form directly.

03

Approach

1

Split and let empties fall out

path.split('/') on "/a//b" yields empty strings where consecutive or trailing slashes occurred. Rather than pre-cleaning the input, simply skip empty pieces during the scan — that handles every multiple-slash case at once.

2

Classify each component

"" and "." mean stay put, so skip them. ".." means go up, so pop the stack — but only if it is non-empty, since the root has no parent and /.. must stay /. Anything else is a directory name and gets pushed, including odd-looking ones like "...", which is a legal name and not a special token.

3

Rejoin

Return '/' + '/'.join(stack). An empty stack yields exactly "/", which is the correct canonical root, so no separate case is needed. The output has no trailing slash and no repeated separators. O(n) time and O(n) space.

04

Solution & live demo

1class Solution:
2 def simplifyPath(self, path):
3 st = []
4 for part in path.split('/'):
5 if part == '' or part == '.':
6 continue
7 if part == '..':
8 if st:
9 st.pop()
10 else:
11 st.append(part)
12 return '/' + '/'.join(st)
05

Common pitfalls

Popping an empty stack

✗ Wrong
if part == '..':
    st.pop()
✓ Right
if part == '..':
    if st:
        st.pop()

.. at the root has nowhere to go — /../ is just /. Popping unguarded throws on any path that tries to ascend past the root.

Not filtering empty segments

✗ Wrong
for part in path.split('/'):
    st.append(part)
✓ Right
if part == '' or part == '.':
    continue

Consecutive slashes and the leading slash produce empty strings from the split. Pushing them creates phantom directories and doubled separators in the output.

Joining without the leading slash

✗ Wrong
return '/'.join(st)
✓ Right
return '/' + '/'.join(st)

The canonical path is absolute and must begin at the root. Without the prefix an empty stack returns "" rather than "/", and every other path loses its leading separator.

06

Edge cases

"/../"

The stack is empty so the pop is skipped and the result is "/".

"/home//foo/"

Empty pieces are skipped, giving "/home/foo".

"/a/./b/../../c/"

Two pops unwind a and b, leaving "/c".

"/..."

Three dots is an ordinary directory name, not an up-level token — it is pushed.

07

Complexity

Time
O(n)
Space
O(n)
One pass over the components; the stack holds at most all of them.