This problem requires designing a file system that supports creating new paths and retrieving their associated values. A Trie data structure is well-suited for this task due to its efficiency in storing and retrieving hierarchical data like file paths.
The solution uses a Trie (also known as a prefix tree) to represent the file system. Each node in the Trie represents a directory or file. The path from the root to a node represents the full path to that file or directory. Each node stores:
children
: A map (dictionary or hash table) to store child nodes (subdirectories or files). The keys are the names of the child directories/files, and the values are pointers to the child nodes.v
: An integer representing the value associated with the file (if it's a file node). For directory nodes, this value can be -1 or any other placeholder.Key Operations:
createPath(path, value)
: This function attempts to create a new path in the file system. It splits the path into its constituent parts and traverses the Trie. If any parent directory doesn't exist, it returns false
. If the path already exists, it also returns false
. Otherwise, it creates the necessary nodes and assigns the given value
to the leaf node (file) and returns true
.
get(path)
: This function retrieves the value associated with a given path. It traverses the Trie, and if the path exists, it returns the value of the leaf node. If the path doesn't exist, it returns -1
.
Time Complexity:
createPath
: O(L), where L is the length of the path string. This is because we iterate through the path components once. In the worst case, we might create O(L) new nodes.get
: O(L). We iterate through the path components to find the value.Space Complexity: O(N * L), where N is the number of files/directories created, and L is the average length of a path. This is because the Trie stores all the paths in the file system.
class TrieNode:
def __init__(self):
self.children = {} # Dictionary to store child nodes
self.value = -1 # Value associated with the node (file)
class FileSystem:
def __init__(self):
self.root = TrieNode()
def createPath(self, path: str, value: int) -> bool:
nodes = path.split('/')[1:] # Split the path into components
curr = self.root
for i, node_name in enumerate(nodes):
if node_name not in curr.children:
if i == len(nodes) - 1: # Create file node
curr.children[node_name] = TrieNode()
curr.children[node_name].value = value
return True
else: # Create directory node
curr.children[node_name] = TrieNode()
curr = curr.children[node_name]
else:
if i == len(nodes) - 1: # Path already exists
return False
else:
curr = curr.children[node_name]
return True # Should not reach here normally
def get(self, path: str) -> int:
nodes = path.split('/')[1:]
curr = self.root
for node_name in nodes:
if node_name not in curr.children:
return -1
curr = curr.children[node_name]
return curr.value
The code in other languages (Java, C++, Go, TypeScript) follows a similar structure, adapting the specific data structures for each language (e.g., HashMap
in Java, unordered_map
in C++, map
in Go). The core Trie logic remains the same.