When we kill a process, all its descendants must also be killed. This is naturally a tree traversal problem. The brute force approach scans through all processes for each recursive call to find children of the current process. While this works, it repeatedly searches the entire list, making it inefficient for large inputs.
ppid array to find all children of the current process.Where is the length of the
pidandppid.
Instead of repeatedly scanning arrays, we can build an actual tree structure upfront. By creating node objects with parent-child relationships, we transform the implicit tree (represented by parallel arrays) into an explicit one. Once built, collecting all descendants becomes a simple tree traversal.
Node object for each process ID and store them in a hash map.ppid array to establish parent-child relationships.class Node:
def __init__(self, val):
self.val = val
self.children = []
class Solution:
def killProcess(self, pid: List[int], ppid: List[int], kill: int) -> List[int]:
mp = {}
for id in pid:
mp[id] = Node(id)
for i in range(len(ppid)):
if ppid[i] > 0:
mp[ppid[i]].children.append(mp[pid[i]])
result = [kill]
self.getAllChildren(mp[kill], result)
return result
def getAllChildren(self, node, result):
for child in node.children:
result.append(child.val)
self.getAllChildren(child, result)Where is the length of the
pidandppid.
We can simplify the tree simulation by using a hash map that directly maps each parent to its list of children. This avoids creating node objects while still giving us O(1) access to any process's children. The DFS traversal then becomes straightforward.
kill process and add it to the result.class Solution:
def killProcess(self, pid: List[int], ppid: List[int], kill: int) -> List[int]:
map_dict = {}
for i in range(len(ppid)):
if ppid[i] > 0:
if ppid[i] not in map_dict:
map_dict[ppid[i]] = []
map_dict[ppid[i]].append(pid[i])
result = [kill]
self.getAllChildren(map_dict, result, kill)
return result
def getAllChildren(self, map_dict, result, kill):
if kill in map_dict:
for child_id in map_dict[kill]:
result.append(child_id)
self.getAllChildren(map_dict, result, child_id)Where is the length of the
pidandppid.
BFS offers an alternative to DFS for traversing the process tree. Instead of going deep into one branch before backtracking, BFS processes all children at the current level before moving to the next. Both approaches yield the same result but BFS can be more intuitive when thinking about the spread of the kill signal.
kill process.class Solution:
def killProcess(self, pid: List[int], ppid: List[int], kill: int) -> List[int]:
map_dict = {}
for i in range(len(ppid)):
if ppid[i] > 0:
if ppid[i] not in map_dict:
map_dict[ppid[i]] = []
map_dict[ppid[i]].append(pid[i])
queue = deque([kill])
result = []
while queue:
r = queue.popleft()
result.append(r)
if r in map_dict:
for child_id in map_dict[r]:
queue.append(child_id)
return resultWhere is the length of the
pidandppid.
A common mistake is only collecting the children of the process to kill, but forgetting to include the kill process itself in the result. The killed process must be added to the result list before traversing its descendants.
When building the parent-to-children map, some processes may have no children (leaf processes). Failing to check if a key exists in the map before accessing its children can cause null pointer exceptions or key errors. Always verify the map contains the key before iterating over children.
The brute force approach scans the entire ppid array for each recursive call, leading to O(n^2) complexity. This becomes a problem for large inputs. Building a hash map upfront to store parent-child relationships reduces this to O(n) by providing O(1) child lookups.