Thread: How to iterate over a balanced binary tree in sequence? (inorder)

  1. #1
    Registered User
    Join Date
    Mar 2018
    Posts
    1

    How to iterate over a balanced binary tree in sequence? (inorder)

    Hello. I am attempting to make an ordered set adt, and implementing a balanced binary tree for an assignment. One of the requirements of the set adt is that the adt should be able to create an iterator, and iterate through all the elements. The final function of the set adt requires the iterator to go through the set in sequence, by showing the next element in sequence. Normally I could recursively iterate through the tree, but I don't think it will work calling this function recursively.

    Code:
    * Returns the next element in the sequence represented by the given
    * set iterator.
    */
    void *set_next(set_iter_t *iter)
    {
      void *elem;
      iter->node = iter->node->left;
      iter->node = iter->node->right;
      elem = iter->node->data;
      return elem;
    }
    Here is the set adt set * GitHub and the header file set.h * GitHub
    Here is the tree implementation treei * GitHub
    and the header file treeh * GitHub

  2. #2
    Registered User
    Join Date
    Dec 2017
    Posts
    1,633
    It's an interesting question. How to return a value-at-a-time from a recursive function? I don't see how it's directly possible.

    The poor-man's way would be to read the tree data into an array first. Then a function with a static variable or two could serve the values from the array one at a time.

    If you want to serve the data directly from the tree then you need to remove the recursion.

    Take the following example:
    Code:
    void print_tree(Tree *t) {
        if (!t) return;
        print_tree(t->left);
        printf("%d\n", t->value);
        print_tree(t->right);
    }
    The second recursive call is tail-recursive and can be easily removed (compilers should do this automatically).
    Code:
    void print_tree(Tree *t) {
        while (t) {
            print_tree(t->left);
            printf("%d ", t->value);
            t = t->right;
        }
    }
    Now the hard part. You need to queue up the t->left calls.
    Code:
    // hide the ugly (poor-mans's) stack stuff
    #define PUSH(x) do { \
                if (top >= STACK_SIZE) \
                    err("stack overflow"); \
                st[top++] = x; } while(0)
    #define POP() st[--top]
    #define STACK_SIZE 100
    
    void print_tree(Tree *t) {
        Tree *st[STACK_SIZE]; // or a dynamic stack-adt
        int top = 0;
        while (t) {
            if (t->left) {
                PUSH(t);
                t = t->left;
            }
            else {
                printf("%d ", t->value);
                t = t->right;
                while (!t && top > 0) {
                    t = POP();
                    printf("%d ", t->value);
                    t = t->right;
                }
            }
        }
        printf("\n");
    }
    Then add in the needed statics and perchance a state variable and a goto or two. But maybe I'm overthinking this and there's a simpler way?
    A little inaccuracy saves tons of explanation. - H.H. Munro

  3. #3
    and the hat of int overfl Salem's Avatar
    Join Date
    Aug 2001
    Location
    The edge of the known universe
    Posts
    39,660
    The iterator should contain a FIFO queue, which contains all the tree nodes visited, but not yet completely processed.

    As you recurse to the left, you push that node onto the front of the queue.

    As you unwind from the right, you pop that node off the front of the queue.
    If you dance barefoot on the broken glass of undefined behaviour, you've got to expect the occasional cut.
    If at first you don't succeed, try writing your phone number on the exam paper.

  4. #4
    Registered User
    Join Date
    Apr 2013
    Posts
    1,658
    In what I consider to be a non-obvious solution, by modifying and restoring the links, then Morris traversal method can be used, which is an iterative (non-recursive) method to traverse a binary search tree in order. Example that traverses through an entire tree:

    Inorder Tree Traversal without recursion and without stack! - GeeksforGeeks

    I can later post example code for a function that takes a pointer to node as a parameter, and returns a pointer to the next in order node. After the initial call (using the root as the parameter), the caller calls the function with (previously_returned_pointer_to_node)->right to continue the in order traversal. Note that the entire tree needs to be traversed in order for all of the links to be restored.
    Last edited by rcgldr; 03-26-2018 at 03:58 PM.

  5. #5
    Registered User
    Join Date
    Apr 2013
    Posts
    1,658
    Example code for program that manually creates a bst and then uses a Morris traversal function to return nodes one at a time. Note that main() advances to current_node->right in the for loop:

    Code:
    /* mrstrv.c - morris traversal of binary tree */
    /* MorrisTraversal returns a set of pointers to nodes */
    
    #include<stdio.h>
    #include<stdlib.h>
    
    /* binary tree node */
    typedef struct BTNODE_
    {
        struct BTNODE_* left;
        struct BTNODE_* right;
        int data;
    }BTNODE;
    
    /* traverse binary tree without stack */
    /* initial input parameter is pointer to root */
    /* predecessor of cur is largest value < cur */
    /* predecessor of cur = cur->left->right->right->right ... */
    BTNODE * MorrisTraversal(BTNODE *cur)
    {
    BTNODE *pre;
        if(cur == NULL)
            return(NULL);
        while(cur != NULL){
            /* if left end of branch, return */
            if(cur->left == NULL)
                return(cur);
            /* advance pre to predecessor of cur */
            pre = cur->left;
            while(pre->right != NULL && pre->right != cur)
                pre = pre->right;
            /* if right end of branch, change pre->right = cur */
            /*  and advance cur left */
            if(pre->right == NULL){
                pre->right = cur;
                cur = cur->left;
            /* else back at cur, so restore pre->right = NULL */
            /*  and return */
            } else {
                pre->right = NULL;
                return(cur);
            }
        }
        return(NULL);
    }
    
    BTNODE* newbtNode(int data)
    {
    BTNODE* btNode = (BTNODE*) malloc(sizeof(BTNODE));
        btNode->left = NULL;
        btNode->right = NULL;
        btNode->data = data;
        return(btNode);
    }
    
    int main()
    {
    BTNODE *root;
    BTNODE *cur;
        /* manually create 15 element bst */
        root                      = newbtNode( 8);
        root->left                = newbtNode( 4);
        root->left->left          = newbtNode( 2);
        root->left->left->left    = newbtNode( 1);
        root->left->left->right   = newbtNode( 3);
        root->left->right         = newbtNode( 6);
        root->left->right->left   = newbtNode( 5);
        root->left->right->right  = newbtNode( 7);
        root->right               = newbtNode(12);
        root->right->left         = newbtNode(10);
        root->right->left->left   = newbtNode( 9);
        root->right->left->right  = newbtNode(11);
        root->right->right        = newbtNode(14);
        root->right->right->left  = newbtNode(13);
        root->right->right->right = newbtNode(15);
        /* use Morris traversal function to iterate through the tree */
        for(cur = root; cur; cur = cur->right){
            cur = MorrisTraversal(cur);
            printf("%2d ", cur->data);
        }
        return 0;
    }
    Last edited by rcgldr; 03-27-2018 at 03:50 PM.

  6. #6
    Registered User
    Join Date
    Dec 2017
    Posts
    1,633
    May as well show the beast I came up with. Not as clever as Morris. Uses a stack. I put both algorithms below, added a tree-displaying algorithm and random tree generation. Give a number at the command line to get a random tree of that size (start with 20). 0 or no number uses a 15-node balanced tree.

    The tree is printed sideways, like this:
    Code:
                .---97
                :   '---95
                :       '---83
            .---83
            :   '---81
            :       '---80
            :           '---78
        .---76
    +---75
        :       .---73
        :   .---67
        :   :   :   .---65
        :   :   '---58
        :   :       '---55
        '---54
            :               .---52
            :           .---50
            :       .---46
            :       :   :   .---44
            :       :   :   :   '---42
            :       :   '---41
            :       :       :   .---38
            :       :       '---29
            :   .---22
            :   :   :   .---20
            :   :   '---14
            '---12
                :   .---6
                :   :   '---4
                '---1
    Code:
    #include <stdio.h>
    #include <stdlib.h>
    #include <time.h>
     
    #define MAX_STACK_SIZE 100  // for Iterator stack
    #define MAX_PATH_DEPTH 100  // for printTreeStructure
     
    typedef struct BTNODE_ {
        int data;
        struct BTNODE_ *left, *right;
    } BTNODE;
     
    typedef struct Iterator {
        int top;
        BTNODE *st[MAX_STACK_SIZE];
    } Iterator;
     
     
    BTNODE* MorrisTraversal (BTNODE *cur);
    BTNODE* UT5000First     (BTNODE *t, Iterator *it);
    BTNODE* UT5000Next      (Iterator *it);
    BTNODE* newbtNode       (int data);
    void    insert          (BTNODE **pt, int data);
    void    printTree       (BTNODE *t);
    void    err             (const char *msg);
     
     
    int main(int argc, char **argv) {
        int size = 0;
        if (argc > 1) {
            size = atoi(argv[1]);
            if (size < 0) size = 0;
        }
    
        BTNODE *root = NULL;
        if (size > 0) {
            srand(time(NULL));
            for (int i = 0; i < size; i++)
                insert(&root, rand() % 100);
        }
        else {
            int a[] = {8,4,2,1,3,6,5,7,12,10,9,11,14,13,15};
            for (size_t i = 0; i < sizeof a / sizeof *a; i++)
                insert(&root, a[i]);
        }
     
        printTree(root);
     
        for(BTNODE *cur = root; cur; cur = cur->right){
            cur = MorrisTraversal(cur);
            printf("%2d ", cur->data);
        }
        printf("\n");
     
        Iterator it;
        BTNODE *nd = UT5000First(root, &it);
        while (nd) {
            printf("%2d ", nd->data);
            nd = UT5000Next(&it);
        }
        printf("\n");
     
        return 0;
    }
     
     
    void err(const char *msg) {
        fprintf(stderr, "Error: %s\n", msg);
        exit(EXIT_FAILURE);
    }
     
     
    BTNODE* newbtNode(int data) {
        BTNODE* btNode = malloc(sizeof *btNode);
        btNode->left = btNode->right = NULL;
        btNode->data = data;
        return btNode;
    }
     
    void insert(BTNODE **pt, int data) {
        for (BTNODE *t; (t = *pt) != NULL; )
            pt = data < t->data ? &t->left : &t->right;
        *pt = newbtNode(data);
    }
     
     
    void printTree(BTNODE *t) {
        static int depth = 0;
        static char path[MAX_PATH_DEPTH];
     
        if (!t) return;
     
        if (t->right) {
            if (depth == MAX_PATH_DEPTH) err("MAX_PATH_DEPTH");
            path[depth++] = 1;
            printTree(t->right);
            --depth;
        }
     
        for (int i = 0; i < depth; i++)
            printf("%c   ", i == 0 || path[i] == path[i - 1] ? ' ' : ':');
        printf("%c---%d\n",
            depth == 0 ? '+' : path[depth-1] ? '.' : '\'', t->data);
     
        if (t->left) {
            if (depth == MAX_PATH_DEPTH) err("MAX_PATH_DEPTH");
            path[depth++] = 0;
            printTree(t->left);
            --depth;
        }
    }
     
     
    //// MorrisTraversal //////////////////////////////////
     
    BTNODE *MorrisTraversal(BTNODE *cur) {
        while (cur && cur->left){
            BTNODE *pre = cur->left;
            while (pre->right && pre->right != cur)
                pre = pre->right;
            if (!pre->right) {
                pre->right = cur;
                cur = cur->left;
            } else {
                pre->right = NULL;
                return cur;
            }
        }
        return cur;
    }
     
     
    //// UltraTraverse5000 ////////////////////////////////
     
    BTNODE *UT5000First(BTNODE *t, Iterator *it) {
        if (!t) return NULL;
        it->top = 0;
        for ( ; t->left; t = t->left) it->st[it->top++] = t;
        return it->st[it->top] = t;
    }
     
    BTNODE *UT5000Next(Iterator *it) {
        BTNODE *t = it->st[it->top];
        if (!(t = t->right) && it->top)
            t = it->st[--it->top];
        else
            for ( ; t && t->left; t = t->left)
                it->st[it->top++] = t;
        return it->st[it->top] = t;
    }
    A little inaccuracy saves tons of explanation. - H.H. Munro

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Replies: 2
    Last Post: 01-17-2014, 05:24 PM
  2. Create a balanced (not BST) tree
    By kr0n1x in forum C Programming
    Replies: 9
    Last Post: 09-13-2011, 03:38 PM
  3. Replies: 6
    Last Post: 04-28-2011, 01:27 PM
  4. Weight Balanced Tree - Implementation
    By paulovitorbal in forum C Programming
    Replies: 1
    Last Post: 10-31-2007, 02:28 PM
  5. How do you create a balanced binary tree?
    By Mr_Jack in forum C++ Programming
    Replies: 3
    Last Post: 01-13-2004, 03:02 PM

Tags for this Thread