This problem involves traversing a tree structure representing process relationships to identify all processes that need to be killed when a specific process is terminated. The input consists of two arrays: pid
(process IDs) and ppid
(parent process IDs), and the ID of the process to be killed (kill
).
The most efficient way to solve this is using Depth-First Search (DFS). DFS systematically explores a tree by going as deep as possible along each branch before backtracking.
Graph Construction: First, we build an adjacency list representation of the process tree. The adjacency list g
maps each parent process ID to a list of its child process IDs. This is done by iterating through pid
and ppid
, associating each process in pid
with its parent in ppid
.
Depth-First Traversal: We initiate a DFS function (dfs
) starting from the kill
process. The dfs
function does the following:
i
) to the result list (ans
).j
) of the current process. This ensures that all descendants of the kill
process are included in the result.Return Result: The dfs
function builds the list of killed processes, which is then returned.
Time Complexity: O(N), where N is the number of processes. This is because we visit each process at most once during the DFS traversal.
Space Complexity: O(N) in the worst case. This is due to the space used by the adjacency list g
and the recursion stack during the DFS. In the worst case (a completely unbalanced tree), the recursion stack could hold all the nodes in the path to the deepest leaf. The space for ans
is also proportional to N in the worst case where all processes are killed.
from collections import defaultdict
class Solution:
def killProcess(self, pid: List[int], ppid: List[int], kill: int) -> List[int]:
g = defaultdict(list) # Adjacency list to store process relationships
for i, p in zip(pid, ppid):
g[p].append(i)
ans = [] # List to store the killed processes
def dfs(i: int):
ans.append(i)
for j in g[i]: # Recursively call dfs for each child
dfs(j)
dfs(kill) # Initiate DFS from the kill process
return ans
The implementations in other languages (Java, C++, Go, TypeScript, Rust) follow a very similar structure, adapting the syntax and data structures to the specific language. The core logic of building the adjacency list and performing DFS remains the same.