andre
andre

Reputation: 175

Free a binary tree without recursion

refering to the question Deallocating binary-tree structure in C

struct Node{
    Node *parent;
    Node *next;
    Node *child;
}

I tried to free a binary tree. The problem I have is the allocated objects are 5520 and the number of calls to the free functions is 2747. I don't know why, it should really free and iterate all over the nodes in the tree, here is the code that I use

int number_of_iterations =0;
int number_of_deletions =0;
    void removetree(Node *node)
    {
        number_of_iterations++;
        while(node != NULL)
        {
            Node *temp = node;

            if(node->child != NULL)
            {
                node = node->child;
                temp->child = node->next;
                node->next = temp;
            }
            else
            {
                node = node->next;
                remove(temp);
                number_of_deletions++ 
            }
        }
    }

Number of iteration is 5440 and the number of deletions is 2747.

New Fixed code: is that code correct ?

 const Node *next(const Node *node)
    {
        if (node == NULL) return NULL;
        if (node->child) return node->child;

        while (node && node->next == NULL) {
            node = node->parent;
        }

        if (node) return node->next;
        return NULL;
    }

 for ( p= ctx->obj_root; p; p = next(p)) {
      free(p);
     }

Upvotes: 4

Views: 4796

Answers (10)

kotan
kotan

Reputation: 3

Binary tree deleting without recursion

typedef struct _knot_t knot_t;
typedef struct _knot_t
{
    knot_t*  left;
    knot_t*  right;
    knot_t*  parent;
} knot_t;

void root_destroy(knot_t* root)
{
    knot_t** glob = &root;
    knot_t*  curr;

    while (*glob)
    {
        curr = *glob;
        if ((*glob = curr->left))
        {
        }
        else if ((*glob = curr->right))
        {
        }
        else if (curr->parent)
        {
            *glob = curr->parent;
            if (curr->parent->left == curr)
            {
                (*glob)->left = 0;
            }
            else
            {
                (*glob)->right = 0;
            }
            free(curr);
        }
        else
        {
            free(curr);
        }
    }
}

Upvotes: 0

cher-nov
cher-nov

Reputation: 610

At first, I considered the code suggested by @AnT to be incorrect, because I thought the question was about a binary tree defined in the usual way (that is, with every node referencing its left and right subtrees). With this (false!) assumption, it turns out that the node is freed immediately after its left subtree is traversed and freed, but before the same happens to the right subtree. Then, when ascending from the right subtree, we will step on the parent node, which has already been freed.

What I missed is that in the original question, the binary tree is represented as a general one, in form of a queue of unidirectional linked lists with additional inter-links to parent nodes. For clarity, I drew the following diagram, where the dashed lines indicate references to parents.

https://pastebin.com/9jY4Kayh


If anyone still cares, there is a correct approach for binary trees in the usual form. It still requires the parent links, though. Written in C89.

void igena_avl_subtree_free( igena_avl_node_p root ) {
  igena_avl_node_p next_node;
{
  while (root != NULL) {
    if (root->left != NULL) {
      next_node = root->left;
      root->left = NULL;
    } else if (root->right != NULL) {
      next_node = root->right;
      root->right = NULL;
    } else {
      next_node = root->parent;
      free( root );
    }
    root = next_node;
  }
}}

Taken directly from my own project Gena.

Upvotes: 0

Sudharsan
Sudharsan

Reputation: 21

I know that this is an old post, but i hope my answer helps someone in the future. Here is my implementation of BinaryTree and it's operations in c++ without recursion, the logics can be easily implemented in C.

Each node owns a pointer to the parent node to make things easier.

NOTE: the inorderPrint() function used for printing out the tree's content uses recursion.

#include<iostream>

template<typename T>
class BinaryTree
{
    
    struct node
    {
        //the binary tree node consists of a parent node for making things easier as you'll see below
        node(T t)
        {
            value = t;
            
        }
        ~node() {  }
        struct node *parent = nullptr;
        T value;
        struct node *left   = nullptr;
        struct node *right  = nullptr;
    };
    node* _root;

    //gets inorder predecessor
    node getMinimum(node* start)
    {
        
        while(start->left)
        {
            start = start->left;
        }
        return *start;
    }
    /*
        this is the only code that uses recursion
        to print the inorder traversal of the binary tree
    */
    void inorderTraversal(node* rootNode)
    {
        
        if (rootNode)
        {
            
            inorderTraversal(rootNode->left);
            std::cout << rootNode->value<<" ";
            inorderTraversal(rootNode->right);
            
        }
        
    }
    int count;
public:
    int Count()
    {
        return count;
    }
    void Insert(T val)
    {
        count++;
        node* tempRoot = _root;
    
        if (_root == nullptr)
        {
            
            _root = new node(val);
            _root->parent = nullptr;
            
        }
        else
        {
            while (tempRoot)
            {

                if (tempRoot->value < val)
                {
                    if (tempRoot->right)
                        tempRoot = tempRoot->right;
                    else
                    {
                        tempRoot->right = new node(val );
                        tempRoot->right->parent = tempRoot;
                        break;
                    }
                }
                else if (tempRoot->value > val)
                {
                    if (tempRoot->left)
                        tempRoot = tempRoot->left;
                    else
                    {
                        tempRoot->left = new node(val);
                        tempRoot->left->parent = tempRoot;
                        break;
                    }
                }
                else
                {
                    std::cout<<"value already exists";
                    count--;
                }
            }
        }

    }
    void inorderPrint()
    {
        inorderTraversal(_root);
        std::cout <<std::endl;
    }
    void Delete(T val)
    {
        node *tempRoot = _root;
        //find the node with the value first
        while (tempRoot!= nullptr)
        {
            if (tempRoot->value == val)
            {
                break;
            }
            
            if (tempRoot->value > val)
            {
                tempRoot = tempRoot->left;
            }
            else
            {
                tempRoot = tempRoot->right;
            }
            
            
        }
        //if such node is found then delete that node
        if (tempRoot)
        {
            //if it contains both left and right child replace the current node's value with inorder predecessor
            if (tempRoot->right && tempRoot->left)
            {
                node inordPred = getMinimum(tempRoot->right);
                Delete(inordPred.value);
                count++;
                tempRoot->value = inordPred.value;
                
            }
            else if (tempRoot->right)
            {
                /*if it only contains right child, then get the current node's parent
                * check if the current node is the parent's left child or right child
                * replace the respective pointer of the parent with the right child of the current node
                * 
                * finally change the child's parent to the new parent
                */
                if (tempRoot->parent)
                {
                    if (tempRoot->parent->right == tempRoot)
                    {
                        tempRoot->parent->right = tempRoot->right;
                    }
                    if (tempRoot->parent->left == tempRoot)
                    {
                        tempRoot->parent->left = tempRoot->right;
                    }
                    tempRoot->right->parent = tempRoot->parent;
                }
                else
                {
                    //if there is no parent then it's a root node
                    //root node should point to the current node's child
                    
                    
                    _root = tempRoot->right;
                    _root->parent = nullptr;
                    delete tempRoot;
                }
                
            }
            else if (tempRoot->left)
            {
                /*
                * same logic as we've done for the right node
                */
                if (tempRoot->parent)
                {
                    if (tempRoot->parent->right == tempRoot)
                    {
                        tempRoot->parent->right = tempRoot->left;
                    }
                    if (tempRoot->parent->left == tempRoot)
                    {
                        tempRoot->parent->left = tempRoot->left;
                    }
                    tempRoot->left->parent =tempRoot->parent ;
                    
                }
                else
                {
                    
                    
                    _root = tempRoot->left;
                    _root->parent = nullptr;
                    delete tempRoot;
                }
                
            }
            else
            {
                /*
                * if it's a leaf node, then check which ptr (left or right) of the parent is pointing to 
                * the pointer to be deleted (tempRoot)
                * then replace that pointer with a nullptr
                * then delete the (tempRoot)
                */
                if (tempRoot->parent)
                {
                    if (tempRoot->parent->right == tempRoot)
                    {
                        tempRoot->parent->right = nullptr;
                    }
                    else if (tempRoot->parent->left == tempRoot)
                    {
                        tempRoot->parent->left = nullptr;
                    }
                    delete tempRoot;
                }
                else
                {
                    //if the leaf node is a root node ,then delete it and set it to null
                    delete _root;
                    _root = nullptr;
                }
                
            }
            count--;
        }
        else
        {
            std::cout << "No element found";
        }
    }
    
    void freeTree()
    {
        //the output it produces will be that of a preorder traversal
        std::cout << "freeing tree:";
        node* end=_root;
        node* parent=nullptr;
        while (end)
        {
            //go to the node with least value
            if (end->left)
                end = end->left;
            else if (end->right)
            {
                //if it's already at the least value, then check if it has a right sub tree
                //if it does then set it as "end" ptr so that the loop will check for the least node in this subtree
                end = end->right;
            }
            else
            {
                //if it's a leaf node then it should be deleted and it's parent's (left or right pointer )
                //should be safely set to nullptr
                //then end should be set to the parent pointer
                //so that it we can repeat the process for all other nodes that's left
                std::cout << end->value<<" ";
                parent = end->parent;
                if (parent)
                {
                    if (parent->left == end)
                        parent->left = nullptr;
                    else
                        parent->right = nullptr;
                    delete end;
                    
                }
                else
                {
                    delete end;
                    _root = nullptr;
                }
                
                count--;
                end = parent;
            }
            
        }
        
    }
    ~BinaryTree()
    {
        freeTree();
    }
};


int main()
{
    BinaryTree<int> bt;
    bt.Insert(3);
    bt.Insert(2);
    bt.Insert(1);
    bt.Insert(5);
    bt.Insert(4);
    bt.Insert(6);
    std::cout << "before deletion:\n";
    bt.inorderPrint();
    bt.Delete(5);
    bt.Delete(2);
    bt.Delete(1);
    bt.Delete(4);
    std::cout << "after deletion:\n";
    bt.inorderPrint();
    bt.freeTree();
    std::cout << "\nCount: " << bt.Count()<<"\n";
}


output:

before deletion:
1 2 3 4 5 6
after deletion:
3 6
freeing tree:6 3
Count: 0
freeing tree:

Upvotes: 2

AnT stands with Russia
AnT stands with Russia

Reputation: 320631

If your nodes do indeed contain valid parent pointers, then the whole thing can be done in a much more compact and readable fashion

void removetree(Node *node)
{
  while (node != NULL)
  {
    Node *next = node->child;

    /* If child subtree exists, we have to delete that child subtree 
       first. Once the child subtree is gone, we'll be able to delete 
       this node. At this moment, if child subtree exists, don't delete
       anything yet - just descend into the child subtree */

    node->child = NULL;
    /* Setting child pointer to null at this early stage ensures that 
       when we emerge from child subtree back to this node again, we will 
       be aware of the fact that child subtree is gone */

    if (next == NULL)
    { /* Child subtree does not exist - delete the current node, 
         and proceed to sibling node. If no sibling, the current 
         subtree is fully deleted - ascend to parent */
      next = node->next != NULL ? node->next : node->parent;
      remove(node); // or `free(node)`
    }

    node = next;
  }
}

Upvotes: 5

Luis Colorado
Luis Colorado

Reputation: 12698

First to say is that if you try to solve a recursive problem in a non-recursive way, you'll run into trouble.

I have seen a wrong answer selected as the good one, so I'll try to show my approach:

I'll begin using pointer references instead of plain pointers, as passing a root pointer reference makes it easier to detect (and update) the pointers to the root node. So the interface to the routine will be:

void delete_tree(struct node * * const ref);

It represents a reference to the pointer that points to the root node. I'll descend to the node and, if one of child or next is NULL then this node can be freely eliminated by just making the referenced pointer to point to the other link (so I'll not lose it). If the node has two children (child and next are both != NULL) then I cannot delete this node until one of the branches has collapsed, and then I select one of the branches and move the reference (I declared the ref parameter const to assure I don't modify it, so I use another moving reference for this)

struct node **moving_reference;

Then, the algorithm follows:

void tree_delete(struct node ** const static_ref)
{
    while (*static_ref) {
        struct node **moving_ref = static_ref;

        while (*moving_ref) {
            struct node *to_be_deleted = NULL;

            if ((*moving_ref)->child && (*moving_ref)->next) {
                /* we have both children, we cannot delete until
                 * ulterior pass. Just move the reference. */
                moving_ref = &(*moving_ref)->child;
            } else if ((*moving_ref)->child) {
                /* not both != NULL and child != NULL, 
                 * so next == NULL */
                to_be_deleted = *moving_ref;
                *moving_ref = to_be_deleted->child;
            } else {
                /* not both != NULL and child == NULL,
                 * so follow next */
                to_be_deleted = *moving_ref;
                *moving_ref = to_be_deleted->next;
            } /* if, else if */
            /* now, delete the unlinked node, if available */
            if (to_be_deleted) node_delete(to_be_deleted);
        } /* while (*moving_ref) */
    } /* while (*static_ref) */
} /* delete_tree */

I have included this algorithm in a complete example, showing you the partial trees and the position of moving_ref as it moves through the tree. It also shows the passes needed to delete it.

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

#define N 100

#define D(x) __FILE__":%d:%s: " x, __LINE__, __func__

#define ASSERT(x) do { \
        int res; \
        printf(D("ASSERT: (" #x ") ==> %s\n"), \
                        (res = (int)(x)) ? "TRUE" : "FALSE"); \
        if (!res) exit(EXIT_FAILURE); \
} while (0)

struct node {
        int key;
        struct node *child;
        struct node *next;
}; /* struct node */

struct node *node_alloc(void);
void node_delete(struct node *n);

/* This routine has been written recursively to show the simplicity
 * of traversing the tree when you can use recursive algorithms. */
void tree_traverse(struct node *n, struct node *p, int lvl)
{
    while(n) {
        printf(D("%*s[%d]\n"), lvl<<2, p && p == n ? ">" : "", n->key);
        tree_traverse(n->child, p, lvl+1);
        n = n->next;
    } /* while */
} /* tree_traverse */

void tree_delete(struct node ** const static_ref)
{
    int pass;

    printf(D("BEGIN\n"));

    for (pass = 1; *static_ref; pass++) {
        struct node **moving_ref = static_ref;

        printf(D("Pass #%d: Considering node %d:\n"),
                        pass, (*moving_ref)->key);

        while (*moving_ref) {
            struct node *to_be_deleted = NULL;

            /* print the tree before deciding what to do. */
            tree_traverse(*static_ref, *moving_ref, 0);

            if ((*moving_ref)->child && (*moving_ref)->next) {
                printf(D("Cannot remove, Node [%d] has both children, "
                                        "skip to 'child'\n"),
                                (*moving_ref)->key);
                /* we have both children, we cannot delete until
                 * ulterior pass. Just move the reference. */
                moving_ref = &(*moving_ref)->child;
            } else if ((*moving_ref)->child) {
                /* not both != NULL and child != NULL, 
                 * so next == NULL */
                to_be_deleted = *moving_ref;
                printf(D("Deleting [%d], link through 'child' pointer\n"),
                                to_be_deleted->key);
                *moving_ref = to_be_deleted->child;
            } else {
                /* not both != NULL and child != NULL,
                 * so follow next */
                to_be_deleted = *moving_ref;
                printf(D("Deleting [%d], link through 'next' pointer\n"),
                                to_be_deleted->key);
                *moving_ref = to_be_deleted->next;
            } /* if, else if */

            /* now, delete the unlinked node, if available */
            if (to_be_deleted) node_delete(to_be_deleted);
        } /* while (*moving_ref) */
        printf(D("Pass #%d end.\n"), pass);
    } /* for */

    printf(D("END.\n"));
} /* delete_tree */

struct node heap[N] = {0};
size_t allocated = 0;
size_t deleted = 0;

/* simple allocation/free routines, normally use malloc(3). */
struct node *node_alloc()
{
        return heap + allocated++;
} /* node_alloc */

void node_delete(struct node *n)
{
        if (n->key == -1) {
                fprintf(stderr,
                                D("doubly freed node %ld\n"),
                                (n - heap));
        }
        n->key = -1;
        n->child = n->next = NULL;
        deleted++;
} /* node_delete */

/* main simulation program. */
int main()
{
        size_t i;

        printf(D("Allocating %d nodes...\n"), N);
        for (i = 0; i < N; i++) {
                struct node *n;

                n = node_alloc(); /* the node */
                n->key = i;
                n->next = NULL;
                n->child = NULL;

                printf(D("Node %d"), n->key);

                if (i) { /* when we have more than one node */
                        /* get a parent for it. */
                        struct node *p = heap + (rand() % i);

                        printf(", parent %d", p->key);
                        /* insert as a child of the parent */
                        n->next = p->child;
                        p->child = n;
                } /* if */
                printf("\n");
        } /* for */

        struct node *root = heap;

        ASSERT(allocated == N);
        ASSERT(deleted == 0);

        printf(D("Complete tree:\n"));
        tree_traverse(root, NULL, 0);

        tree_delete(&root);

        ASSERT(allocated == N);
        ASSERT(deleted == N);
} /* main */

Upvotes: 1

weston
weston

Reputation: 54801

This is a binary tree! It's confusing people because of the names you have chosen, next is common in a linked list but the types are what matters and you have one node referencing exactly two identical nodes types and that's all that matters.

Taken from here: https://codegolf.stackexchange.com/a/489/15982 which you linked to. And I renamed left to child and right to next :)

void removetree(Node *root) {
    struct Node * node = root;
    struct Node * up = NULL;

    while (node != NULL) {
        if (node->child != NULL) {
            struct Node * child = node->child;
            node->child = up;
            up = node;
            node = child;
        } else if (node->next != NULL) {
            struct Node * next = node->next;
            node->child = up;
            node->next = NULL;
            up = node;
            node = next;
        } else {
            if (up == NULL) {
                free(node);
                node = NULL;
            }
            while (up != NULL) {
                free(node);
                if (up->next != NULL) {
                    node = up->next;
                    up->next = NULL;
                    break;
                } else {
                    node = up;
                    up = up->child;
                }
            }
        }
    }
}

Upvotes: 3

Transcendental
Transcendental

Reputation: 981

You are freeing only subtrees, not parent nodes. Suppose child represents the left child of the node and next is the right child. Then, your code becomes:

struct Node{
    Node *parent;
    Node *right;
    Node *left;
}

int number_of_iterations =0;
int number_of_deletions =0;
    void removetree(Node *node)
    {
        number_of_iterations++;
        while(node != NULL)
        {
            Node *temp = node;

            if(node->left != NULL)
            {
                node = node->left;
                temp->left = node->right;
                node->right = temp;
            }
            else
            {
                node = node->right;
                remove(temp);
                number_of_deletions++ 
            }
        }
    }

Each time your while loop finishes one iteration, a left sub-node will be left without a pointer to it. Take, for example the following tree:

             1
     2                3
4        5        6         7

When node is the root node, the following happens

  • node is pointing to '1'
  • temp is pointing to '1'
  • node now points to '2'
  • temp->left now points to '5'
  • node->right now points to '1'

In the next iteration, you begin with node pointing to '2' and temp also. As you can see, you did not remove node '1'. This will repeat.

In order to fix this, you might want to consider using BFS and a queue-like structure to remove all the nodes if you want to avoid recursion.

Edit BFS way:

  1. Create a Queue structure Q
  2. Add the root node node to Q
  3. Add left node of node to Q
  4. Add right node of node to Q
  5. Free node and remove it from Q
  6. Set node = first element of Q
  7. Repeat steps 3-6 until Q becomes empty

This uses the fact that queue is a FIFO structure.

Upvotes: 0

Mr. E
Mr. E

Reputation: 2120

I would do

void removeTree(Node *node){
    Node *temp;
    while (node !=NULL){
        if (node->child != NULL) {
            removeTree(node->child);
        }
        temp = node;
        node = node->next;
        free(temp);
    }
}

Non-recursive version:

void removeTree(Node *node){
    Node *temp;
    while (node !=NULL){
        temp = node;
        while(temp != node) {
            for(;temp->child == NULL; temp = temp->child); //Go to the leaf
            if (temp->next == NULL)
                free(temp); //free the leaf
            else { // If there is a next move it to the child an repeat
                temp->child = temp->next;
                temp->next = temp->next->next;
            }
        }
        node = temp->next; // Move to the next
        free(temp)
    }
}

I think this should work

Upvotes: 0

Codor
Codor

Reputation: 17605

The implementation does not work for a tree with root 1, which has a single child 2.

NodeOne.parent = null
NodeOne.next = null
NodeOne.child = NodeTwo

NodeTwo.parent = null
NodeTwo.next = null
NodeTwo.child = null

Execution of Removetree(NodeOne) will not call remove on NodeOne.

Upvotes: 0

Stefan Haustein
Stefan Haustein

Reputation: 18803

Why not just do this recurisively?

void removetree(Node *node) {
  if (node == NULL) {
    return;
  }
  number_of_iterations++;
  removetree(node->next);
  removetree(node->child);
  free(node);
  number_of_deletions++;
}

Upvotes: 0

Related Questions