views:

241

answers:

5

I got this question in an interview with amazon. I was asked to perform a depth first traversal of a tree, without using recursion or stack. I could use a parent pointer for each node, as a part of the structure, but nothing else other than that.(for ex, a "visited" variable" or anything). Please suggest me an algorithm.

+1  A: 

For a 'hacky' solution you can use the fact that pointers are usually 4 byte aligned (i.e, last two bits are 0) and use those two bits as your visited flag.

Moron
Clever, but if someone presented that in an interview I'd give them a pretty hard looking-over.
Borealid
@Bore: I would give the same look to someone asking such a silly question ;-)
Moron
@moron: i neva had an idea i could use a pointer that way. could you please elaborate how i can implement this?
wordwiz
Moron
+1  A: 

If you have a parent pointer, then you can unwind up the tree without a stack. The only other problem during unwind is "do I need to visit the other child(ren)?" This can be answered simply by comparing pointer values to work out if you've just returned from the left child or the right child (or generalise to N children).

EDIT

Pseudocode (untested):

p_last          = NULL;
p               = p_head;
descend         = true;

while (NULL != p)
{
    p_tmp = p;

    if (descend)
    {
        // ... Node processing here...

        if (0 == p->num_children)
        {
            // No children, so unwind
            p = p_parent;
            descend = false;
        }
        else
        {
            // Visit first child
            p = p->child[0];
        }
    }
    else
    {
        // Find the child we just visited
        for (i = 0; i < p->num_children; i++)
        {
            if (p_last == p->child[i])
            {
                break;
            }
        }
        if (i == num_children-1)
        {
            // Processed last child, so unwind
            p = p_parent;
        }
        else
        {
            // Visit next child
            p = p->p_child[i+1];
            descend = true;
        }
    }

    p_last = p_tmp;
}
Oli Charlesworth
Show some code? I'm not sure this actually works, because visiting the right child requires storage outside of the tree.
Borealid
Can't be bothered to think of the code! Every time you come back up to a node, compare the address of the child you just came from with the pointer for the right child. If they're equal, then continue to unwind, otherwise visit the right child. So not sure what you mean
Oli Charlesworth
This code doesn't work. Some nodes won't get visited if the tree structure contains some nodes which only have right children in some places. Also, you don't mark where a node is handled - important, since your code is almost a top-down traversal.
Borealid
The question just says tree. Why are you assuming binary tree?
Moron
@Borealid: fair point, but as I said, "untested!". I don't think it breaks the principle (but I'll try to fix it).
Oli Charlesworth
@Moron: this would generalise pretty easily to arbitrary numbers of children.
Oli Charlesworth
How about this?
Oli Charlesworth
@Oli: Assuming that there is an ordering between children, comparing pointers works (you can get the 'next' child to start visiting based on the ordering). +1.
Moron
@Moron: If there's no ordering between children, then presumably it doesn't matter which order you traverse them in? The child pointers could equally well have been stored in a linked list.
Oli Charlesworth
@Oli: If there is no ordering, you might have to keep track of exactly how many children you have seen/left, which is probably against the constraints of the problem.
Moron
@Moron: You can just scan all the children.
caf
@caf: You can, but how do you know when to stop?
Moron
@Moron: When your `previous` pointer is equal to the last extant child - then you go back up to the parent.
caf
@caf: How do you know that it is the 'last' child? That is what I meant by ordering...
Moron
@Moron: Certainly it requires that the sequence in which the child nodes of a given node are seen be deterministic / repeatable, but that is not a particularly onerous condition. I thought you meant that they needed an externally defined ordering. (ie. as long as the child nodes are examined in the same sequence each time, it doesn't matter what that sequence is).
caf
+4  A: 

The parent pointer is actually all you need. The trick is to consume the tree as you traverse it.

Ugly pseudocode:

cur = treeroot;
while (1) { // Get to bottom of tree
    if (cur.hasLeft) {
        cur = left;
    } else if (cur.hasRight) {
        cur = right;
    } else {
        break;
}

// cur is now the bottom node

while (1) {
    doStuff(cur); // output cur, perform op on it, whatever
    if (!cur.hasParent) { // Done with traversal
        break;
    }
    prev = cur; // So we can tell which way we came up to the parent.
    cur = cur.parent;
    if (cur.hasLeft && cur.left == prev) { // Delete left child; it's done
       cur.hasLeft = false;
    } else if (cur.hasRight && cur.right == prev) { // Delete right child; it's done
       // Note: "else" not desirable if a node should not be able to have the same child in two spots
       cur.hasRight = false;
    }
    if (cur.hasLeft) { // Go all the way to the bottom of the left child
        cur = cur.left;
        while (1) {
            if (cur.hasLeft) {
                cur = cur.left;
            } else if (cur.hasRight) {
                cur = cur.right;
            } else {
                break;
            }
        }
    } else if (cur.hasRight) { // Go all the way to the bottom of the right child
        cur = cur.right;
        while (1) {
            if (cur.hasLeft) {
                cur = cur.left;
            } else if (cur.hasRight) {
                cur = cur.right;
            } else {
                break;
            }
        }
    }
}
Borealid
Where does the question say it is a binary tree? Or that you can destroy the tree or that there are flags like hasRight/hasLeft?
Moron
@Moron: this solution extends fine to arbitrary numbers of children, so long as there is a defined traversal order. The question doesn't say you can destroy the tree. It also doesn't say you can't. hasRight/hasLeft is shorthand for `.left == NULL`, or whatever. If you don't have a way to tell if a node has a left child, you **cannot** traverse the tree safely.
Borealid
@Bore: Frankly, if someone gave a solution to traverse the tree which destroys it, I would give them a hard looking-over ;-) Of course, I probably wouldn't be asking such a stupid question in the first place.
Moron
@Bore: thanks. this code seems to be working fine. and the interviewer did mention that i could assume the tree to be binary. sorry for not being specific about that part.n yes, i did give the interviewer a stern look, since he kept modifying the question till he ended up wid this n i cudnt answer anymore.i still wanna knw though, if i could perform this, without having to destroy the tree.
wordwiz
In my opinion, destruction of the tree counts as the "or anything" in *for ex, a "visited" variable" or anything*.
R..
A: 

Here is my proposition for a binary tree. The language is C#. It's a private method of a binarTree class that holds ints

private Node DFS(int value)
{
    Node current = this.root;
    if(current.data == value) return current;

    while(true)
    {
        //go down-left as far as possible
        while(current.leftChild != null)
        {
            current = current.leftChild;
            if(current.data == value) return current;
        }
        //leftChild is null, but maybe I can go right from here
        while(current.leftChild == null && current.rightChild != null)
        {
            current = current.rightChild;
            if(current.data == value) return current;
        }
        if(current.leftChild == null && current.rightChild == null)
        {
            // Ok, I got to a leaf. Now I have to get back to the last "crossroads"
            // I went down-left from, but there was also down-right option
            while(current.parent != null &&
                  (current == current.parent.rightChild ||
                   current.parent.rightChild == null))
            {
                current = current.parent;
            }
            if(current.parent == null) return null;

            // Ok If I'm here, that means I found the crossroads mentioned before
            // I'll go down-right once and then I should try down-left again
            current = current.parent.rightChild;
            if(current.data == value) return current;
        }
    }
}

If it's not a binary tree, then things get more complicated of course, but the logic is similar. Go down to the leaf taking the first possible child at each level. Once you reach a leaf you go up. Every time you look up at a parent you check if the child you are supposed to come from was the last in parent's list. If not, you take next child and go down again. If yes, you go up and check the following parent. If you get back to the root you searched the whole tree.

EDIT

Ok search and traversals are distinct things, my bad. Here is some modified code for traversals

preorder:

public void preorderTraversal()
{
    Node current = this.root;
    Console.WriteLine(" item: {0} ", current.data);
    while(true)
    {
        while(current.leftChild != null)
        {
            current = current.leftChild;
            Console.WriteLine(" item: {0} ", current.data);
        }
        while(current.leftChild == null && current.rightChild != null)
        {
            current = current.rightChild;
            Console.WriteLine(" item: {0} ", current.data);
        }
        if(current.leftChild == null && current.rightChild == null)
        {
            while(current.parent != null &&
                  (current == current.parent.rightChild ||
                   current.parent.rightChild == null))
            {
                current = current.parent;
            }
            if(current.parent == null)
            {
                return;
            }
            else
            {
                current = current.parent.rightChild;
                Console.WriteLine(" item: {0} ", current.data);
            }
        }
    }
}

inorder:

public void inorderTraversal()
{
    Node current = this.root;
    while(true)
    {
        while(current.leftChild != null)
        {
            current = current.leftChild;
        }
        Console.WriteLine(" item: {0} ", current.data);
        while(current.leftChild == null && current.rightChild != null)
        {
            current = current.rightChild;
            Console.WriteLine(" item: {0} ", current.data);
        }
        if(current.leftChild == null && current.rightChild == null)
        {
            while(current.parent != null &&
                  (current == current.parent.rightChild ||
                   current.parent.rightChild == null))
            {
                    current = current.parent;
                    if(current.rightChild == null)
                    {
                        Console.WriteLine(" item: {0} ", current.data);
                    }
            }
            if(current.parent == null)
            {
                return;
            }
            else
            {
                Console.WriteLine(" item: {0} ", current.parent.data);
                current = current.parent.rightChild;
            }
        }
    }
}

postorder:

public void postorderTraversal()
{
    Node current = this.root;
    while(true)
    {
        while(true)
        {
            if(current.leftChild != null)
            {
                current = current.leftChild;
            }
            else if(current.rightChild != null)
            {
                current = current.rightChild;
            }
            else
            {
                break;
            }
        }
        while(current.parent != null &&
              (current == current.parent.rightChild ||
               current.parent.rightChild == null))
        {
            Console.WriteLine(" item: {0} ", current.data);
            current = current.parent;
        }
        Console.WriteLine(" item: {0} ", current.data);
        if(current.parent == null)
        {
            return;
        }
        else
        {
            current = current.parent.rightChild;
        }
    }
}
Maciej Hehl
@maciej: it is a binary tree, but its not a searching opeartion. i was asked to traverse the tree. i couldnt really get, what this "value" thing is all about.
wordwiz
@wordwiz Ok my bad. I read depth - first and that triggered search in my head instantly. For traversal I'd rather expect pre, post or inorder. Changing my code to get preorder traversal is trivial. Just replace checks if value was found with some action performed on every visit. Inorder and postorder traversals require more changes.
Maciej Hehl
A: 

This shows how I'd do it in C. It demonstrates both pre-order and post-order traversal, and is generalised for 0..N children of each node.

struct node {
    struct node *parent;
    struct node *child[NCHILD];
};

void dfs(struct node *head, void (*visit_preorder)(struct node *n), void (*visit_postorder)(struct node *n))
{
    struct node *current = head;
    struct node *prev = head;

    while (current)
    {
        int i;

        /* preorder traversal */
        if (prev == current->parent)
            visit_preorder(current);

        /* Find first child to visit */
        for (i = NCHILD; i > 0; i--)
        {
            if (prev == current->child[i - 1])
                break;
        }
        while (i < NCHILD && current->child[i] == NULL)
            i++;

        prev = current;

        if (i < NCHILD)
        {
            /* Descend to current->child[i] */
            current = current->child[i];
        }
        else
        {
            /* Postorder traversal */
            visit_postorder(current);

            /* Ascend back to parent */
            current = current->parent;
        }
    }
}
caf