Problem Description
We are given a tree with n nodes (numbered from 0 to n-1) represented via a parent array, where parent[0] = -1 for the root node. We need to implement a LockingTree class that supports three operations:
- lock(num, user): Lock the node num for the given user if it is currently unlocked.
- unlock(num, user): Unlock node num if it is currently locked by the same user.
- upgrade(num, user): If node num is unlocked, has at least one locked descendant, and none of its ancestors are locked, then lock node num for the user and unlock all of its descendants.
Key Insights
- Use an array (or similar data structure) to keep track of the lock status of each node (e.g., using -1 for unlocked and a valid user id when locked).
- Pre-compute a children mapping from the parent array, allowing efficient DFS/BFS traversal when checking or unlocking descendants.
- When upgrading, check upward (using the parent pointers) to ensure no locked ancestors.
- Use DFS from the node to both check for any locked descendants and to unlock them if needed.
Space and Time Complexity
Time Complexity:
- lock and unlock operations: O(1)
- upgrade operation: O(n) in worst-case due to traversal of the descendant subtree.
Space Complexity: O(n) for storing the children list and the lock state array.
Solution
The solution involves:
- Building a children list from the parent array to efficiently traverse descendants.
- Maintaining an array for the locked status of each node.
- For the lock operation, directly update the state if the node is unlocked.
- For the unlock operation, verify the user before unlocking.
- For the upgrade operation, first check that the node is unlocked, then traverse up the tree to ensure there are no locked ancestors. Next, perform a DFS to check if there is any locked descendant while unlocking them. If at least one descendant was locked, the current node is locked with the provided user.