the problem I am having trouble with is as follows.
Suppose that you have two different binary search trees, A and B, and
you somehow know that the largest element in A is less than the
smallest element in B. Suppose also that height(A) < height(B). Give
an algorithm for destructively creating a binary search tree that will
contain all the elements in A ∪ B and that runs in time O(height(A)).
So since the largest element in A is less than the smallest in B, that means every element in A is smaller than every element in B. In the new tree, the left hand side should be tree A and the right hand side should be tree B. But how do you do the merge programatically in time O(height(A))? Won't you need to loop through B as well? (which would make it O(height(A)+height(B))
As the problem is stated currently, you could just append the root of B tree as a right child of the largest element (which is the rightmost leaf) of A tree.
The complexity would be O(height(A)) - to find the leaf.
In the new tree, the left hand side should be tree A and the right hand side should be tree B.
Then what would the element at the root node be?
But how do you do the merge programatically in time O(height(A))?
If you were right, then you could do it in O(1): node(X, A, B) (pseudocode for creating a node with element X, left subtree A, and right subtree B).
Won't you need to loop through B as well? (which would make it O(height(A)+height(B))
If by "loop through B" you mean visiting all elements, then that would take even longer. In a balanced search tree, height(B) is proportional to log(size(B)).
What you can do instead is walk down the rightmost branch of A (this takes O(height(A)) steps). Once you've found the end, insert B there (by assigning to node.right, in O(1)).
Related
I'm trying to write an algorithm for this problem:
Merge three binary search trees into one sorted array, using O(n) time and O(1) additional space.
I think the straightforward answer is to do an in-order traversal of all three trees at once and compare the elements while traversing. But how can I do such a traversal in all three trees at once? Especially when the trees don't all have the same number of elements.
Your idea seems right.
In each tree, maintain a pointer (iterator).
Initially, the iterator should point to the leftmost node of the tree.
In every iteration, select the minimum of the elements under the three current pointers (it is O(1) time and memory).
Then put that minimum into the resulting array.
After that, advance the corresponding pointer so that it points to the leftmost unvisited element of the tree.
To be able to do that in O(1) memory, the tree should allow some way to go to this next unvisited element: it is sufficient to have a pointer to parent in each node.
Proceed with such iterations until all nodes are visited.
The traversal of a whole tree of n elements takes O(n) time: there are n-1 edges, and the process moves twice along each edge, once up and once down.
So the resulting complexity is 3*O(n) = O(n).
The algorithm to find the next unvisited node is as follows.
Note that, when we are at a node, its left subtree is already fully visited.
The steps are as follows:
While there is no unvisited right child, go up to the parent once.
If, in doing so, we went up and right (we were at the left child), stop right there at the parent.
If we were at the root, terminate the traversal.
Assuming we did not stop yet, there's a right child.
Go there.
Then while there's a left child, go to the left child.
Stop.
The best way to grasp it is perhaps to visualize the steps on some non-trivial picture of a binary search tree. For example, there are explanatory pictures at the Wikipedia article on tree traversal.
I have an one lecture slides says following:
To find middle element in AVL tree, I traverse elements in order until It reaches the moddile element. It takes O(N).
If I know correctly, in tree structure, finding element takes base 2 O(logn) since AVL is binary tree that always divided into 2 childs.
But why it says O(N)?
I am just trying to elaborate 'A. Mashreghi' comment.
Since, the tree under consideration is AVL tree - the guaranteed finding of element in O(log n) holds as log as you have the element(key) to find.
The problem is - you are trying to identify a middle element in the given data structure. As it is AVL tree (self balanced BST) in-order travel gives you elements in ascending order. You want to use this property to find the middle element.
Algorithm goes like - have a counter increment for every node traversed in-order and return # n/2th position. This sums to O(n/2) and hence the overall complexity O(n).
Being divided into 2 children does not guarantee perfect symmetry. For instance, consider the most unbalanced of all balanced binary trees: each right child has a depth one more than its corresponding left child.
In such a tree, the middle element will be somewhere down in the right branch's left branch's ...
You need to determine how many nodes N you have, then locate the N/2th largest node. This is not O(log N) process.
Is there a way to do this? In other words, is there a way to know which tree has the smaller/larger height without knowing the height themselves? That way we would know which tree we need to pick the room from. Note that all the elements in the first tree are smaller than all the elements in the second tree.
h1 and h2 are the heights of the trees
the height of the merged new tree needs to be max(h1 , h2) + 1
Remove the least element from T2 and make it the root of a tree with T1 as the left subtree and the rest of T2 as the right. max(h1, h2) is just a convenient bound on the length of T2's left spine. We can actually get an algorithm that runs in time O(min(h1, h2)) by traversing T1's right spine and T2's left spine in a dovetailed fashion and then executing the appropriate mirror image of this algorithm when the greatest of T1/least of T2 is discovered.
I think you have to explicitly find the heights of the trees. Without more specific info, there just isn't anything else you can do. You can find the height of a tree recursively in O(n) time where n is the number of nodes in the tree.
Given a binary tree I need to implement a method findAllElements(k) to find all the elements in the tree with a key equal to k.
The idea I had is the first time you come across an element with key k. All the elements with the same key should be either in the left child's right subtree or the right child's left subtree. But I was told this may not be the case?
I just need to find a way to implement an algorithm. So pseudo code is needed.
I probably should have added this sorry. But the implementation is that the left subtree contains keys less than or equal to the key at the root and the right subtree contains keys greater than or equal to the key at the root.
It depends on your tree implementation, by binary tree I assume you mean binary search tree, and you use operator< to compare the key. That is, The left subtree of a node contains only nodes with keys less(<) than the node's key, and the right subtree of a node contains only nodes with keys not less(!<) than the node's key.
e.g.
7
/ \
4 7
/ \
6 8
If there is multi equal keys in the tree, do this
k < current_node_key, search left subtree
k > current_node_key, search right subtree
k == current_node_key, record current node , then search right tree
Look at the current node. If its key is higher than k, search the left subtree. If it is lower, search the right subtree. If it is equal, search both left and right subtrees (and also include the current node in the results).
Do that recursively starting from the root node.
Thought I'd come back and explain what the result should have been after conversing with me teacher. So if you perform a method findElement(k) that will find an element with the key equal to k, the element it find should be the element highest in the tree with key k (let's denote this element V).
Then from this element V, other elements the contain a key=k will either be in the left child subtree (particularly all the way to the right) or the right child subtree (particularly all the way to the left). So for the left child keep going to the next nodes right child until an element with key=k is found...now... every element in the subtree with this node as its root must have a key=k (this is the part i didn't recognize at first) thus ANY kind of traversal of this full subtree can be done to find and store all the elements in this subtree (visiting every node in it). This type of thing must be repeated for the right child but visiting every left child until an element with a key=k is found. then the subtree with this element as its root has all the other elements with key=k in it and they can be found by one again fully traversing this subtree.
That is just a word description of it obviously, sorry for the length, and any confusion. Hopefully this will help anyone else trying to solve a similar problem.
Given a set of values, it's possible for there to be many different possible binary search trees that can be formed from those values. For example, for the values 1, 2, and 3, there are five BSTs we can make from those values:
1 1 2 3 3
\ \ / \ / /
2 3 1 3 1 2
\ / \ /
3 2 2 1
Many data structures that are based on balanced binary search trees use tree rotations as a primitive for reshaping a BST without breaking the required binary search tree invariants. Tree rotations can be used to pull a node up above its parent, as shown here:
rotate
u right v
/ \ -----> / \
v C A u
/ \ <----- / \
A B rotate B C
left
Given a BST containing a set of values, is it always possible to convert that BST into any arbitrary other BST for the same set of values? For example, could we convert between any of the five BSTs above into any of the other BSTs just by using tree rotations?
The answer to your question depends on whether you are allowed to have equal values in the BST that can appear different from one another. For example, if your BST stores key/value pairs, then it is not always possible to turn one BST for those key/value pairs into a different BST for the same key/value pairs.
The reason for this is that the inorder traversal of the nodes in a BST remains the same regardless of how many tree rotations are performed. As a result, it's not possible to convert from one BST to another if the inorder traversal of the nodes would come out differently. As a very simple case, suppose you have a BST holding two copies of the number 1, each of which is annotated with a different value (say, A or B). In that case, there is no way to turn these two trees into one another using tree rotations:
1:a 1:b
\ \
1:b 1:a
You can check this by brute-forcing the (very small!) set of possible trees you can make with the rotations. However, it suffices to note that an inorder traversal of the first tree gives 1:a, 1:b and an inorder traversal of the second tree gives 1:b, 1:a. Consequently, no number of rotations will suffice to convert between the trees.
On the other hand, if all the values are different, then it is always possible to convert between two BSTs by applying the right number of tree rotations. I'll prove this using an inductive argument on the number of nodes.
As a simple base case, if there are no nodes in the tree, there is only one possible BST holding those nodes: the empty tree. Therefore, it's always possible to convert between two trees with zero nodes in them, since the start and end tree must always be the same.
For the inductive step, let's assume that for any two BSTs of 0, 1, 2, .., n nodes with the same values, that it's always possible to convert from one BST to another using rotations. We'll prove that given any two BSTs made from the same n + 1 values, it's always possible to convert the first tree to the second.
To do this, we'll start off by making a key observation. Given any node in a BST, it is always possible to apply tree rotations to pull that node up to the root of the tree. To do this, we can apply this algorithm:
while (target node is not the root) {
if (node is a left child) {
apply a right rotation to the node and its parent;
} else {
apply a left rotation to the node and its parent;
}
}
The reason that this works is that every time a node is rotated with its parent, its height increases by one. As a result, after applying sufficiently many rotations of the above forms, we can get the root up to the top of the tree.
This now gives us a very straightforward recursive algorithm we can use to reshape any one BST into another BST using rotations. The idea is as follows. First, look at the root node of the second tree. Find that node in the first tree (this is pretty easy, since it's a BST!), then use the above algorithm to pull it up to the root of the tree. At this point, we have turned the first tree into a tree with the following properties:
The first tree's root node is the root node of the second tree.
The first tree's right subtree contains the same nodes as the second tree's right subtree, but possibly with a different shape.
The first tree's left subtree contains the same nodes as the second tree's left subtree, but possibly with a different shape.
Consequently, we could then recursively apply this same algorithm to make the left subtree have the same shape as the left subtree of the second tree and to make the right subtree have the same shape as the right subtree of the second tree. Since these left and right subtrees must have strictly no more than n nodes each, by our inductive hypothesis we know that it's always possible to do this, and so the algorithm will work as intended.
To summarize, the algorithm works as follows:
If the two trees are empty, we are done.
Find the root node of the second tree in the first tree.
Apply rotations to bring that node up to the root.
Recursively reshape the left subtree of the first tree to have the same shape as the left subtree of the second tree.
Recursively reshape the right subtree of the first tree to have the same shape as the right subtree of the second tree.
To analyze the runtime of this algorithm, note that applying steps 1 - 3 requires at most O(h) steps, where h is the height of the first tree. Every node will be brought up to the root of some subtree exactly once, so we do this a total of O(n) times. Since the height of an n-node tree is never greater than O(n), this means that the algorithm takes at most O(n2) time to complete. It's possible that it will do a lot better (for example, if the two trees already have the same shape, then this runs in time O(n)), but this gives a nice worst-case bound.
Hope this helps!
For binary search trees this can actually be done in O(n).
Any tree can be "straightened out", ie put into a form in which all nodes are either the root or a left child.
This form is unique (reading down from root gives the ordering of the elements)
A tree is straightened out as follows:
For any right child, perform a left rotation about itself. This decreases the number of right children by 1, so the tree is straightened out in O(n) rotations.
If A can be straightened out into S in O(n) rotations, and B into S in O(n) rotations, then since rotations are reversible one can turn A -> S -> B in O(n) rotations.