Operations on Tree - Solution & Explanation
Problem Statement
You are given a tree with n nodes numbered from 0 to n - 1 in the form of a parent array parent where parent[i] is the parent of the ith node. The root of the tree is node 0, so parent[0] = -1 since it has no parent. You want to design a data structure that allows users to lock, unlock, and upgrade nodes in the tree.
The data structure should support the following functions:
- Lock: Locks the given node for the given user and prevents other users from locking the same node. You may only lock a node using this function if the node is unlocked.
- Unlock: Unlocks the given node for the given user. You may only unlock a node using this function if it is currently locked by the same user.
- Upgrade: Locks the given node for the given user and unlocks all of its descendants regardless of who locked it. You may only upgrade a node if all 3 conditions are true:
- The node is unlocked,
- It has at least one locked descendant (by any user), and
- It does not have any locked ancestors.
Implement the LockingTree class:
LockingTree(int[] parent)initializes the data structure with the parent array.lock(int num, int user)returnstrueif it is possible for the user with iduserto lock the nodenum, orfalseotherwise. If it is possible, the nodenumwill become locked by the user with iduser.unlock(int num, int user)returnstrueif it is possible for the user with iduserto unlock the nodenum, orfalseotherwise. If it is possible, the nodenumwill become unlocked.upgrade(int num, int user)returnstrueif it is possible for the user with iduserto upgrade the nodenum, orfalseotherwise. If it is possible, the nodenumwill be upgraded.
Example 1:
Input
["LockingTree", "lock", "unlock", "unlock", "lock", "upgrade", "lock"]
[[[-1, 0, 0, 1, 1, 2, 2]], [2, 2], [2, 3], [2, 2], [4, 5], [0, 1], [0, 1]]
Output
[null, true, false, true, true, true, false]
Explanation
LockingTree lockingTree = new LockingTree([-1, 0, 0, 1, 1, 2, 2]);
lockingTree.lock(2, 2); // return true because node 2 is unlocked.
// Node 2 will now be locked by user 2.
lockingTree.unlock(2, 3); // return false because user 3 cannot unlock a node locked by user 2.
lockingTree.unlock(2, 2); // return true because node 2 was previously locked by user 2.
// Node 2 will now be unlocked.
lockingTree.lock(4, 5); // return true because node 4 is unlocked.
// Node 4 will now be locked by user 5.
lockingTree.upgrade(0, 1); // return true because node 0 is unlocked and has at least one locked descendant (node 4).
// Node 0 will now be locked by user 1 and node 4 will now be unlocked.
lockingTree.lock(0, 1); // return false because node 0 is already locked.
Constraints:
n == parent.length2 <= n <= 20000 <= parent[i] <= n - 1fori != 0parent[0] == -10 <= num <= n - 11 <= user <= 104parentrepresents a valid tree.- At most
2000calls in total will be made tolock,unlock, andupgrade.
Approach Overview
Problem Overview: You need to design a data structure that supports three operations on a tree: lock, unlock, and upgrade. A node can be locked by a user, unlocked by the same user, and upgraded only if none of its ancestors are locked and at least one descendant is locked. The challenge is efficiently checking ancestor and descendant states during these operations.
Approach 1: DFS Traversal for Tree Operations (O(n) per upgrade, O(h) ancestor check)
This approach builds the tree from the parent array and stores the lock status for each node. When performing operations, you explicitly traverse the structure. For ancestor validation, iterate upward using the parent pointer until reaching the root. For descendant checks during upgrade, run a Depth-First Search or Breadth-First Search starting from the target node to detect locked descendants and unlock them if needed.
The key idea is direct traversal instead of maintaining extra metadata. The lock and unlock operations run in O(1) time, while the upgrade operation may scan the subtree, giving O(n) time in the worst case with O(n) space for the adjacency list. This solution is simple to implement and works well when the number of upgrade operations is small.
Approach 2: Using Parent and Child Tracking (O(h + k) operations)
This design improves efficiency by storing additional relationships between nodes. Along with the parent array, maintain a list of children for each node and track lock ownership in a hash structure. Checking ancestors becomes a quick upward traversal of height h. Descendant verification can iterate through children recursively and unlock nodes as required.
The advantage is clearer control over subtree traversal and easier management of node relationships. The upgrade operation runs in O(k), where k is the number of nodes in the subtree being inspected, and space complexity remains O(n). This approach fits naturally with problems involving tree design and hierarchical state management.
Recommended for interviews: Interviewers typically expect the DFS-based design. It demonstrates that you understand tree traversal, ancestor checks, and state management. Starting with the straightforward traversal approach shows clear reasoning. Optimizing the design with structured parent/child tracking highlights deeper understanding of data structure design.
Approach 1: DFS Traversal for Tree Operations
In this approach, we use recursive Depth First Search (DFS) to handle tree operations. The idea is to keep track of locked nodes using an array, allowing quick updates and checks. For the 'upgrade' operation, we ensure that the target node is unlocked and it has at least one locked descendant. We achieve this by recursively examining descendants while also ensuring no ancestors are locked.
This Python solution uses a simple tree traversal method to perform lock/unlock operations and complex upgrade operations. It first ensures the node to be upgraded is unlocked and has no locked ancestors. Then it checks for any locked descendants and unlocks them, finally locking the target node for the upgrade.
Code
Python
JavaScript
Complexity
Time Complexity: O(N) in worst case for upgrade operation due to DFS traversal. Space Complexity: O(N) where N is the number of nodes, to keep track of locks and the recursion stack.
Approach 2: Using Parent and Child Tracking
This approach involves explicitly maintaining a list of children for each node to enable efficient descendant operations. Besides storing locked states, we also keep a list of children for each node, which simplifies descendant traversal, making tree operations like upgrade potentially quicker by directly accessing children.
This Java solution uses an additional children list for each node to facilitate child traversal easily. By storing references to each node's children, we can more efficiently implement and verify upgrade conditions.
Complexity
Time Complexity: O(N) where N is the total number of nodes when checking and unlocking all descendants. Space Complexity: O(N) for maintaining the array and child lists.
Approach 3: Default Approach
Code
Python
Java
C++
Go
TypeScript
Complexity Comparison
| Approach | Complexity |
|---|---|
| DFS Traversal for Tree Operations | Time Complexity: O(N) in worst case for upgrade operation due to DFS traversal. Space Complexity: O(N) where N is the number of nodes, to keep track of locks and the recursion stack. |
| Using Parent and Child Tracking | Time Complexity: O(N) where N is the total number of nodes when checking and unlocking all descendants. Space Complexity: O(N) for maintaining the array and child lists. |
| Default Approach | — |
Detailed Complexity Analysis
| Approach | Time | Space | When to Use |
|---|---|---|---|
| DFS Traversal for Tree Operations | Lock/Unlock: O(1), Upgrade: O(n) | O(n) | Best for simple implementations and interview settings where clarity matters more than heavy optimization. |
| Using Parent and Child Tracking | O(h + k) | O(n) | Better when frequent upgrade operations require repeated ancestor and subtree validation. |
Video Solution
Operations on Tree - Leetcode Biweekly Contest - 1993 - Python • NeetCode • 15,074 views views
Watch 4 more video solutions →Frequently Asked Questions
Is Operations on Tree easy or hard?
Operations on Tree Python/Java solution
How to solve Operations on Tree in O(n)?
What is the best approach for Operations on Tree?
Is Operations on Tree asked at Google/Amazon/Meta?
What data structure is used in Operations on Tree?
What is the time complexity of Operations on Tree?
Ready to solve this problem?
Practice Operations on Tree with our built-in code editor and test cases.
Practice on FleetCodeTable of Contents
Practice this problem
Open in Editor