LeetCode #71 Medium

Simplify Path

Simplify Path: given an absolute Unix-style file path, return its simplified canonical form.

Constraints
  • 1 <= path.length <= 3000
  • path consists of English letters, digits, period '.', slash '/' or '_'.
  • path is a valid absolute Unix path.
stackstrings
Open on LeetCode ↗
02

Intuition

Simplify path converts a Unix-style absolute path into its canonical form, resolving . and .. and collapsing redundant slashes. The structure of the problem is in .., which cancels the directory before it. That cancellation — most recent first — is exactly a stack: - Split on slashes, push real directory names, and pop on ... Splitting on / produces empty strings for consecutive slashes and for the leading slash. Those, along with ., are simply skipped, which handles // and /./ without any special logic. The case that breaks implementations is .. at the root. /../ stays at / rather than failing, so popping an empty stack must be a no-op rather than an error. Guarding that pop is essential. Directory names may contain dots — ... and ..hidden are valid names, not navigation. Only exactly . and exactly .. are special, so the comparison must be for equality, not for a leading dot. Building the result joins the stack with / and prepends one, giving /a/b/c. An empty stack yields / alone, which is the correct canonical form for the root. The result never has a trailing slash, so joining must not append one — a detail easy to get wrong when constructing the string manually rather than with a join. Splitting is O(n) and each component is pushed and popped at most once, giving O(n) time and O(n) space.

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

Try it first

Before reading on: price up what the direct approach costs here, then ask what unresolved thing you are holding, and what event finally resolves it. Aim for O(n) time and O(n) space.

1

Recognise the cancellation

.. cancels the most recent directory, which is precisely a stack's behaviour. Everything else in the problem is parsing.

2

Split and skip the noise

Split on /. Empty strings from consecutive slashes and . are simply skipped, which handles // and /./ with no special logic.

3

Push real directory names

Anything that is not empty, ., or .. is a directory name and is pushed. The stack holds the resolved path so far.

4

Guard the pop at root

/../ stays at / rather than failing, so popping an empty stack must be a no-op. This is the case that breaks most implementations.

5

Compare for exact equality

... and ..hidden are valid directory names, not navigation. Test for exactly . and exactly .., never for a leading dot.

6

Join without a trailing slash

Prepend / and join the stack with /. An empty stack yields / alone, and the result never ends in a slash.

7

Cost of the approach

Splitting is O(n) and each component is pushed and popped at most once, giving 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.