How to correctly deallocate structure from memory - c

I have a dynamic data structure which looks like this:
struct tree_node {
int y;
int x;
struct tree_node *left;
struct tree_node *right;
struct tree_node *parent;
};
The structure is a node of a binary tree, with the addition that each node also points to its parent. Now, using the classical method of adding nodes to the binary tree using malloc() I can populate the binary tree easily. However, I am having trouble deallocating the binary tree from memory.
Usually, to remove nodes from the binary tree you perform a post-order traversal and then free each node like this:
void deleteTree(struct tree_node* node)
{
if (node == NULL) return;
deleteTree(node->left);
deleteTree(node->right);
printf("Deleting node with values [%d][%d]\n", node->y , node-> x);
free(node -> left);
free(node -> right);
free(node -> parent);
free(node);
printf("\nNode deleted");
}
However, when I run the above function it does not deallocate the binary tree from memory. When I run the function, it deallocates one leaf and then when it tries to delete the next node it gets stuck in an endless loop and my computer either crashes or the program exits with a non-descriptive error.
The output in the terminal is the following:
Deleting node with values [11][4]
Node deleted
Deleting node with values [7739840][0]
So the terminal shows that it deletes the first leaf, and then it tries to fetch the values from the next node but it cannot (which is why it displays 7739840). Then it gets stuck in an endless loop since it does not print "Node deleted".
How can I correctly deallocate the memory? Does it have to do with the way my node is built?

The correct way of deallocating all the nodes from your tree structure looks like this:
void deleteTree(struct tree_node* node)
{
if (node == NULL) return;
deleteTree(node->left);
deleteTree(node->right);
free(node);
}

You're deallocating nodes multiple times.
When you delete a given node, after deleteTree(node->left) is called the left node has already been freed, so it doesn't need to be freed again. So remove free(node->left).
The current node has also been freed because free(node->parent) was called in that function, so any further reads of node access freed memory. So remove free(node->parent).
And similarly to the call to deleteTree(node->left), calling deleteTree(node->right) already frees the right node, so remove the call to free(node->right).
So now you're left with:
void deleteTree(struct tree_node* node)
{
if (node == NULL) return;
deleteTree(node->left);
deleteTree(node->right);
printf("Deleting node with values [%d][%d]\n", node->y , node-> x);
free(node);
printf("\nNode deleted");
}
In short, each node is responsible for cleaning itself up.

This is happening because you are freeing the same node multiple times.
To delete a binary search tree you have to do something like this.
void bst_destroy(BSTNode node) {
if (node == NULL) {
return;
}
bst_destroy(node->left);
bst_destroy(node->right);
free(node);
}
Basically to delete a binary search tree you have to delete the leaves of the tree one by one so you don't end up deleting parent nodes you might have to visit again (which you will have to do if you are using recursion).

Related

Freeing dynamically allocated graph nodes in C

I want to build a graph that creates a new parent node by merging two child nodes. The code below is supposed to merge node a and b into a parent node c. Then, nodes a and c to create a parent node d:
a b
|---|
|
a c
|---|
|
d
When I try to free the graph starting at node d I get a segmentation fault and I don't know why. Somehow it works if I don't use the same node twice in the graph. However, I want to be able to use the same node more than once. Can someone please tell me what am I missing here?
#include <stdlib.h>
struct Node {
int data;
struct Node *child1;
struct Node *child2;
};
struct Node *NewNode(double data) {
struct Node *node = NULL;
node = malloc(sizeof(*node));
if (node == NULL) {
return node;
}
node->data = data;
node->child1 = NULL;
node->child2 = NULL;
return node;
}
struct Node* merge(struct Node *self, struct Node *other) {
struct Node *node = NewNode(-1);
node->child1 = self;
node->child2 = other;
return node;
}
void free_graph(struct Node **node) {
if (*node != NULL) {
free_graph(&(*node)->child1);
free_graph(&(*node)->child2);
free(*node);
*node = NULL;
}
}
int main(void){
struct Node *a = NewNode(1);
struct Node *b = NewNode(2);
struct Node *c = merge(a, b);
struct Node *d = merge(a, c);
free_graph(&d);
}
It does not work because your "tree" does not match your illustration, and is in fact technically not a tree. What you have looks like this:
You need to make a copy instead of reusing a node if you want a tree.
In order to free everything in a graph like this, I'd suggest having a separate linked list to keep track of everything you need to free.
If you don't want to do that, or cannot do that for some reason, it gets more complicated. Performing an operation an all nodes in a tree is trivial, but for a general directed graph it's slightly more complicated. I guess this answer could help, and if not, it at least gives you an idea about what to search for:
Finding list of all nodes in a directed graph
I assume you could do something like this pseudo:
getAllNodes(root, nodes)
if root // NULL check
if not node in nodes // If it's the first time we visit the node
// Add this node to the list of visited nodes
nodes = nodes + [root]
// And then call this function recursively on the children
getAllNodes(root->left, nodes)
getAlLNodes(root->right, nodes)
nodes = []
getAllNodes(root, nodes)
for node in nodes
free(node)
Trees have the nice feature that they never contain loops. But directed graphs do, so you have to have some check to see if a node is already visited. Note that in order for this to work, it has to be called from the root. Or to be more precise, every node needs to be reachable from the node. But that's not so different from a tree.
I guess you could somehow move the free inside to create a freeAllNodes() function, but this is more flexible. Maybe you want a list for other purposes. So my suggestion in that case is to just make freeAllNodes() call getAllNodes().
I could write an implementation for the above, but since C does not provide library functions for linked lists, that would mean including a lot of extra code.
You put a into the intended tree twice, so free_graph attempts to free it twice. Calling free twice on the same address from the same original allocation is improper.
If you want to have a true tree, do not put any node into it twice. If you want to have a data structure that can have the same node in it twice, either use separate copies of the node (e.g., two different allocations for struct Node with the same value for data) or make provisions in the data structure to avoid freeing it twice (for example, add a reference count to struct node to count how many times it is currently in the tree, and free the node only when its reference count reaches zero).

Freeing tree, but IDE gains a bit of memory over time

I am using the following free function for my tree:
void freeTree(struct node *tree) {
if (tree == NULL) return;
freeTree(tree->left);
freeTree(tree->right);
free(tree);
}
And I am creating a tree/operating on it like this:
struct node *root = NULL;
root = createTree(testNodes);
inOrderPrint(root);
freeTree(root);
Definition of node struct:
struct node {
int val;
int color;
struct node *parent;
struct node *left;
struct node *right;
};
I have noticed after running this enough (many hours), memory starts to build up (very little amount) in my IDE (I am using CLion). I am posting to ask if my freeTree function is implemented correctly to rule that out and to see if it could be something else causing this slow memory build up.
Supposing that your tree is in fact constructed in the manner implied by the names of the members of struct node, and that nodes' left and right pointers are set to NULL for nodes that do not have a left or right child, respectively, your freeTree() function will free all memory associated with the (sub) tree whose root node its argument points to. If you have a leak, it is elsewhere.

Binary Trees - difference between insert and mirror?

What manipulations are we doing in insert/delete that we aren't in mirror?
By mirror I'm referring to switching the left and right child of every single node in the tree.
The reason I'm asking is that when it comes to an insert, if you don't save the root returned by the function, it leads to errors. The same is not true of mirror. I assumed mirror would have the same requirement (returning the root node), but that is not the case. Why is that?
Here's my code if it's relevant (the insert is implemented for a BST):
Insert:
Node *insert(Node *root,int val)
{
Node *newNode=NULL;
if(root==NULL)
{
newNode=(Node*) malloc(sizeof(Node));
newNode->value=val;
newNode->left=NULL;
newNode->right=NULL;
return newNode;
}
if(root->value>val)
{
root->left=insert(root->left,val);
}
else
{
root->right=insert(root->right,val);
}
return root;
}
Mirror:
void mirror(Node *root)
{
Node *temp_left;
if(root==NULL)
return;
temp_left=root->left;
root->left=root->right;
root->right=temp_left;
mirror(root->left);
mirror(root->right);
}
Notice the line:
root->left=insert(root->left,val);
Here you are assigning the result of insert() to root->left. You need insert() to return a node* pointer here, or else we would have no idea where malloc placed the new node in memory, and so you wouldn't be able to add the node to the tree.
On the other hand, mirror only traverses the tree recursively and swaps some already-existing pointers. You need never assign the result mirror() to a variable.

deletion of binary tree using new method

Actually I was stuck while implementing the deletion of a tree . I deleted the leaf nodes using free() and now the parent would become leaf nodes , and delete those nodes too using recursion . But the problem is leaf node is not getting deleted actually , its still there . And also the method i followed in deletion of tree is as follows
void deleteTree(struct node *root)
{
if(root->left == NULL && root->right == NULL)
{
free(root);
}
else
{
if(root->left != NULL)
deleteTree(root->left);
if(root->right != NULL)
deleteTree(root->right);
}
}
This method deleted only the leaf nodes , and the corresponding parent nodes were not deleted . After debugging in XCode I found out that the leaf nodes were not deleted , They were still there.
So, why this happening ?
void deleteTree(struct node *root)
{
if (root) {
deleteTree(root->left);
deleteTree(root->right);
free(root);
}
}
Your deleteTree() is not good for delete nodes in tree. What you are doing is preorder traverse in binary tree recursively. The node you delete/free from tree will be refereed again when function returns, and hence you are accessing a free node that will cause Undefined behavior at run-time.
Do not access freed memory:
According to the C Standard, the behavior of a program that uses the value of a pointer that refers to space deallocated by a call to the free() or realloc() function is undefined.
You should choose Post-order to free tree's node. because in post-order traversal you don't access a node that is processed (deleted/free). And your deleteTree() function should be something like as follows:
void deleteTree(struct node *root){
if(root == NULL) return;
deleteTree(root->left);
deleteTree(root->right);
free(root); // node processed and return
}
Since you do not free the parent nodes on any level, all levels except for leaves remain. What you need to do is add free(root) after the if statement, at the end of the else statement.

How to free tree effectively

How can I free this tree effectively? That algorithm should work for any given node in such tree. So I'll have pointer to node, and that node will be "root" node. And I want to free everything below that node.
Every node in tree is this struct:
typedef struct tag
{
struct tag* parent;
struct tag* nextSibling;
struct tag* previousSibling;
struct tag* firstChild;
struct tag* lastChild;
char* name;
char* text;
}node;
I imagine this would work. But in reality, Dariusz is correct. You just use a valid tree traversal, and perform your operation on each node.
The question changed: And since you want this to operate on any node in the tree, just find the root first. It's much easier to write a tree traversal that progresses in one direction, than up and down the tree.
You've changed the question from deleting a tree, to deleting a subset of a tree. So, instead, let's do this. Remove the element from the tree first (remove_node). and then perform the same free that we would have done before.
void remove_node(node *self) {
if (self->previousSibling)
self->previousSibling->nextSibling = self->nextSibling;
if (self->nextSibling)
self->nextSibling->previousSibling = self->previousSibling;
if (self->parent && self->parent->firstChild == self)
self->parent->firstChild = self->nextSibling;
if (self->parent && self->parent->lastChild == self)
self->parent->lastChild = self->previousSibling;
}
void free_node(node *self) {
// Free one node. Perhaps this is:
free(self->name);
free(self->text);
free(self);
}
void iterate_nodes(node *root, void op(node *self) ) {
if (root == NULL)
return;
iterate_nodes(root->nextSibling, op);
iterate_nodes(root->firstChild, op);
op(root);
}
int main() {
node *node = NULL; // Some node in the tree...
remove_node(node);
iterate_nodes(node, free_node);
}
Use any of the standard tree-traversal mechanisms and delete all elements.
http://en.wikipedia.org/wiki/Tree_traversal
You can use a standard post-order tree traversal algorithms to free the whole tree. To make it work from any given node, just start by traversing all of the parent links to the root. For example:
void free_tree(node *n)
{
// Find the root node of the tree
while(n->parent)
n = n->parent;
free_tree_helper(n);
}
void free_tree_helper(node *n)
{
// Free all children of this node in post-order traversal
node *child = n->firstChild;
while(child)
{
// Save the next sibling pointer to avoid dangling pointers
node *next = child->nextSibling;
free_tree_helper(child);
child = next;
}
// All the children have been freed, now free the parent
free(n->name);
free(n->text);
free(n);
}
Alternatively, if you use a memory pool to allocate your tree nodes, all of your nodes come from the same pool, and the pool contains no other tree's nodes, you can instead just free the entire memory pool at once, saving you from having to traverse the entire tree. This will be much more efficient, since it avoids a lot of cache misses in a potentially large tree, and it also avoids certain memory fragmentation problems.

Resources