Lockable Binary Trees
This interview question was asked by Google.
Implement locking in a binary tree. A binary tree node can be locked or unlocked only if all of its descendants or ancestors are not locked.
Design a binary tree node class with the following methods:
is_locked, which returns whether the node is locked
lock, which attempts to lock the node. If it cannot be locked, then it should return false. Otherwise, it should lock it and return true.
unlock, which unlocks the node. If it cannot be unlocked, then it should return false. Otherwise, it should unlock it and return true.
You may augment the node to add parent pointers or any other property you would like.
You may assume the class is used in a single-threaded program, so there is no need for actual locks or mutexes.
Each method should run in O(h), where h is the height of the tree.
A way to implement this would be to augment each node with an
is_locked attribute as well as a parent pointer. We can then implement the methods this way:
is_lockedsimply returns the node’s attribute
locksearches the node’s children and parents for a true
is_lockedattribute. If it is set to true on any of them, then return false. Otherwise, set the current node’s
is_lockedto true and return true.
unlocksimply changes the node’s attribute to false. If we want to be safe, then we should search the node’s children and parents as in
lockto make sure we can actually unlock the node, but that shouldn’t ever happen.
is_locked is O(1) time,
unlock will take O(m + h) time where m is the number of nodes in the node’s subtree (since we have to traverse through all its descendants) and h is the height of the node (since we have to traverse through the node’s ancestors).
We can improve the performance of
unlock by adding another field to the node that keeps tracks of the count of locked descendants. That way, we can immediately see whether any of its descendants are locked. This will reduce our
unlock functions to only O(h). We can maintain this field by doing the following:
- When locking, if the locking succeeds, traverse the node’s ancestors and increment each one’s count
- When unlocking, traverse the node’s ancestors and decrement each one’s count
In the end, the code will look something like the following:
is_locked is still O(1), but
unlock are both O(h) instead of O(m + h).
Are you interviewing for programming jobs, or do you just enjoy fun programming questions? Check out our newsletter, Daily Coding Problem, to get a question in your inbox every day.
Thanks for reading! I hope this was interesting. Good luck on your interviews!
Ace your programming interview
Get a coding problem every day in your inbox!