Simplify Path

Updated on 24 June, 2025
Simplify Path header image

Problem Statement

Given an absolute Unix-style file system path, your task is to convert this path into a simplified version according to the Unix file system's conventions. In Unix systems, file paths begin with a '/' and use '/' to separate directory names. These paths may also contain identifiers like '.' and '..' which represent the current and parent directories, respectively. Furthermore, paths may contain redundant slashes or parts which need to be technologically aligned to a more readable and standardized format, called the canonical path. The simplified path must adhere to a specific set of rules, removing unnecessary or redundant characters and interpreting directory movements correctly.

Examples

Example 1

Input:

path = "/home/"

Output:

"/home"

Explanation:

The trailing slash should be removed.

Example 2

Input:

path = "/home//foo/"

Output:

"/home/foo"

Explanation:

Multiple consecutive slashes are replaced by a single one.

Example 3

Input:

path = "/home/user/Documents/../Pictures"

Output:

"/home/user/Pictures"

Explanation:

A double period `".."` refers to the directory up a level (the parent directory).

Example 4

Input:

path = "/../"

Output:

"/"

Explanation:

Going one level up from the root directory is not possible.

Example 5

Input:

path = "/.../a/../b/c/../d/./"

Output:

"/.../b/d"

Explanation:

`"..."` is a valid name for a directory in this problem.

Constraints

  • 1 <= path.length <= 3000
  • path consists of English letters, digits, period '.', slash '/' or '_'.
  • path is a valid absolute Unix path.

Approach and Intuition

For transforming the given path into its simplified canonical form, it's essential to decode the logic behind Unix file system paths:

  1. Every sequential pair or group of slashes ('//', '///', etc.) should be treated as a single slash '/' in Unix paths, primarily meant for separating directories.
  2. Symbols like '.' represent the current directory, whereas '..' implies moving up to the parent directory. These should be handled accordingly — neglecting the former, and effectively moving up a directory for the latter.
  3. Periods not conforming to the above rules are part of valid filenames or directory names, such as '...'.
  4. A valid simplified path should start with a '/' and not end with a '/' unless it's the root directory. This requirement also means that while reconstructing the path, adequate care must be taken to add slashes only when moving from one directory to another, without trailing the path end with unnecessary slashes.

Here's how we can logically approach the solution using the examples provided:

  • Example 1 (/home/): Needs simplification by removing the trailing slash, as it's not the root.
  • Example 2 (/home//foo/): Requires reduction of multiple slashes to a single one and removal of the trailing slash.
  • Example 3 (/home/user/Documents/../Pictures): Interpreting '..' to move one directory up (from 'Documents' to 'user') ensures accurate directory traversal.
  • Example 4 (/../): Shows that moving up from the root directory keeps you at the root, thus returning '/'.
  • Example 5 (/.../a/../b/c/../d/./): Involves comprehensive parsing where '...' is a valid directory name, '..' moves up a directory, and '.' is the current directory and hence ignored.

Each step towards constructing the canonical path involves validating segments based on the specified rules, ensuring the final path is both compliant and devoid of any redundant or meaningless sequences. This methodological traversal and reconstruction provide clarity and adherence to Unix path conventions.

Solutions

  • C++
  • Java
  • C
  • JavaScript
  • Python
cpp
class Solution {
public:
    string simplifyPath(string path) {
        vector<string> pathStack;
        stringstream ss(path);
        string component;
        while (getline(ss, component, '/')) {
            if (component == "..") {
                if (!pathStack.empty()) pathStack.pop_back();
            } else if (component != "." && !component.empty()) {
                pathStack.push_back(component);
            }
        }
        string result = "";
        for (auto dir : pathStack) result += "/" + dir;
        return result.empty() ? "/" : result;
    }
};

The provided C++ code defines a function simplifyPath that processes a file path string into its simplified form, adhering to the UNIX file system standard. The function relies on effective string manipulation and the stack data structure to achieve this simplification. Here’s a breakdown of the approach used:

  • A vector named pathStack is used to simulate a stack to store the directory names.
  • A stringstream is initialized with the input path to facilitate the parsing of components separated by slashes (/).
  • A while loop is used to process each component obtained by parsing with the getline function.
  • If the current component is "..", it checks if the stack isn't empty and then pops the top element, simulating returning to the previous directory.
  • If the component is neither "." (current directory symbol which should be ignored) nor an empty string (multiple consecutive slashes), it pushes the component onto the stack.
  • After processing all components, it constructs the simplified path by concatenating all the elements from the stack, each prefixed by a slash. If the resultant string is empty, it defaults to root "/".
  • The result is then returned as the simplified path.

This procedure leverages the inherent last-in, first-out (LIFO) property of stacks, which is ideal for navigating backward or forward through the path directives effectively and efficiently.

java
class Solution {
    public String simplifyPath(String path) {
        Stack<String> pathStack = new Stack<>();
        String[] pathParts = path.split("/");

        for (String part : pathParts) {
            if (part.equals("..")) {
                if (!pathStack.isEmpty()) {
                    pathStack.pop();
                }
            } else if (!part.equals(".") && !part.isEmpty()) {
                pathStack.push(part);
            }
        }

        StringBuilder simplifiedPath = new StringBuilder();
        for (String dir : pathStack) {
            simplifiedPath.append("/").append(dir);
        }

        return simplifiedPath.length() > 0 ? simplifiedPath.toString() : "/";
    }
}

The provided Java solution simplifies a given file path using stack data structure to efficiently manage the directories. Here is a breakdown of how the solution processes the input path:

  • A Stack<String> named pathStack is initialized to store the directory names.
  • The input string path is split into parts using "/" as a delimiter, resulting in an array pathParts.

The core logic iterates through each part in pathParts:

  • If a part is "..", it checks if the stack is not empty; if true, it pops the top element from the stack. This simulates moving up one directory level.
  • If a part is "." or empty, it does nothing as "." represents the current directory and empty strings arise from consecutive slashes.
  • For any other non-empty string, it treats it as a directory name and pushes it onto the stack.

After processing all parts:

  • A StringBuilder named simplifiedPath is used to construct the result path.
  • It iterates over the Stack, prepending each directory name with "/".
  • If the StringBuilder is not empty by the end of the iteration, it returns the constructed path; otherwise, it returns "/" as the simplified path for cases where the stack is empty.

This approach ensures that the path is simplified correctly according to Unix-style filesystem rules.

c
char *reducePath(char *inputPath) {
    char *tempStack[1000];
    unsigned index = 0;
    char *section = strtok(inputPath, "/");
    while (section) {
        if (strcmp(section, "..") == 0) {
            if (index > 0) index--;
        } else if (strcmp(section, ".") != 0 && strlen(section) > 0) {
            tempStack[index] = section;
            index++;
        }
        section = strtok(NULL, "/");
    }
    char *result = (char *)malloc(3000 * sizeof(char));
    memset(result, 0, 3000 * sizeof(char));
    for (unsigned i = 0; i < index; i++) {
        strcat(result, "/");
        strcat(result, tempStack[i]);
    }
    if (strcmp(result, "") == 0)
        return "/";
    else
        return result;
}

The provided C program functions as a path reducer, simplifying an input UNIX-style file path through a series of logical operations based on path elements. The process is executed through the use of string manipulation and stack data structure principles, to achieve a canonical path.

  • The function reducePath initiates by declaring a temporary stack tempStack to store segments of the path.
  • It utilizes the strtok function to divide the input path inputPath by the delimiter / and processes each segment or section in a loop.
  • The program examines each segment:
    • If the segment equals .., it moves one level up in the directory tree. This is managed by reducing the stack index unless the stack is empty.
    • If the segment is ., or an empty string, it signifies the current directory, so these are ignored and not pushed into the stack.
    • Any other segment not matching the above conditions is pushed onto the stack.
  • After processing all parts of the path, a new string result is dynamically allocated for storing the simplified path.
  • The function concatenates each segment from the stack back together, ensuring each starts with a / to reform the path correctly.
  • Finally, if the result string remains empty after processing, the function returns / indicating the root directory. Otherwise, it returns the simplified path stored in result.

This approach ensures that unnecessary path elements are eliminated, providing a cleaner, and more direct path representing the same location in the file system effectively.

js
var canonicalPath = function (inputPath) {
    let directories = [];
    let pathSegments = inputPath.split("/");
    for (let segment of pathSegments) {
        if (segment === "..") {
            if (directories.length) {
                directories.pop();
            }
        } else if (segment === "." || segment === "") {
            continue;
        } else {
            directories.push(segment);
        }
    }
    return "/" + directories.join("/");
};

The "Simplify Path" program in JavaScript is designed to convert a complex filesystem path into its canonical form, eliminating any redundant or unnecessary elements such as "." (current directory) or ".." (parent directory). The solution implements a stack-based approach for path simplification.

Here’s how the code works:

  1. Initialize an empty array directories to serve as a stack to keep track of the path segments that contribute to the canonical path.
  2. Split the input path inputPath by "/" to obtain individual path segments.
  3. Iterate over each segment:
    • If the segment is .., pop the last element from the directories stack (if the stack is not empty), moving up one directory in the path.
    • Skip the segment if it’s "." or an empty string, as these represent the current directory or extraneous slashes respectively.
    • For segments that are actual directory names (not special characters), push the segment onto the directories stack.
  4. Join the elements in the directories stack, prefaced by a "/", to form the simplified canonical path.

The function returns the canonical path as a string, providing a simplified and navigable filesystem path. This approach effectively filters out unnecessary path components, ensuring that the output path is both concise and accurate for directory navigation.

python
class Solution:
    def canonicalPath(self, path: str) -> str:
        # Stack to hold valid path parts
        dirs = []

        # Iterate through each part split by '/'
        for part in path.split("/"):
            if part == "..":
                if dirs:
                    dirs.pop()
            elif part == "." or part == "":
                # Skip over current directory indicator or empty strings
                continue
            else:
                dirs.append(part)

        # Join parts into the final canonical path
        simplified_path = "/" + "/".join(dirs)
        return simplified_path

The Python program provided addresses the task of simplifying Unix-style file paths. To achieve a simplified or canonical path from a given complex path, the solution leverages a stack data structure (dirs) to effectively manage and process each component of the input path.

  • Initialize an empty list dirs to function as the stack for storing valid path segments.
  • Split the input string path on each slash ("/") and iterate over the resulting segments.
    • If a segment is "..", pop the last valid directory from the stack if it's not empty. This simulates moving up one directory level.
    • Ignore segments that are either the current directory indicator ("."), or empty strings resulting from consecutive slashes.
    • Any other segment is treated as a valid directory name and is pushed onto the stack.
  • After processing all segments, construct the canonical path by joining the segments in the stack, prefixed with a "/" to denote the root directory.

By pushing and popping directory names to and from the stack based on their significance (".." to move up a directory, ignoring ".", and adding valid names), the program efficiently compiles the segments into a path that represents the same location in a simplified format. This results in a string that is a canonical path devoid of unnecessary or redundant components.

Comments

No comments yet.