views:

324

answers:

5

How to traverse each node of a tree efficiently without recursion in C (no C++)?

Suppose I have the following node structure of that tree:

struct Node
{
    struct Node* next;   /* sibling node linked list */
    struct Node* parent; /* parent of current node   */
    struct Node* child;  /* first child node         */
}
  • It's not homework.
  • I prefer depth first.
  • I prefer no additional data struct needed (such as stack).
  • I prefer the most efficient way in term of speed (not space).
  • You can change or add the member of Node struct to store additional information.
+8  A: 

Generally you'll make use of a your own stack data structure which stores a list of nodes (or queue if you want a level order traversal).

You start by pushing any given starting node onto the stack. Then you enter your main loop which continues until the stack is empty. After you pop each node from the stack you push on its next and child nodes if not empty.

Brian R. Bondy
That won't traverse down the tree as well though.
zebediah49
@zeb, yes it will
glowcoder
if I Implement my own stack using linked list, would it be more efficient than using recursive?
uray
@uray: I don't think the recursive function calls would be a bottleneck in your program. But if you had a huge huge huge tree then implementing your own may be more efficient because you can do things like caching part of the stack to disk yourself as you want.
Brian R. Bondy
is there any algorithm without using any iterative data structure (such as stack or queue), for example by modifying or add variable in that node structure?
uray
Read zebediah49's answer. The way the tree structure is designed here, the solution is easy.
R..
@uray: What you have is just a binary tree (except you consider left = child and right = next) so yes, but generally for trees with an arbitrary number of children, not that I can think of.
Brian R. Bondy
@Brian: Zebediah's answer works for arbitrary trees, not just binary trees.
Moron
A: 

You'd have to store it in an iterable list. a basic list with indexes will work. Then you just go from 0 to end looking at the data.

If you want to avoid recursion you need to hold onto a reference of each object within the tree.

+12  A: 

If you don't want to have to store anything, and are OK with a depth-first search:

process = TRUE;
while(pNode != null) {
    if(process) {
        //stuff
    }
    if(pNode->child != null && process) {
         pNode = pNode->child;
         process = true;
    } else if(pNode->next != null) {
         pNode = pNode->next;
         process = true;
    } else {
         pNode = pNode->parent;
         process = false;
    }
}

Will traverse the tree; process is to keep it from re-hitting parent nodes when it travels back up.

zebediah49
+1 for recognizing the way the linked list of sibling nodes works and not introducing lots of nonsense overhead like the high-scoring answer...
R..
... which is still a lot of higher-scoring ... - Reputation calls reputation...
ShinTakezou
+1. This looks right. I deleted my answer because of this :-) (I was using extra node info). For an explanation: This is the invariant: when visiting a node, if process is true, it means you are yet to traverse down that subtree. You only get back to a node after visiting its children when you are done with all of them, in which case process is false, and you now move to your sibling.
Moron
@ShinTakezou: Let us not jump to any conclusions. The question was edited to prohibit the stack _after_ Brian's answer was posted.
Moron
@Moron I intended no to jump to conclusion. I have not evaluated this better of that because of stack alone. This looks better (_to me_!!) since gives code to achieve the aim, given the struct. The other ans is good, but "generic"; this one seems to be more specific(O well, I hope he gave the struct at the beginning at least, and that he did not change it too later!!)
ShinTakezou
The edit was to my wording, not my coding. It originally read "based on Brian's answer", but Brian changed his, so it was no longer relevant
zebediah49
@ShinTakezou: Of course this answer is better. My point is the upvotes for Brian's answer need not be because of the Brian having high rep: I was talking about your "Reputation calls reputation" comment.
Moron
@Moron ah ok. It's just my opinion expressed on the template "X calls X" (like, "money calls money" and so on). I've observed it several time on SO: high rep guys _tend_ to have more "attention" (and so, more votes) than the others. Of course it happens also the high rep guys give the better ans. But when it is not so, they have many upvotes anyway (more than other good answers by guys with less reputation). Anyway, I don't want to stress too much this, since I've not done a serious statistical analysis and I can't say it more decisely before I do. Just, my opinion and impression.
ShinTakezou
@ShinTakezou: What you're seeing is not so much a "high rep phenomenon" but a "Fastest gun in the west" phenomenon which has been discussed a lot previously on meta. Also the answer I gave is valid and met the original expectations and is the general solution for trees (not specific to only binary trees).
Brian R. Bondy
@Brian R. Bondy Yes ur ans is good, I've already said that it is. I have to learn how to type faster and produce almost-correct-sentences in english faster:)
ShinTakezou
I think that there is the implied assumption here that pNode is initially at the root of the tree. I've not done a detailed analysis yet, but my instincts tell me that if pNode is initialized to an inner sibling node, the "older" siblings and their children will not be processed.
Ants
Not quite. It won't behave quite right through (it will do every node except for all of the parents of pNode). If you did want to be able to pass it a non-root node, you could just `while(pNode->parent != null) {pNode=pNode->parent;}` up to the root of the tree, and then process it from there.
zebediah49
+1  A: 

You can use the Pointer Reversal method. The downside is that you need to save some information inside the node, so it can't be used on a const data structure.

Tomer Vromen
A: 

This looks like an exercise I did in Engineering school 25 years ago. I think this is called the tree-envelope algorithm, since it plots the envelope of the tree.

I can't believe it is that simple. I must have made an oblivious mistake somewhere. Any mistake regardless, I believe the enveloping strategy is correct. If code is erroneous, just treat it as pseudo-code.

while current node exists{
  go down all the way until a leaf is reached;
  set current node = leaf node;
  visit the node (do whatever needs to be done with the node);

  get the next sibling to the current node;
  if no node next to the current{
    ascend the parentage trail until a higher parent has a next sibling;
  }
  set current node = found sibling node;
}

The code:

void traverse(Node* node){

  while(node!=null){
    while (node->child!=null){
      node = node->child;
    }

    visit(node);

    node = getNextParent(Node* node);
  }
}

/* ascend until reaches a non-null uncle or 
 * grand-uncle or ... grand-grand...uncle
 */
Node* getNextParent(Node* node){

  /* See if a next node exists
   * Otherwise, find a parentage node
   * that has a next node
   */
  while(node->next==null){
    node = node->parent;
    /* parent node is null means
     * tree traversal is completed
     */
    if (node==null)
      break;
  }

  node = node->next;

  return node;
}
Blessed Geek