2

I am trying to write a method for a binary search tree class to modify a balanced a normal tree which makes the tree have nodes only on one side.

From the order in which the elements are appearing in the unbalanced tree, to me there seems to be some relation between the In Order Traversal (Left, Middle, Right).

7
  • If you were given this assignment in class, I would expect that your instructor would've presented sufficient material, in advance, that's needed to complete this assignment; so the proper party to whom your questions should be advanced would be your instructor. Commented Nov 25, 2016 at 17:29
  • This is not a class assignment @SamVarshavchik, I am practicing Commented Nov 25, 2016 at 17:30
  • thanks @melpomene made that edit in the question Commented Nov 25, 2016 at 17:38
  • @SamVarshavchik Don't blame the instructor unless you have more information than we do. Commented Nov 25, 2016 at 17:44
  • @nicomp but it's fun! Commented Nov 25, 2016 at 18:04

2 Answers 2

2
void unbalance(Node **pp) {
    Node *p;
    while ((p = *pp)) {
        if (!p->left) {
            pp = &p->right;
        } else {
            Node *tmp = p->left->right;
            *pp = p->left;
            p->left->right = p;
            p->left = tmp;
        }
    }
}

...
Node *tree = ...;
unbalance(&tree);

This code is based on @templatetypedef's answer. It uses a pointer-to-pointer to avoid orphaning nodes (which also gets rid of finalRoot, whose only purpose is to avoid orphaning the original root passed in by the caller).

The idea is to walk down the tree along the rightmost branch. If there never are any left children, we only enter the first part of the if statement and exit without doing anything.

The else part is used when there is a left subtree. In that case we rotate the tree right. animated tree rotation

This has the effect of pulling one node up from the left subtree, i.e. our left subtree is now one node smaller. We repeat this until the left subtree is completely gone, at which point we enter the first part of the if again. Then we go down to the remaining right subtree and repeat the whole thing.

Pointer-to-pointers are often useful when modifying the structure of a linked list or tree. They let us work directly on the original pointers instead of making a copy of them. In this case, the assignment *pp = ... modifies a pointer elsewhere (the one pp is pointing to). This is either the original root (as passed in by the caller), or one of the right pointers within the tree (as set by pp = &p->right). This repointing is needed because the tree rotation in the else branch shuffles nodes around, so what used to be the root of a subtree gets moved further down, and the former left node gets pulled up to become the new root. We update *pp to preserve the invariant that the pointer higher up (either the tree variable in our caller or the right member of our parent node) points to the (new) root of the subtree.

Sign up to request clarification or add additional context in comments.

2 Comments

Thank you so much! This worked for the implementation that I was working with. Could you elaborate a little more on your use of the Node ** and how that helped? Is it because it is a reference to begin with and thus the changes are actually reflected? Could you also tell me what software/website you used for the animation?
@C2K I stole the animation from en.wikipedia.org/wiki/Tree_rotation (actually I just hotlinked it). It was created by "Tar-Elessar" and is under CC BY-SA 4.0.
1

One way to do this iteratively is to use tree rotations. The idea goes something like this:

  1. If the root node has a left child, rotate the root node right.
  2. Otherwise, the root node has no left child. Descend right and repeat this process.

In pseudocode:

Node finalRoot = null;
while (currNode != null) {
    if (currNode.left != null) {
        currNode = rotateRight(currNode);
    } else {
        if (finalRoot == null) finalRoot = currNode;
        currNode = currNode.right;
    }
}
return finalRoot;

This approach is adapted from the Day-Stout-Warren algorithm, which does this as its first step. It runs in time O(n).

7 Comments

Something was bothering me about the if (finalRoot == null) part: It's inelegant. And now I know why: It's treating the original root pointer (initial value of currNode) differently from all the other pointers currNode steps through (all the .right children). I think this means the pseudocode is broken: It doesn't update currNode.right, so the tree loses access to all the left nodes that were rotated in.
So finalRoot is not actually the initial tree root - it ends up being the rightmost node in the tree (trace through the code on a sample tree). Also, can you elaborate on the bit about losing nodes? When we descend into the right subtree we know there is no left subtree to lose (and that everything above us is in a giant spine.)
Here's a diagram of what I'm talking about: i.imgur.com/P1qPc1m.png - does this make sense? The point is that A.right never gets updated and so keeps pointing to B, even when B is no longer the root of its subtree.
@melpomene I did try running the pseudocode and it seems I lose one node in my tree of 3...will look into it more and update when I have a working solution.
@templatetypedef Is this a good reading to understand the Day-Stout-Warren algorithm? penguin.ewu.edu/~trolfe/DSWpaper I am trying to understand the right rotate
|

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.