Search code examples
cstringbinary-treeinfinite-loopradix-tree

Issue with infinite loop in radix tree implementation


I'm having trouble with my radix tree implementation. The idea is that I create the first node, then enter a number of binary numbers. The binary numbers determine whether a left node (0) or a right node (1) is created. Once I reach the end of the binary number, I set a node to "active".

Then I search through the tree to find an active node, and output the original binary numbers again by checking in which direction I had to go to reach the active node.

Here is the complete code:

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

typedef int bool;
enum { false, true };

typedef struct radixNode {
    bool active;
    struct radixNode * pnt;
    struct radixNode * l;
    struct radixNode * r;
} node;

void insert(node *root, char * B) {

    printf("String: %s\n", B);
    printf("1st: %c", B[0]);
    printf("\n\n", B);

    // digit is zero so we go left
    if (B[0] == '0') {

        printf("till here if");

        // left child doesn't exist, create it
        if (root->l == NULL) {

            root->l = malloc(sizeof(node));

            /* if the next index in the string does NOT contain a 1 or 0,
            the current index is the last index and the node is activated */
            if (B[1] == 1 || B[1] == 0)
                root->l->active = false;
            else
                root->l->active = true;

            root->l->pnt = root;
            root->l->l = NULL;
            root->l->r = NULL;
            insert(root->l,B++); // B++ removes the first digit of the string
        }

        // left child exists, traverse
        else {
            insert(root->l,B++);
        }
    }

    // digit is one, go right
    else if (B[0] == '1') {
        printf("first was 1\n");

        // right child doesn't exist, create it
        if (root->r == NULL) {

            printf("if triggered\n");

            root->r = malloc(sizeof(node));

            /* if the next index in the string does NOT contain a 1 or 0,
            the current index is the last index and the node is activated */
            if (B[1] == 1 || B[1] == 0)
                root->r->active = false;
            else
                root->r->active = true;

            root->r->pnt = root;
            root->r->l = NULL;
            root->r->r = NULL;
            insert(root->r,B++);
        }

        // left child exists, traverse
        else {
            printf("else triggered\n");
            insert(root->r,B++);
        }
    }
}

node * printTreeMin(node *root) {

    char C[10];

    /* goes left until it can't, appends 0 to string
    till it can't. if node is active, print the string */
    while (root->l != NULL) {

        C[strlen(C)] = '0';

        if (root->active == true)
            printf("%s\n",C);

        root = root->l;
    }

    return root;
}

// prints the next smallest binary number in the tree, returns the node it printed
node * printNextSmallest(node * root) {

    char C[10];

    // if right child exists, go there and find lowest node (after if same deal as printTreeMin() )
    if (root->r != NULL) {

        C[strlen(C)] = '1';
        if (root->active == true)
            printf("%s\n",C);

        root = root->r;

        while (root->l != NULL) {

            C[strlen(C)] = '0';
            if (root->active == true)
                printf("%s\n",C);

            root = root->l;
        }

        return root;
    }

    node * temp = root->pnt;

    while (temp != NULL && root == temp->r) {

        root = temp;
        temp = temp->pnt;
    }

    return temp;
}

void printRadixTree(node *root) {

    root = printTreeMin(root);

    while (printNextSmallest(root) != NULL)
        root = printNextSmallest(root);
}

void test() {

    node * tree = malloc(sizeof(node));
    tree->l = NULL;
    tree->r = NULL;

    // a)
    insert(tree,"101000");
    insert(tree,"10100");
    insert(tree,"10110");
    insert(tree,"101");
    insert(tree,"1111");

    // b)
    printRadixTree(tree);

}

int main() {
    test();
}

Here is the output:

if triggered
String: 101000
1st: 1

first was 1
if triggered
String: 101000
1st: 1

first was 1
if triggered
String: 101000
1st: 1

(and continuing ad infinitum)

Clearly I have an issue within the insert() function's recursion but considering I remove the first char of the binary number string when doing the recurrence, I don't understand how it can run infinitely.


Solution

  • The reason for the infinite recursion is your choice of auto-increment operator. You want the prefix, not suffix form.

    insert(..., B++)
    

    increments the pointer (stripping the first character) after calling insert.

    Instead the calls should be

    insert (..., ++B)
    

    You also have problems with your active flag, and this is your culprit

    if (B[1] == 1 || B[1] == 0)
    

    I think you meant

    if (B[1] == '1' || B[1] == '0')
    

    The first form is checking for a binary zero or one, rather than an ASCII character.

    The result of this is that your active flag will probably be set incorrectly for most nodes. I expect that will then cause problems when traversing the tree. In fact, active will only be set to false when you are looking at the last '0' or '1' in the string (as B[1] at that point will be the terminating '\0').

    Also, with recusive routines it is always a good idea to make the base case explicit, rather than implicit. Thus, one of the first blocks of code in insert should probably be

    if (B[0] != '1' && B[0] != `0`)
        return;
    

    then you can replace the else if with a simple else

    if (B[0] == '0')
    {
        // ... go left
    }
    else
    {
        // ... go right
    }