Check if a binary tree is balanced (Big-O) - binary-tree

Check if a binary tree is balanced.
The source code on the CTCI 5th:
public class QuestionBrute {
public static int getHeight(TreeNode root) {
if (root == null) {
return 0;
}
return Math.max(getHeight(root.left), getHeight(root.right)) + 1;
}
public static boolean isBalanced(TreeNode root) {
if (root == null) {
return true;
}
int heightDiff = getHeight(root.left) - getHeight(root.right);
if (Math.abs(heightDiff) > 1) {
return false;
}
else {
return isBalanced(root.left) && isBalanced(root.right);
}
}
public static void main(String[] args) {
// Create balanced tree
int[] array = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
TreeNode root = TreeNode.createMinimalBST(array);
System.out.println("Root? " + root.data);
System.out.println("Is balanced? " + isBalanced(root));
// Could be balanced, actually, but it's very unlikely...
TreeNode unbalanced = new TreeNode(10);
for (int i = 0; i < 10; i++) {
unbalanced.insertInOrder(AssortedMethods.randomIntInRange(0, 100));
}
System.out.println("Root? " + unbalanced.data);
System.out.println("Is balanced? " + isBalanced(unbalanced));
}
}
As the algorithm has to check the height of every node, and we don't save the height in each recursion, the running time should be O(N^2).

First of all let's fix a bit your code. Your function to check if the root is balanced will not work simply because a binary tree is balanced if:
maxHeight(root) - minHeight(root) <= 1
I quote Wikipedia: "A balanced binary tree is commonly defined as a binary tree in which the depth of the two subtrees of every node differ by 1 or less"
Your algorithm will give the wrong answer for this tree:
When you call getHeight(Node7) it will return 3, and when you call getHeight(Node5) it will return 3 as well and since (0>1) == false you will return true :(
To fix this all you have to do is to implement the int MinHeight(TreeNode node) the same way you did getHeight() but with Math.min()
Now to your answer. In terms of runtime complexity whenever you call the getHeight() function from the root you are doing a DFS and since you have to visit all the nodes to find the height of the tree this algorithm will be O(N). Now it is true you execute this algorithm twice, when you call maxHeight(root) and minHeight(root) but since they are both O(N) (given that they do exactly what getHeight() does) the overall complexity will have C*N as an upper limit for some constant C and all N bigger than some N knot i.e. O(N) where N is the number of nodes of your tree ;)
Cheers!

Related

Reordering a binary search tree within the tree itself

If I am given a binary tree that is unordered, what would be the best method of ordering it without just creating a new tree? When I say ordered, I mean such that all nodes in a left subtree is less than the root node and all nodes in a right subtree is greater than the root node.
I appreciate that the most optimal way to make an undordered binary tree into a binary seach tree is to extract all the nodes then insert them into a new tree, but is there another approach involving switching the placement of nodes in the original tree that could be done algorithmically?
The method of creating a new tree is certainly the way to go, but just as an excercise, it is possible to sort a binary tree in-place.
You could for instance implement bubble sort, such that all nodes remain in place, but their values are swapped in the process.
For this to work you need to implement an inorder traversal. Then keep repeating a full inorder traversal, where you compare the values of two successively visited nodes, and swap their values if they are not in the right order. When an inorder traversal does not result in at least one swap, the tree is sorted and the process can stop.
Here is an implementation in JavaScript:
It first generates a tree with 10 nodes having randomly unsigned integers below 100. The shape of the tree is random too (based on a random "path" that is provided with each insertion)
Then it sorts the tree as described above. As JavaScript has support for generators and iterators, I have used that syntax, but it could also be done with a callback system.
It displays the tree in a very rudimentary way (90° rotated, i.e. with the root at the left side), as it is before and after the sort operation.
class Node {
constructor(value) {
this.value = value;
this.left = this.right = null;
}
}
class Tree {
constructor() {
this.root = null;
}
// Method to add a value as a new leaf node, using the
// binary bits in the given path to determine
// the leaf's location:
addAtPath(value, path) {
function recur(node, path) {
if (!node) return new Node(value);
if (path % 2) {
node.right = recur(node.right, path >> 1);
} else {
node.left = recur(node.left, path >> 1);
}
return node;
}
this.root = recur(this.root, path);
}
*inorder() {
function* recur(node) {
if (!node) return;
yield* recur(node.left);
yield node;
yield* recur(node.right);
}
yield* recur(this.root);
}
toString() {
function recur(node, indent) {
if (!node) return "";
return recur(node.right, indent + " ")
+ indent + node.value + "\n"
+ recur(node.left, indent + " ");
}
return recur(this.root, "");
}
bubbleSort() {
let dirty = true;
while (dirty) {
dirty = false;
let iterator = this.inorder();
// Get first node from inorder traversal
let prevNode = iterator.next().value;
for (let currNode of iterator) { // Get all other nodes
if (prevNode.value > currNode.value) {
// Swap
const temp = prevNode.value;
prevNode.value = currNode.value;
currNode.value = temp;
dirty = true;
}
prevNode = currNode;
}
}
}
}
// Helper
const randInt = max => Math.floor(Math.random() * max);
// Demo:
const tree = new Tree();
for (let i = 0; i < 10; i++) {
tree.addAtPath(randInt(100), randInt(0x80000000));
}
console.log("Tree before sorting (root is at left, leaves at the right):");
console.log(tree.toString());
tree.bubbleSort();
console.log("Tree after sorting:");
console.log(tree.toString());
The time complexity is O(n²) -- typical for bubble sort.
This sorting does not change the shape of the tree -- all nodes stay where they are. Only the values are moved around.

Last remaining number

I was asked this question in an interview.
Given an array 'arr' of positive integers and a starting index 'k' of the array. Delete element at k and jump arr[k] steps in the array in circular fashion. Do this repeatedly until only one element remain. Find the last remaining element.
I thought of O(nlogn) solution using ordered map. Is any O(n) solution possible?
My guess is that there is not an O(n) solution to this problem based on the fact that it seems to involve doing something that is impossible. The obvious thing you would need to solve this problem in linear time is a data structure like an array that exposes two operations on an ordered collection of values:
O(1) order-preserving deletes from the data structure.
O(1) lookups of the nth undeleted item in the data structure.
However, such a data structure has been formally proven to not exist; see "Optimal Algorithms for List Indexing and Subset Rank" and its citations. It is not a proof to say that if the natural way to solve some problem involves using a data structure that is impossible, the problem itself is probably impossible, but such an intuition is often correct.
Anyway there are lots of ways to do this in O(n log n). Below is an implementation of maintaining a tree of undeleted ranges in the array. GetIndex() below returns an index into the original array given a zero-based index into the array if items had been deleted from it. Such a tree is not self-balancing so will have O(n) operations in the worst case but in the average case Delete and GetIndex will be O(log n).
namespace CircleGame
{
class Program
{
class ArrayDeletes
{
private class UndeletedRange
{
private int _size;
private int _index;
private UndeletedRange _left;
private UndeletedRange _right;
public UndeletedRange(int i, int sz)
{
_index = i;
_size = sz;
}
public bool IsLeaf()
{
return _left == null && _right == null;
}
public int Size()
{
return _size;
}
public void Delete(int i)
{
if (i >= _size)
throw new IndexOutOfRangeException();
if (! IsLeaf())
{
int left_range = _left._size;
if (i < left_range)
_left.Delete(i);
else
_right.Delete(i - left_range);
_size--;
return;
}
if (i == _size - 1)
{
_size--; // Can delete the last item in a range by decremnting its size
return;
}
if (i == 0) // Can delete the first item in a range by incrementing the index
{
_index++;
_size--;
return;
}
_left = new UndeletedRange(_index, i);
int right_index = i + 1;
_right = new UndeletedRange(_index + right_index, _size - right_index);
_size--;
_index = -1; // the index field of a non-leaf is no longer necessarily valid.
}
public int GetIndex(int i)
{
if (i >= _size)
throw new IndexOutOfRangeException();
if (IsLeaf())
return _index + i;
int left_range = _left._size;
if (i < left_range)
return _left.GetIndex(i);
else
return _right.GetIndex(i - left_range);
}
}
private UndeletedRange _root;
public ArrayDeletes(int n)
{
_root = new UndeletedRange(0, n);
}
public void Delete(int i)
{
_root.Delete(i);
}
public int GetIndex(int indexRelativeToDeletes )
{
return _root.GetIndex(indexRelativeToDeletes);
}
public int Size()
{
return _root.Size();
}
}
static int CircleGame( int[] array, int k )
{
var ary_deletes = new ArrayDeletes(array.Length);
while (ary_deletes.Size() > 1)
{
int next_step = array[ary_deletes.GetIndex(k)];
ary_deletes.Delete(k);
k = (k + next_step - 1) % ary_deletes.Size();
}
return array[ary_deletes.GetIndex(0)];
}
static void Main(string[] args)
{
var array = new int[] { 5,4,3,2,1 };
int last_remaining = CircleGame(array, 2); // third element, this call is zero-based...
}
}
}
Also note that if the values in the array are known to be bounded such that they are always less than some m less than n, there are lots of O(nm) algorithms -- for example, just using a circular linked list.
I couldn't think of an O(n) solution. However, we could have O(n log n) average time by using a treap or an augmented BST with a value in each node for the size of its subtree. The treap enables us to find and remove the kth entry in O(log n) average time.
For example, A = [1, 2, 3, 4] and k = 3 (as Sumit reminded me in the comments, use the array indexes as values in the tree since those are ordered):
2(0.9)
/ \
1(0.81) 4(0.82)
/
3(0.76)
Find and remove 3rd element. Start at 2 with size = 2 (including the left subtree). Go right. Left subtree is size 1, which together makes 3, so we found the 3rd element. Remove:
2(0.9)
/ \
1(0.81) 4(0.82)
Now we're starting on the third element in an array with n - 1 = 3 elements and looking for the 3rd element from there. We'll use zero-indexing to correlate with our modular arithmetic, so the third element in modulus 3 would be 2 and 2 + 3 = 5 mod 3 = 2, the second element. We find it immediately since the root with its left subtree is size 2. Remove:
4(0.82)
/
1(0.81)
Now we're starting on the second element in modulus 2, so 1, and we're adding 2. 3 mod 2 is 1. Removing the first element we are left with 4 as the last element.

Diameter of a rooted k-ary tree

I'm trying to find a linear-time algorithm using recursion to solve the diameter problem for a rooted k-ary tree implemented with adjacency lists. The diameter of a tree is the maximum distance between any couple of leaves. If I choose a root r (that is, a node whose degree is > 1), it can be shown that the diameter is either the maximum distance between two leaves in the same subtree or the maximum distance between two leaves of a path that go through r. My pseudocode for this problem:
Tree-Diameter(T,r)
if degree[r] = 1 then
height[r] = 0
return 0
for each v in Adj[r] do
for i = 1 to degree[r] - 1 do
d_i = Tree-Diameter(T,v)
height[r] = max_{v in Adj[r]} (height[v]
return max(d_i, max_{v in V} (height[v]) + secmax_{v in V} (height[v], 0) + 1)
To get linear time, I compute the diameter AND the height of each subtree at the same time. Then, I choose the maximum quantity between the diameters of each subtrees and the the two biggest heights of the tree + 1 (the secmax function chooses between height[v] and 0 because some subtree can have only a child: in this case, the second biggest height is 0). I ask you if this algorithm works fine and if not, what are the problems? I tried to generalize an algorithm that solve the same problem for a binary tree but I don't know if it's a good generalization.
Any help is appreciated! Thanks in advance!
In all in tree for finding diameter do as below:
Select a random node A, run BFS on this node, to find furthermost node from A. name this node as S.
Now run BFS starting from S, find the furthermost node from S, name it D.
Path between S and D is diameter of your tree. This algorithm is O(n), and just two time traverses tree. Proof is little tricky but not hard. (try yourself or if you think is not true, I'll write it later). And be careful I'm talking about Trees not general graphs. (There is no loop in tree and is connected).
This is a python implementation of what I believe you are interested in. Here, a tree is represented as a list of child trees.
def process(tree):
max_child_height=0
secmax_child_height=0
max_child_diameter=0
for child in tree:
child_height,child_diameter=process(child)
if child_height>max_child_height:
secmax_child_height=max_child_height
max_child_height=child_height
elif child_height>secmax_child_height:
secmax_child_height=child_height
if child_diameter>max_child_diameter:
max_child_diameter=child_diameter
height=max_child_height+1
if len(tree)>1:
diameter=max(max_child_diameter,max_child_height+secmax_child_height)
else:
diameter=max_child_diameter
return height,diameter
def diameter(tree):
height,diameter=process(tree)
return diameter
This is the recursive solution with Java.
import java.util.ArrayList;
import java.util.List;
public class DiameterOrNAryTree {
public int diameter(Node root) {
Result result = new Result();
getDepth(root, result);
return result.max;
}
private int getDepth(Node node, Result result) {
if (node == null) return 0;
int h1 = 0, h2 = 0;
for (Node c : node.children) {
int d = getDepth(c, result);
if (d > h1) {
h2 = h1;
h1 = d;
} else if (d > h2) h2 = d;
}
result.max = Math.max(result.max, h1 + h2);
return h1 + 1;
}
class Result {
int max;
Result() {
max = 0;
}
}
class Node {
public int val;
public List<Node> children;
public Node() {
children = new ArrayList<Node>();
}
public Node(int _val) {
val = _val;
children = new ArrayList<Node>();
}
public Node(int _val, ArrayList<Node> _children) {
val = _val;
children = _children;
}
}
}

Lowest Common Ancestor implementations - what's the difference?

I've been reading about the Lowest Common Ancestor algorithm on top coder and I can't understand why the RMQ algorithm is involved - the solution listed there is insanely complicated and has the following properties:
O(sqrt(n)) time complexity for searches, O(n) precalculation time complexity
O(n) space complexity for storing parents of each node
O(n) space complexity again, for storing precalculations of each node
My solution: given 2 integer values, find the nodes through a simple preorder traversal. Take one of the nodes and go up the tree and store the path into a Set. Take the other node and go up the tree and check each node as I go up: if the node is in the Set, stop and return the LCA. Full implementation.
O(n) time complexity for finding each of the 2 nodes, given the values (because it's a regular tree, not a BST -
O(log n) space complexity for storing the path into the Set
O(log n) time complexity for going up the tree with the second node
So given these two choices, is the algorithm on Top Coder better and if yes, why? That's what I can't understand. I thought O(log n) is better than O(sqrt(n)).
public class LCA {
private class Node {
int data;
Node[] children = new Node[0];
Node parent;
public Node() {
}
public Node(int v) {
data = v;
}
#Override
public boolean equals(Object other) {
if (this.data == ((Node) other).data) {
return true;
}
return false;
}
}
private Node root;
public LCA() {
root = new Node(3);
root.children = new Node[4];
root.children[0] = new Node(15);
root.children[0].parent = root;
root.children[1] = new Node(40);
root.children[1].parent = root;
root.children[2] = new Node(100);
root.children[2].parent = root;
root.children[3] = new Node(10);
root.children[3].parent = root;
root.children[0].children = new Node[3];
root.children[0].children[0] = new Node(22);
root.children[0].children[0].parent = root.children[0];
root.children[0].children[1] = new Node(11);
root.children[0].children[1].parent = root.children[0];
root.children[0].children[2] = new Node(99);
root.children[0].children[2].parent = root.children[0];
root.children[2].children = new Node[2];
root.children[2].children[0] = new Node(120);
root.children[2].children[0].parent = root.children[2];
root.children[2].children[1] = new Node(33);
root.children[2].children[1].parent = root.children[2];
root.children[3].children = new Node[4];
root.children[3].children[0] = new Node(51);
root.children[3].children[0].parent = root.children[3];
root.children[3].children[1] = new Node(52);
root.children[3].children[1].parent = root.children[3];
root.children[3].children[2] = new Node(53);
root.children[3].children[2].parent = root.children[3];
root.children[3].children[3] = new Node(54);
root.children[3].children[3].parent = root.children[3];
root.children[3].children[0].children = new Node[2];
root.children[3].children[0].children[0] = new Node(25);
root.children[3].children[0].children[0].parent = root.children[3].children[0];
root.children[3].children[0].children[1] = new Node(26);
root.children[3].children[0].children[1].parent = root.children[3].children[0];
root.children[3].children[3].children = new Node[1];
root.children[3].children[3].children[0] = new Node(27);
root.children[3].children[3].children[0].parent = root.children[3].children[3];
}
private Node findNode(Node root, int value) {
if (root == null) {
return null;
}
if (root.data == value) {
return root;
}
for (int i = 0; i < root.children.length; i++) {
Node found = findNode(root.children[i], value);
if (found != null) {
return found;
}
}
return null;
}
public void LCA(int node1, int node2) {
Node n1 = findNode(root, node1);
Node n2 = findNode(root, node2);
Set<Node> ancestors = new HashSet<Node>();
while (n1 != null) {
ancestors.add(n1);
n1 = n1.parent;
}
while (n2 != null) {
if (ancestors.contains(n2)) {
System.out.println("Found common ancestor between " + node1 + " and " + node2 + ": node " + n2.data);
return;
}
n2 = n2.parent;
}
}
public static void main(String[] args) {
LCA tree = new LCA();
tree.LCA(33, 27);
}
}
The LCA algorithm works for any tree (not necessarily binary and not necessarily balanced). Your "simple algorithm" analysis breaks down since tracing a path to a root node is actually O(N) time and space instead of O(log N)
Just want to point out that the problem is about the rooted tree and not binary search tree. So, in you algorithm the
O(n) time complexity for finding each of the 2 nodes, given the values
O(n) space complexity for storing the path into the Set
O(sqrt(n)) time complexity for going up the tree with the second node and searching in the first n-stored elements.
Checking of each node as we go up from the second node with take O(n), so for n nodes it will take O(sqrt(n)).
The Harel and Tarjan LCA algorithm (reference in the link you gave) uses a pre-calculation with O(n) complexity, after which a lookup is O(1) (not O(sqrt(n) as you claim).

Merging 2 Binary Search Trees

How do you merge 2 Binary Search Trees in such a way that the resultant tree contains all the elements of both the trees and also maintains the BST property.
I saw the solution provided in
How to merge two BST's efficiently?
However that solution involves converting into a Double Linked List. I was wondering if there is a more elegant way of doing this which could be done in place without the conversion. I came up with the following pseudocode. Does it work for all cases? Also I am having trouble with the 3rd case.
node* merge(node* head1, node* head2) {
if (!head1)
return head2;
if (!head2)
return head1;
// Case 1.
if (head1->info > head2->info) {
node* temp = head2->right;
head2->right = NULL;
head1->left = merge(head1->left, head2);
head1 = merge(head1, temp);
return head1;
} else if (head1->info < head2->info) { // Case 2
// Similar to case 1.
} else { // Case 3
// ...
}
}
The two binary search trees (BST) cannot be merged directly during a recursive traversal.
Suppose we should merge Tree 1 and Tree 2 shown in the figure.
The recursion should reduce the merging to a simpler situation. We cannot reduce
the merging only to the respective left subtrees L1 and L2, because L2 can contain
numbers larger than 10, so we would need to include the right
subtree R1 into the process. But then we include numbers greater
than 10 and possibly greater than 20, so we would need to include
the right subtree R2 as well. A similar reasoning shows that
we cannot simplify the merging by including subtrees from Tree 1 and from Tree 2
at the same time.
The only possibility for reduction is to simplify only inside the respective trees.
So, we can transform
the trees to their right spines with sorted nodes:
Now, we can merge the two spines easily into one spine. This
spine is in fact a BST, so we could stop here. However, this BST
is completely unbalanced, so we transform it to a balanced BST.
The complexity is:
Spine 1: time = O(n1), space = O(1)
Spine 2: time = O(n2), space = O(1)
Merge: time = O(n1+n2), space = O(1)
Balance: time = O(n1+n2), space = O(1)
Total: time = O(n1+n2), space = O(1)
The complete running code is on http://ideone.com/RGBFQ. Here are the essential parts. The top level code is a follows:
Node* merge(Node* n1, Node* n2) {
Node *prev, *head1, *head2;
prev = head1 = 0; spine(n1, prev, head1);
prev = head2 = 0; spine(n2, prev, head2);
return balance(mergeSpines(head1, head2));
}
The auxiliary functions are for the tranformation to spines:
void spine(Node *p, Node *& prev, Node *& head) {
if (!p) return;
spine(p->left, prev, head);
if (prev) prev->right = p;
else head = p;
prev = p;
p->left = 0;
spine(p->right, prev, head);
}
Merging of the spines:
void advance(Node*& last, Node*& n) {
last->right = n;
last = n;
n = n->right;
}
Node* mergeSpines(Node* n1, Node* n2) {
Node head;
Node* last = &head;
while (n1 || n2) {
if (!n1) advance(last, n2);
else if (!n2) advance(last, n1);
else if (n1->info < n2->info) advance(last, n1);
else if (n1->info > n2->info) advance(last, n2);
else {
advance(last, n1);
printf("Duplicate key skipped %d \n", n2->info);
n2 = n2->right;
}
}
return head.right;
}
Balancing:
Node* balance(Node *& list, int start, int end) {
if (start > end) return NULL;
int mid = start + (end - start) / 2;
Node *leftChild = balance(list, start, mid-1);
Node *parent = list;
parent->left = leftChild;
list = list->right;
parent->right = balance(list, mid+1, end);
return parent;
}
Node* balance(Node *head) {
int size = 0;
for (Node* n = head; n; n = n->right) ++size;
return balance(head, 0, size-1);
}
Assuming we have two trees A and B we insert root of tree A into tree B and using rotations move inserted root to become new root of tree B. Next we recursively merge left and right sub-trees of trees A and B. This algorithm takes into account both trees structure but insertion still depends on how balanced target tree is. You can use this idea to merge the two trees in O(n+m) time and O(1) space.
The following implementation is due to Dzmitry Huba:
// Converts tree to sorted singly linked list and appends it
// to the head of the existing list and returns new head.
// Left pointers are used as next pointer to form singly
// linked list thus basically forming degenerate tree of
// single left oriented branch. Head of the list points
// to the node with greatest element.
static TreeNode<T> ToSortedList<T>(TreeNode<T> tree, TreeNode<T> head)
{
if (tree == null)
// Nothing to convert and append
return head;
// Do conversion using in order traversal
// Convert first left sub-tree and append it to
// existing list
head = ToSortedList(tree.Left, head);
// Append root to the list and use it as new head
tree.Left = head;
// Convert right sub-tree and append it to list
// already containing left sub-tree and root
return ToSortedList(tree.Right, tree);
}
// Merges two sorted singly linked lists into one and
// calculates the size of merged list. Merged list uses
// right pointers to form singly linked list thus forming
// degenerate tree of single right oriented branch.
// Head points to the node with smallest element.
static TreeNode<T> MergeAsSortedLists<T>(TreeNode<T> left, TreeNode<T> right, IComparer<T> comparer, out int size)
{
TreeNode<T> head = null;
size = 0;
// See merge phase of merge sort for linked lists
// with the only difference in that this implementations
// reverts the list during merge
while (left != null || right != null)
{
TreeNode<T> next;
if (left == null)
next = DetachAndAdvance(ref right);
else if (right == null)
next = DetachAndAdvance(ref left);
else
next = comparer.Compare(left.Value, right.Value) > 0
? DetachAndAdvance(ref left)
: DetachAndAdvance(ref right);
next.Right = head;
head = next;
size++;
}
return head;
}
static TreeNode<T> DetachAndAdvance<T>(ref TreeNode<T> node)
{
var tmp = node;
node = node.Left;
tmp.Left = null;
return tmp;
}
// Converts singly linked list into binary search tree
// advancing list head to next unused list node and
// returning created tree root
static TreeNode<T> ToBinarySearchTree<T>(ref TreeNode<T> head, int size)
{
if (size == 0)
// Zero sized list converts to null
return null;
TreeNode<T> root;
if (size == 1)
{
// Unit sized list converts to a node with
// left and right pointers set to null
root = head;
// Advance head to next node in list
head = head.Right;
// Left pointers were so only right needs to
// be nullified
root.Right = null;
return root;
}
var leftSize = size / 2;
var rightSize = size - leftSize - 1;
// Create left substree out of half of list nodes
var leftRoot = ToBinarySearchTree(ref head, leftSize);
// List head now points to the root of the subtree
// being created
root = head;
// Advance list head and the rest of the list will
// be used to create right subtree
head = head.Right;
// Link left subtree to the root
root.Left = leftRoot;
// Create right subtree and link it to the root
root.Right = ToBinarySearchTree(ref head, rightSize);
return root;
}
public static TreeNode<T> Merge<T>(TreeNode<T> left, TreeNode<T> right, IComparer<T> comparer)
{
Contract.Requires(comparer != null);
if (left == null || right == null)
return left ?? right;
// Convert both trees to sorted lists using original tree nodes
var leftList = ToSortedList(left, null);
var rightList = ToSortedList(right, null);
int size;
// Merge sorted lists and calculate merged list size
var list = MergeAsSortedLists(leftList, rightList, comparer, out size);
// Convert sorted list into optimal binary search tree
return ToBinarySearchTree(ref list, size);
}
The best way we could merge the trees in place is something like:
For each node n in first BST {
Go down the 2nd tree and find the appropriate place to insert n
Insert n there
}
Each iteration in the for loop is O(log n) since we are dealing with trees, and the for loop will be iterated n times, so in total we have O(n log n).
A BST is a ordered or sorted binary tree. My algorithm would be to simple :
traverse through both trees
compare the values
insert the smaller of the two into a new BST.
The python code for traversing is as follows:
def traverse_binary_tree(node, callback):
if node is None:
return
traverse_binary_tree(node.leftChild, callback)
callback(node.value)
traverse_binary_tree(node.rightChild, callback)
The cost for traversing through the BST and building a new merged BST would remain O(n)
This blog post provides a solution to the problem with O(logn) space complexity. (Pay attention that the given approach does not modify input trees.)
This can be done in 3 steps:
covert the BSTs to sorted linked list (this can be done in place with O(m+n) time)
Merge this two sorted linked lists to a single list (this can be done in place with O(m+n) time)
Convert sorted linked list to balanced BST (this can be done in place with O(m+n) time)
Here is what I would do.
This solution is O(n1+n2) time complexity.
STEPS:
Perform the inorder traversal of both the trees to get sorted arrays --> linear time
Merge the two arrays --> again linear time
Convert the merged array into a Balanced binary search tree --> again linear time
This would require O(n1+n2) time and space.
Links you may find useful while implementing:
How to merge 2 sorted arrays
Inorder traversal
Sorted array to a balanced BST
The following algorithm is from Algorithms in C++.
The idea is almost the same as in the algorithm posted by PengOne. This algorithm does in place merging, time complexity is O(n+m).
link join(link a, link b) {
if (b == 0) return a;
if (a == 0) return b;
insert(b, a->item);
b->left = join(a->left, b->left);
b->right = join(a->right, b->right);
delete a;
return b;
}
insert just inserts an item in the right place in the tree.
void insert(link &h, Item x) {
if (h == 0) {
h = new node(x);
return;
}
if (x.key() < h->item.key()) {
insert(h->left, x);
rotateRight(h);
}
else {
insert(h->right, x);
rotateLeft(h);
}
}
rotateRight and rotateLeft keep tree in the right order.
void rotateRight(link &h) {
link x = h->left;
h->left = x->right;
x->right = h;
h = x;
}
void rotateLeft(link &h) {
link x = h->right;
h->right = x->left;
x->left = h;
h = x;
}
Here link is node *.
Assuming the question is just to print sorted from both BSTs. Then the easier way is,
Store inorder traversal of 2 BSTs in 2 seperate arrays.
Now the problem reduces to merging\printing elements from 2 sorted arrays, which we got from step one. This merging can be done in o(m) when m>n or o(n) when m
Complexity: o(m+n)
Aux space: o(m+n) for the 2 arrays
MergeTwoBST_to_BalancedBST.java
public class MergeTwoBST_to_BalancedBST {
// arr1 and arr2 are the input arrays to be converted into a binary search
// structure and then merged and then balanced.
int[] arr1 = new int[] { 1, 2, 3, 4, 5, 6, 7, 8 };
int[] arr2 = new int[] { 11, 12, 13, 14, 15, 16, 17, 18 };
BSTNode root1;
BSTNode root2;
// vector object to hold the nodes from the merged unbalanced binary search
// tree.
Vector<BSTNode> vNodes = new Vector<BSTNode>();
/**
* Constructor to initialize the Binary Search Tree root nodes to start
* processing. This constructor creates two trees from two given sorted
* array inputs. root1 tree from arr1 and root2 tree from arr2.
*
* Once we are done with creating the tree, we are traversing the tree in
* inorder format, to verify whether nodes are inserted properly or not. An
* inorder traversal should give us the nodes in a sorted order.
*/
public MergeTwoBST_to_BalancedBST() {
// passing 0 as the startIndex and arr1.length-1 as the endIndex.
root1 = getBSTFromSortedArray(arr1, 0, arr1.length - 1);
System.out.println("\nPrinting the first binary search tree");
inorder(root1); // traverse the tree in inorder format to verify whether
// nodes are inserted correctly or not.
// passing 0 as the startIndex and arr2.length-1 as the endIndex.
root2 = getBSTFromSortedArray(arr2, 0, arr2.length - 1);
System.out.println("\nPrinting the second binary search tree");
inorder(root2); // same here - checking whether the nodes are inserted
// properly or not.
}
/**
* Method to traverse the tree in inorder format. Where it traverses the
* left child first, then root and then right child.
*
* #param node
*/
public void inorder(BSTNode node) {
if (null != node) {
inorder(node.getLeft());
System.out.print(node.getData() + " ");
inorder(node.getRight());
}
}
/**
* Method to traverse the tree in preorder format. Where it traverses the
* root node first, then left child and then right child.
*
* #param node
*/
public void preorder(BSTNode node) {
if (null != node) {
System.out.print(node.getData() + " ");
preorder(node.getLeft());
preorder(node.getRight());
}
}
/**
* Creating a new Binary Search Tree object from a sorted array and
* returning the root of the newly created node for further processing.
*
* #param arr
* #param startIndex
* #param endIndex
* #return
*/
public BSTNode getBSTFromSortedArray(int[] arr, int startIndex, int endIndex) {
if (startIndex > endIndex) {
return null;
}
int middleIndex = startIndex + (endIndex - startIndex) / 2;
BSTNode node = new BSTNode(arr[middleIndex]);
node.setLeft(getBSTFromSortedArray(arr, startIndex, middleIndex - 1));
node.setRight(getBSTFromSortedArray(arr, middleIndex + 1, endIndex));
return node;
}
/**
* This method involves two operation. First - it adds the nodes from root1
* tree to root2 tree, and hence we get a merged root2 tree.Second - it
* balances the merged root2 tree with the help of a vector object which can
* contain objects only of BSTNode type.
*/
public void mergeTwoBinarySearchTree() {
// First operation - merging the trees. root1 with root2 merging should
// give us a new root2 tree.
addUtil(root1);
System.out.println("\nAfter the root1 tree nodes are added to root2");
System.out.println("Inorder Traversal of root2 nodes");
inorder(root2); // inorder traversal of the root2 tree should display
// the nodes in a sorted order.
System.out.println("\nPreorder traversal of root2 nodes");
preorder(root2);
// Second operation - this will take care of balancing the merged binary
// search trees.
balancedTheMergedBST();
}
/**
* Here we are doing two operations. First operation involves, adding nodes
* from root2 tree to the vector object. Second operation involves, creating
* the Balanced binary search tree from the vector objects.
*/
public void balancedTheMergedBST() {
// First operation : adding nodes to the vector object
addNodesToVector(root2, vNodes);
int vSize = vNodes.size();
// Second operation : getting a balanced binary search tree
BSTNode node = getBalancedBSTFromVector(vNodes, 0, vSize - 1);
System.out
.println("\n********************************************************");
System.out.println("After balancing the merged trees");
System.out.println("\nInorder Traversal of nodes");
inorder(node); // traversing the tree in inoder process should give us
// the output in sorted order ascending
System.out.println("\nPreorder traversal of root2 nodes");
preorder(node);
}
/**
* This method will provide us a Balanced Binary Search Tree. Elements of
* the root2 tree has been added to the vector object. It is parsed
* recursively to create a balanced tree.
*
* #param vNodes
* #param startIndex
* #param endIndex
* #return
*/
public BSTNode getBalancedBSTFromVector(Vector<BSTNode> vNodes,
int startIndex, int endIndex) {
if (startIndex > endIndex) {
return null;
}
int middleIndex = startIndex + (endIndex - startIndex) / 2;
BSTNode node = vNodes.get(middleIndex);
node.setLeft(getBalancedBSTFromVector(vNodes, startIndex,
middleIndex - 1));
node.setRight(getBalancedBSTFromVector(vNodes, middleIndex + 1,
endIndex));
return node;
}
/**
* This method traverse the tree in inorder process and adds each node from
* root2 to the vector object vNodes object only accepts objects of BSTNode
* type.
*
* #param node
* #param vNodes
*/
public void addNodesToVector(BSTNode node, Vector<BSTNode> vNodes) {
if (null != node) {
addNodesToVector(node.getLeft(), vNodes);
// here we are adding the node in the vector object.
vNodes.add(node);
addNodesToVector(node.getRight(), vNodes);
}
}
/**
* This method traverse the root1 tree in inorder process and add the nodes
* in the root2 tree based on their value
*
* #param node
*/
public void addUtil(BSTNode node) {
if (null != node) {
addUtil(node.getLeft());
mergeToSecondTree(root2, node.getData());
addUtil(node.getRight());
}
}
/**
* This method adds the nodes found from root1 tree as part it's inorder
* traversal and add it to the second tree.
*
* This method follows simple Binary Search Tree inserstion logic to insert
* a node considering the tree already exists.
*
* #param node
* #param data
* #return
*/
public BSTNode mergeToSecondTree(BSTNode node, int data) {
if (null == node) {
node = new BSTNode(data);
} else {
if (data < node.getData()) {
node.setLeft(mergeToSecondTree(node.getLeft(), data));
} else if (data > node.getData()) {
node.setRight(mergeToSecondTree(node.getRight(), data));
}
}
return node;
}
/**
*
* #param args
*/
public static void main(String[] args) {
MergeTwoBST_to_BalancedBST mergeTwoBST = new MergeTwoBST_to_BalancedBST();
mergeTwoBST.mergeTwoBinarySearchTree();
}
}
BSTNode.java:
public class BSTNode {
BSTNode left, right;
int data;
/* Default constructor */
public BSTNode() {
left = null;
right = null;
data = 0;
}
/* Constructor */
public BSTNode(int data) {
left = null;
right = null;
this.data = data;
}
public BSTNode getLeft() {
return left;
}
public void setLeft(BSTNode left) {
this.left = left;
}
public BSTNode getRight() {
return right;
}
public void setRight(BSTNode right) {
this.right = right;
}
public int getData() {
return data;
}
public void setData(int data) {
this.data = data;
}
}

Resources