Sunday, March 5, 2017

CLRS Exercise 12.2-3

12.2-3
Write the TREE-PREDECESSOR procedure.

If a node x has a left child, its predecessor is the rightmost node of left sub-tree. Otherwise, the predecessor is the lowest ancestor of x whose right child is an ancestor of x.
TREE-PREDECESSOR(x)
        if x.left != NIL
                return TREE-MAXIMUM(x->left)
        p = x.p
        while p != NIL and x != p.right
                x = p
                p = p.p
        return p

CLRS Exercise 12.2-2

12.2-2
Write recursive versions of TREE-MINIMUM and TREE-MAXIMUM.

Recursive version of TREE-MINIMUM:
TREE-MINIMUM-RECURSIVE(x)
        if x.left == NIL
                return x
        return TREE-MINIMUM-RECURSIVE(x.left)

Recursive version of TREE-MAXIMUM:
TREE-MAXIMUM-RECURSIVE(x)
        if x.right == NIL
                return x
        return TREE-MAXIMUM-RECURSIVE(x.right)

CLRS Exercise 12.1-4

12.1-4
Give recursive algorithms that perform preorder and postorder tree walks in $\Theta(n)$ time on a tree of $n$ nodes.

Preorder traversal:
PREORDER-TREE-WALK(x)
        if (x != NIL)
                print x.key
                PREORDER-TREE-WALK(x.left)
                PREORDER-TREE-WALK(x.right)
Postorder traversal:
POSTORDER-TREE-WALK(x)
        if (x != NIL)
                PREORDER-TREE-WALK(x.left)
                PREORDER-TREE-WALK(x.right)
                print x.key

CLRS Exercise 12.1-3

12.1-3
Give a nonrecursive algorithm that performs an inorder tree walk. (Hint: An easy solution uses a stack as an auxiliary data structure. A more complicated, but elegant, solution uses no stack but assumes that we can test two pointers for equality.)

Following code snippet is implementing the inorder traversal in recursive manner:
void inorder_tree_walk(struct node *t)
{
        if (t != NULL) {
                inorder_tree_walk(t->left);
                process(t);
                inorder_tree_walk(t->right);
        }
}
There are two recursive calls to inorder_tree_walk() and both can be eliminated. First, we can remove the second one at line 6 by using tail call elimination technique as following:
void inorder_tree_walk(struct node *t)
{
start:  if (t == NULL) goto end;
        inorder_tree_walk(t->left);
        process(t);
        t = t->right; goto start;
end:
}
The first call also can be removed by introducing a stack for storing the current context.
void inorder_tree_walk(struct node *t)
{
        stack<struct node *> s;
start:  if (t == NULL) goto pop;
        s.push(t); t = t.left; goto start;
pop:    if (s.empty()) goto end;
        t = s.pop();
        process(t);
        t = t->right; goto start;
end:
}
Now that the recursive calls are removed, code can be further simplified by removing dirty goto statements. The code between the first if statement and the first goto start is nothing more than a while loop. The label end can also be removed.
void inorder_tree_walk(struct node *t)
{
        stack<struct node *> s;
start:  while (t != NULL) {
                s.push(t);
                t = t.left;
        }
        if (!s.empty()) {
                t = s.pop();
                process(t);
                t = t->right;
                goto start;
        }
}
Looking at the overall flow of the program, the remaining goto statement can be converted into an outer while loop. Therefore, we finally get the following code:
void inorder_tree_walk(struct node *t)
{
        stack<struct node *> s;
        while (1) {
                while (t != NULL) {
                        s.push(t);
                        t = t.left;
                }

                if (s.empty()) break;

                t = s.pop();
                process(t);
                t = t->right;
        }
}
As mentioned in the problem statement, there exists another solution without using either stack or recursion. It is called as Morris inorder traversal, which uses the threaded binary tree.
void morris_inorder_walk_tree(struct node *t)
{
        while (t != NULL) {
                if (t->left == NULL) {
                        /* no more left child, process it and go right */
                        process(t);
                        t = t->right;
                } else {
                        /*
                         * before going to the left, make the right-most node
                         * of left child point to the current. Not only that,
                         * we should check if we already visited the left to
                         * prevent getting stuck in an infinite loop.
                         */
                        struct node *p = t->left;
                        while (p->right != NULL && p->right != t)
                                p = p->right;

                        if (p->right == NULL) {
                                p->right = t;
                                t = t->left;
                        } else {
                                /* restore the pointer back */
                                p->right = NULL;
                                process(t);
                                t = t->right;
                        }
                }
        }
}

CLRS Exercise 12.1-2

12.1-2
What is the difference between the binary-search-tree property and the min-heap property (see page 153)? Can the min-heap property be used to print out the keys of an n-node tree in sorted order in $O(n)$ time? Show how, or explain why not.

In the Min-Heap, both child nodes are less than or equal to their parents, while in the binary-search-tree, left child node cannot be greater than its parent and right child node cannot be less than. Thus, in-order traversal for BST gives nodes in sorted order with $\Theta(n)$ time complexity, but in the Min-Heap we need $n$ times of EXTRACT-MIN followed by HEAPIFY operations to get the same result, which takes $O(n\log{}n)$ time.