As part of my university algorithm course, we were asked the following question:
Given two binary search trees, T1 and T2 (not necessarily balanced). h1 and h2 represent the height of each tree, n1 and n2 represent the number of elements in each tree.
Write an algorithm that merges the two trees into one red black tree. Time complexity required is θ(max(n1, n2)). Space complexity required is θ(max(h1, h2)).
Note: the RBT should be created from the existing nodes of T1 and T2.
The nodes of the binary trees already have the color property, and they are all set to red. We should obviously change them accordingly when creating the RBT.
So far I am not bad at those type of question, but this time I’m a bit clueless…
I was thinking of changing the pointers of the trees into linked list for each level and to store a pointer to the head of each list at an array of size h, but I can't find a way to merge those linked list into one red black tree at time complexity of θ(n).
Here is a sketch of an algorithm:
For each of the given BSTs create an inorder iterator, which which you can traverse nodes in their proper value order.
Combine these two iterators into one (much like with merge sort), so that it produces nodes in their proper value order.
As you know that the target tree will have = 1+2 nodes, you can aim for a shape of the target tree that is complete, i.e. where all levels are fully populated, except maybe for the bottom layer, which will be populated such that all its nodes are at the left side of that layer.
Using an array with an entry per level in the target tree (that still is to be populated), you can track the path to where the next node should be placed. The first node should be placed at the bottom layer, the next node should be its parent, the one after that should be its sibling, ...etc. As you do that traversal on the input trees and get nodes from it, use those nodes for creating the target tree with the help of this array (so the tree builds up in in-order fashion).
Take care that the iterators over the input trees do not get "disoriented" by the reuse of the yielded nodes in the target tree: make sure the cursors move one step ahead before the "current" node gets mutated into its target position.
Color all nodes black, except for the nodes in the bottom layer when it is not completely filled -- those should be red. This way the tree fulfils the red-black property.
All those actions can be done with θ() time complexity, which is equivalent with θ(1+2) or θ(max(1,2)).
The space complexity (not counting the existing nodes) is determined by the inorder traversal (both in reading and writing) which needs to maintain the path from the root to the current node, so that we have a space complexity of θ(ℎ1+ℎ2), again equivalent with θ(max(ℎ1,ℎ2)).
Implementation
Here is an implementation in JavaScript of the above described algorithm: