#1993

Operations on Tree

Medium
ArrayHash TableTreeDepth-First SearchBreadth-First SearchDesignHash MapArray
LeetCode ↗

Approaches

Brute ForceOptimal
Complexity Comparison
Brute ForceOptimal Solution
Time
O(n²)
O(n)
Space
O(1)
O(n)
💡

Intuition

Time O(n)Space O(n)

This approach efficiently manages the locking and unlocking operations by using a combination of a parent-child relationship and a map to track locked nodes. It allows us to quickly check conditions for locking, unlocking, and upgrading without redundant traversals.

⚙️

Algorithm

4 steps
  1. 1Step 1: Store the parent-child relationships in a list and maintain a map for locked nodes.
  2. 2Step 2: For locking, check if the node is unlocked and lock it for the user.
  3. 3Step 3: For unlocking, check if the node is locked by the same user and unlock it.
  4. 4Step 4: For upgrading, check the three conditions (unlocked, has locked descendant, no locked ancestor) and perform the operations accordingly.
solution.py51 lines
1class LockingTree:
2    def __init__(self, parent):
3        self.parent = parent
4        self.locked = {}
5        self.children = [[] for _ in range(len(parent))]
6        for i in range(1, len(parent)):
7            self.children[parent[i]].append(i)
8
9    def lock(self, num, user):
10        if num in self.locked:
11            return False
12        self.locked[num] = user
13        return True
14
15    def unlock(self, num, user):
16        if num not in self.locked or self.locked[num] != user:
17            return False
18        del self.locked[num]
19        return True
20
21    def upgrade(self, num, user):
22        if num in self.locked:
23            return False
24        if not self.has_locked_descendant(num):
25            return False
26        if self.has_locked_ancestor(num):
27            return False
28        self.locked[num] = user
29        self.unlock_descendants(num)
30        return True
31
32    def has_locked_descendant(self, num):
33        if num in self.locked:
34            return True
35        for child in self.children[num]:
36            if self.has_locked_descendant(child):
37                return True
38        return False
39
40    def has_locked_ancestor(self, num):
41        while num != -1:
42            if num in self.locked:
43                return True
44            num = self.parent[num]
45        return False
46
47    def unlock_descendants(self, num):
48        if num in self.locked:
49            del self.locked[num]
50        for child in self.children[num]:
51            self.unlock_descendants(child)

Complexity note: This complexity is due to the fact that we can efficiently check for locked nodes and their relationships using a map and a list, allowing us to avoid redundant traversals.

  • 1Understanding tree traversal is crucial for managing parent-child relationships.
  • 2Efficiently tracking locked nodes can significantly reduce operation time.

Solutions and explanations are original Tejav content. Problem titles © LeetCode — use the LeetCode button above for the full problem statement.