31

Assume that I have two AVL trees and that each element from the first tree is smaller then any element from the second tree. What is the most efficient way to concatenate them into one single AVL tree? I've searched everywhere but haven't found anything useful.

liviucmg
  • 1,290
  • 2
  • 17
  • 25

4 Answers4

33

Assuming you may destroy the input trees:

  1. remove the rightmost element for the left tree, and use it to construct a new root node, whose left child is the left tree, and whose right child is the right tree: O(log n)
  2. determine and set that node's balance factor: O(log n). In (temporary) violation of the invariant, the balance factor may be outside the range {-1, 0, 1}
  3. rotate to get the balance factor back into range: O(log n) rotations: O(log n)

Thus, the entire operation can be performed in O(log n).

Edit: On second thought, it is easier to reason about the rotations in the following algorithm. It is also quite likely faster:

  1. Determine the height of both trees: O(log n).
    Assuming that the right tree is taller (the other case is symmetric):
  2. remove the rightmost element from the left tree (rotating and adjusting its computed height if necessary). Let n be that element. O(log n)
  3. In the right tree, navigate left until you reach a node whose subtree is at most one 1 taller than left. Let r be that node. O(log n)
  4. replace that node with a new node with value n, and subtrees left and r. O(1)
    By construction, the new node is AVL-balanced, and its subtree 1 taller than r.

  5. increment its parent's balance accordingly. O(1)

  6. and rebalance like you would after inserting. O(log n)
meriton
  • 61,876
  • 13
  • 96
  • 163
  • 2
    Are you quite sure? (You might easily be correct, but I'm just curious.) Suppose the left tree is much smaller than the right tree ie, much shallower. Why will O(log n) rotations restore the AVL property? How do you decide where to rotate? – Dale Hagglund Jan 10 '10 at 14:57
  • 1
    What Dale says: the usual choice of rotations for an AVL tree allows an imbalance of size 2 to be corrected back into range [-1,1] with O(log n) rotations. You need a new scheme for choosing rotations in order to correct an arbitrary imbalance. Since that's the part of AVL trees that I can never remember, and have to look up every time, I expect that even if the choice of rotations is obvious, it's not obvious to me :-) – Steve Jessop Jan 10 '10 at 15:21
  • 1
    Good points. I found it easier to prove another algorithm (c.f. my edited answer). – meriton Jan 10 '10 at 16:36
  • Thanks for your answer. It worked! One thing, though, at point 3 you need to navigate left until you reach a node with 'node.height <= left_tree.height + 1'. – liviucmg Jan 11 '10 at 16:21
  • 5
    It took me some time to parse what you meant by "replace that node with (left,n,r)". Consider rephrasing. – ripper234 Jan 18 '11 at 08:08
  • @meriton, I agree with ripper234 – Jackson Tale Mar 07 '12 at 17:42
  • Very nice answer indeed. – axel22 Jul 30 '14 at 12:43
  • 4
    I believe your answer has a wrong detail. In the third step of your last algorithm you **navigate left until you reach the node whose sub tree has the same height as the left tree. Let r be that node**. This is not always possible, [counterexample here](http://picpaste.com/Untitled-3FBKEgQX.png). The right way to do this step is two find for a subtree with height `h` or `h+1` where `h` is the height of the left tree. – rareyesdev Oct 29 '14 at 18:23
  • For step 2. should you rebalance the nodes in the left tree after removing n? – Lukazoid Jul 06 '17 at 19:47
  • 1
    Yes, of course; otherwise the resulting tree would not be AVL-balanced. Have edited to clarify. While I was at it, I also incorporated @agarwaen's feedback. – meriton Jul 06 '17 at 21:06
  • It's a bit late considering the age of the post but could someone explain step 6. I understand how you would rebalance after inserting a node but how would it work after inserting a sub tree? – Geralt Jan 23 '20 at 22:22
5

One ultra simple solution (that works without any assumptions in the relations between the trees) is this:

  1. Do a merge sort of both trees into one merged array (concurrently iterate both trees).
  2. Build an AVL tree from the array - take the middle element to be the root, and apply recursively to left and right halves.

Both steps are O(n). The major issue with it is that it takes O(n) extra space.

ripper234
  • 202,011
  • 255
  • 600
  • 878
4

The best solution I read to this problem can be found here. Is very close to meriton's answer if you correct this issue:

In the third step of the algorithm navigates left until you reach the node whose sub tree has the same height as the left tree. This is not always possible, (see counterexample image). The right way to do this step is two find for a subtree with height h or h+1 where h is the height of the left tree counterexample

rareyesdev
  • 1,929
  • 22
  • 39
1

I suspect that you'll just have to walk one tree (hopefully the smaller) and individually add each of it's elements to the other tree. The AVL insert/delete operations are not designed to handle adding a whole subtree at a time.

Dale Hagglund
  • 14,948
  • 3
  • 26
  • 37
  • I have a feeling that it can be done linearly. Using the naïve approach takes O(n log n) time. – avakar Jan 10 '10 at 14:27
  • This takes O(n log n) -> much slower than meriton's solution – inspectorG4dget Feb 12 '10 at 15:45
  • 2
    @meriton's solution is indeed very nice, but it assumes (a) that every node in one tree is strictly less than every node in the other tree, (b) you have access to the raw avl tree data structures, and (c) can perform rotations directly on the tree nodes. If (a) doesn't hold, or the low level tree manipulations aren't available to you (most likely because you're using an avl library) then you may have to fall back on simply inserting each node from the smaller tree into the larger. – Dale Hagglund Feb 13 '10 at 04:15