1

I know about algorithm to level order traversal of a tree. (I think everybody knows about that) That algorithm uses queue to store the nodes of the tree. Is there a algorithm that do not uses additional memory? That algorithm must not use recursion (in that way we are using stack). Note, that tree is given in left-child right-sibling representation. No additional pointers are allowed.
The structures in C, for the tree are:

struct node {
int data;
struct node *left-child;
struct node *right-sibling;
}

Tree is represented with a pointer to the root node. Of course, root cannot have right-sibling.

  • Well, if the tree was a complete tree represented in an array, there'd hardly be anything to do: please mention *how* the tree is represented.. – greybeard Jan 03 '20 at 19:04
  • (Great, a non-binary tree for a change.) See also: [Morris inorder tree traversal explained](https://stackoverflow.com/a/5506601) – greybeard Jan 03 '20 at 19:26
  • @greybeard See the coment I have wrote in the answer from Rudresh Dixit –  Jan 03 '20 at 19:45
  • Is it allowed to modify the tree pointers during the traversal? If so, must the tree be restored to its original state once the traversal is completed? – trincot Jan 03 '20 at 21:43

3 Answers3

1

One way could be to use the right-sibling pointers which are null, to make all nodes siblings of each other (temporarily).

You could use a slow and fast pointer. The fast one would always be at the last sibling (that has a null pointer as right-sibling). The left-child pointer of the slow node would then be copied into that right-sibling, after which the fast pointer runs further to the end again. The slow pointer goes one step to the right and the same repeats. When the slow pointer also reaches the end, all nodes will be siblings. Either the slow or fast pointer can be used to output the values in the level-order. This will do the job, but the tree will have been destroyed as a consequence.

To restore the tree, I would suggest that during the above process the direction of all sibling edges is reversed. This means you need to have another pointer that lags behind the slow pointer. This will allow the reversal to be performed between those two. This is a bit obscure, because the right-sibling will then in fact point to something that is mostly a left sibling.

After the above process, the pointers will be at the end of the node list, but because we have reversed the sibling edges, we can also walk back and reverse the edges again. One difficulty is to know which sibling pointers should become null again (for when a node was originally a right most child). This can be done by having again a fast pointer moving ahead (in the left direction) to find nodes that have child. If the pointer that lags behind the slow pointer hits such a child, we know that the slow pointer's node should get a null pointer as right-sibling. When this fix is applied, the fast pointer should again run ahead to find yet another parent node, ...etc.

Note that the left-child pointers are not altered by this algorithm.

So, in total this solution uses three pointers and the structure of the tree itself.

Here is a sample tree I have used in an implementation below:

          1
         /
        2 ------------ 3 ---------4
       /              /          /
      5 -- 6 -- 7    8 -- 9     10 -- 11 -- 12 -- 13
          /                           /
        14 -- 15 -- 16              17 -- 18 -- 19

Implementation in JavaScript -- runnable snippet:

function * traverse(node) {
    let lead = node; // ...walks somewhere ahead of node
    let lag = null; // ... always follows one step behind node
    while (node) {
        yield node.data; // output
        lead.rightSibling = node.leftChild;
        while (lead.rightSibling) lead = lead.rightSibling;
        // rotate: point node to next right-sibling, and reverse direction of sibling edge
        [node.rightSibling, lag, node] = [lag, node, node.rightSibling]
    }
    
    // Restore tree
    lead = node = lag.rightSibling; // backwards
    lag.rightSibling = null;
    while (lead !== null && lead.leftChild === null) lead = lead.rightSibling; // actually going left!
    while (node) {
        if (lead !== null && lead.leftChild === lag) {
            // When lag is the leftChild of some node (lead), then lag should not be the target of a rightSibling
            [node.rightSibling, lag, node] = [null, node, node.rightSibling];
            // Find previous parent
            lead = lead.rightSibling;
            while (lead !== null && lead.leftChild === null) lead = lead.rightSibling; // actually going left!
        } else {
            // walk back and restore sibling pointers
            [node.rightSibling, lag, node] = [lag, node, node.rightSibling];
        }
    }
}

// Create node, given its data and child nodes
function Node(data, ...children) {
    // Link the children as siblings
    if (children.length > 1) children.reduceRight((a, b) => (b.rightSibling = a, b))
    // Create the node itself. For now, without any siblings
    return {
        data,
        leftChild: children.length ? children[0] : null,
        rightSibling: null
    };
}

// Example tree
let tree = Node(1, 
    Node(2, 
        Node(5), Node(6,
            Node(14), Node(15), Node(16)
        ), Node(7)
    ), Node(3,
        Node(8), Node(9)
    ), Node(4,
        Node(10), Node(11,
            Node(17), Node(18), Node(19)
        ), Node(12), Node(13)
    )
);

// Apply the algorithm and output the yielded values     
console.log(...traverse(tree)); 

Version in C

I am not so fluent in C, but I think this should do it:

#include <stdio.h>
#include <stdlib.h>

// define Node as a pointer to a node struct
typedef struct node {
    int data;
    struct node *leftChild;
    struct node *rightSibling;
} * Node;

// Some helper functions to ease the creation of a tree:
Node sibling(Node leftSibling, Node rightSibling) {
    leftSibling->rightSibling = rightSibling;
    return leftSibling;
}

Node parent(Node parent, Node child) {
    parent->leftChild = child;
    return parent;
}

Node create(int data) {
    Node node = malloc(sizeof(struct node));
    node->data = data;
    return node;
}
// end - helper functions

void traverse(Node node) {
    Node lead = node; // ...walks somewhere ahead of node
    Node lag = NULL; // ... always follows one step behind node
    while (node) {
        printf("%d\n", node->data); // output
        lead->rightSibling = node->leftChild;
        while (lead->rightSibling) lead = lead->rightSibling;
        // rotate: point node to next right-sibling, and reverse direction of sibling edge
        Node temp = node->rightSibling;
        node->rightSibling = lag;
        lag = node;
        node = temp;
    }

    // Restore tree
    lead = node = lag->rightSibling; // backwards
    lag->rightSibling = NULL;
    while (lead != NULL && lead->leftChild == NULL) lead = lead->rightSibling; // actually going left!
    while (node != NULL) {
        if (lead != NULL && lead->leftChild == lag) {
            // When lag is the leftChild of some node (lead), then lag should not be the target of a rightSibling
            lag = node;
            node = node->rightSibling;
            lag->rightSibling = NULL;
            // Find previous parent
            lead = lead->rightSibling;
            while (lead != NULL && lead->leftChild == NULL) lead = lead->rightSibling; // actually going left!
        } else {
            // walk back and restore sibling pointers
            Node temp = node->rightSibling;
            node->rightSibling = lag;
            lag = node;
            node = temp;
        }
    }
}

int main(void) {
    // Create the example tree
    Node root = parent(create(1), 
        sibling(parent(create(2), 
            sibling(create(5), sibling(parent(create(6),
                sibling(create(14), sibling(create(15), create(16)))
            ), create(7)))
        ), sibling(parent(create(3),
            sibling(create(8), create(9))
        ), parent(create(4),
            sibling(create(10), sibling(parent(create(11),
                sibling(create(17), sibling(create(18), create(19)))
            ), sibling(create(12), create(13))))
        )))
    );
    traverse(root);
    return 0;
}

To print the tree in a very basic format, you can use this function:

void printTree(Node node, int indent) {
    if (!node) return;
    for (int i = 0; i < indent; i++) printf("  ");
    printf("%d\n", node->data);
    printTree(node->leftChild, indent+1);
    printTree(node->rightSibling, indent);
}

This will help to verify that indeed the tree is the same before and after the traversal.

trincot
  • 317,000
  • 35
  • 244
  • 286
  • This is what I need. I have one question. Is the slow pointer one level up to the fast pointer? Can you rewrite algorithm in C, I am not so good in JavaScript. Thanks. –  Jan 06 '20 at 11:48
  • The term "level" becomes a bit ambiguous as the changes made to the sibling edges make the graph temporarily cyclic (a node's child also becomes its sibling). But at all stages the slow and fast nodes are also) siblings. In fact, the aim is to make *all* nodes siblings (without destroying the existing child relationships). I will spend some time to convert this to C. – trincot Jan 06 '20 at 12:49
  • Thanks. I have understood the first part of the algorithm, where the tree is "flattened" and printed, but I cannot understand the second part where the tree is restored. I will have to read it tomorrow, when I will be fresh. Anyway, thanks again. –  Jan 06 '20 at 18:54
  • For understanding the second part make sure to realise that `rightSibling` is at that moment pointing to the *left*. Secondly, the `leftChild` links will still point to nodes that are temporarily linked as siblings. To restore the `rightSibling` links we need to (1) reverse them again, and (2) some must be set to NULL. The rule must be that a node cannot ever be both a `leftChild` and a `rightSibling` at the same time. So when we detect such a node, the `rightSibling` pointer *to* that node must be set to `NULL`. – trincot Jan 06 '20 at 19:34
0

If you can store an extra next pointer in each node of the tree which points to the next node in level order for each level, then you can do the level order traversal in constant space.

Shashank V
  • 10,007
  • 2
  • 25
  • 41
  • I didn't mentioned that tree is given in left-child right-sibling representation, no additional pointers are allowed. –  Jan 03 '20 at 17:15
0

You can apply Morris level order traversal if you want to traverse your tree in constant space.You can refer here and here.

Rudresh Dixit
  • 320
  • 1
  • 4
  • 15
  • That is helpful, but I need some guidelines. I don't know how to adapt that algorithm (which is for binary trees ) for ordinary trees. Every (ordinary) tree can be represented with binary tree, but the level order of the corresponding binary tree is not the same as the level order of the (ordinary) tree. –  Jan 03 '20 at 19:35