Okay so I have a Binary Search Tree built using only C structs and pointers because I'm insane and didn't want to use C++. Anyways, I've got some serious memory leaks since I'm assuming free(tree), tree being an instance of the struct below, doesn't free all the children of that tree.
Here is my node:
struct node{
struct node* parent;
struct node* left;
struct node* right;
int key; //the value of the node
};
and here is my bst:
struct bst{
struct node* root;
int elements; //number of nodes in the bst
};
So my question, is there any better way of doing this than recursively calling a delete function? for instance (writing this on the spot):
void delete_tree(struct node* n){
if(n == NULL) return;
struct node* left = n->left;
struct node* right = n->right;
free(n);
delete_tree(left);
delete_tree(right);
}
I see absolutely nothing wrong with a recursive delete. You could use an iterative approach, but it wouldn't have any discernible benefits and would be harder to write.
By the way, you can simplify the code a little and remove the two local variables as so:
void delete_tree(struct node* n){
if(n == NULL) return;
delete_tree(n->left);
delete_tree(n->right);
free(n);
}
You are making the recursive calls but never actually call free. You probably need to verify if a node is a leaf node (maybe asking if both children are null) and call free on that node.
Related
I'm currently studying Computer Science and we started working with Pointers. I had the feeling that I started to understand pointers but I ran into a problem and can't figure out what went wrong.
We defined a Tree like this:
typedef struct node *tree;
struct node {int key; tree left, right;};
Now we should write a function that creates nodes with three parameters, the key of the node, the left node and the right node that should be "below" the node. I did it like this and it seemed to work:
tree createNode(int n, tree l, tree r){
tree node = (tree) malloc(sizeof(tree));
node->key = n;
node->left = l;
node->right = r;
return node;
}
Finally we should write a function that multiplies all leaves of the Tree and i thought the easiest way would be to start at the root and search for the leaves through a recursion and then multiply them. But when i call the function the program seem to crash in the middle of the function. My function looks like this:
int leafprod(tree t){
printf("%d\n", t->key);
if (t->left == NULL){
if (t->right == NULL){
printf("$1\n\n");
return t->key;
}
printf("$2\n\n");
return leafprod(t->right);
}
if (t->right == NULL){
printf("$3\n\n");
return leafprod(t->left);
}
printf("$4\n\n");
return leafprod(t->left) * leafprod(t->right);
}
and i call the function in the main function like this:
int main(){
tree a = createNode(1, NULL, NULL);
tree b = createNode(2, NULL, NULL);
tree c = createNode(3, a, NULL);
tree d = createNode(4, b, c);
int n = leafprod(d);
printf("end: %d", n);
free(a);
free(b);
free(c);
free(d);
return 0;
}
I used the print statements to follow the programm and try to locate the error, but in most cases it prints nothing. Then sometimes it prints:
4
$4
2
$2
And only two times the program went through the whole code. I believe maybe I am using the malloc function wrong but I cannot tell.
The problem is in this line:
tree node = (tree) malloc(sizeof(tree));
tree is typedef for a pointer to struct node.
Therefore sizeof(tree) is just the size of the pointer.
You should one of the following instead:
tree node = malloc(sizeof(*l));
Or:
tree node = malloc(sizeof(*r));
*l and *r are of type struct node (not a pointer) and this is the element you are trying to create.
Another option as #IanAbbott commented is:
tree node = malloc(sizeof(*node));
Note that node here is the name of the variable, not the type (which in C requires to be prefixes with struct, i.e. struct node).
The advantage of this approach is that the statement is not dependent on other variables.
Side notes:
You shouldn't cast the result of malloc. See here:Do I cast the result of malloc?.
It's not a good practice to hide pointer types with typedefs. You can consider to avoid it (you can use typedef struct node Node if you want to save the need to use the struct keyword everywhere).
Don't use the typedef for hiding pointer types.
With typedef struct node *tree; you have no idea what tree is in your code hence the confusion.
Typically here tree node = (tree) malloc(sizeof(tree)) you did it wrong, and the main reason was probably because you didn't know anymore what tree actually was.
...
struct node
{
int key;
struct node* left;
struct node *right;
};
struct node* createNode(int n, struct node*l, struct node*r) {
struct node* node = malloc(sizeof(*node)); // don't use the cast, it's useless
...
}
int leafprod(struct node* t) {
...
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).
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.
My problem: I have a graph made of node struct
struct node {
node *n1;
node *n2;
}
And I want to add some members to make a new struct newNode
struct newNode {
newNode *n1;
newNode *n2;
newNode *n3; // new member
int a; // new member
}
I want to keep the original "node connections" in the graph so that I can do
newNode *n;
n->n1->n2...
just like
node *n;
n->n1->n2...
where n1 (or n2) of the two structs represent the same node in the graph, while I can also do
newNode *n;
n->n1->a...
n->n1->n3...
It's tricky to modify the node struct as it's defined in a big library.
My idea is to add one more member to newNode struct
struct newNode {
node *n; // added member
newNode *n1;
newNode *n2;
newNode *n3;
int a;
}
First, I create a newNode n_new for each node n. Then I traverse every pair of n_news, say n_new1 and n_new2, look at n_new1->n and n_new2->n to see whether there's a connection between them, that is, whether n_new1->n->n1 == n_new2->n, or n_new1->n->n2 == n_new2->n or vice versa. If so I make n_new1->n1 = n_new2, or n_new1->n2 = n_new2 or vice versa.
But the time complexity is O(#node^2). Inefficient.
Any other ideas?
P.S. The graph is a reduced-ordered binary decision diagram.
You have to copy recursively, creating a new new_node each time you encounter a new node (read that carefully). This will be a complete copy of the original graph, structurally identical but using different memory.
To determine if we have already copied a node, we keep a list of the ones we have copied (and their copies) in a BST that maps node* to new_node*.
You can find many BST implementations out there. There is one in the stl c++ library, for example and I think glibc, and so on.
The code will look like this:
struct node {
node *n1;
node *n2;
}
struct new_node {
node *n1;
node *n2;
newNode *n3; // new member
int a; // new member
}
new_node* GraphCopy (node* start)
{
new_node* n_new;
if(!start) return NULL;
n_new = malloc(sizeof(new_node));
//Add start.n1 and start.n2 to a bst here
//with keys of node* and vals of new_node*
TreeInsert(start, n_new)
if(IsInTree(start->n1))
n_new->n1 = TreeFind(start->n1);
else
n_new->n1 = GraphCopy(start->n1);
//repeat for n2
}
Complexity would be linear if there were no tree, so its still quite quick. (It would be more linear with a hash table, for example.)
(The tree would map node* to their copies.)
I get this weird side effect while using operator '->' in code I wrote in C. The pointer which I used -> on , is changed to have some garbage.
More specifically:
I have the following structs:
typedef void* ListElement ;
typedef struct List_t* List ;
typedef struct Node_t* Node;
Struct Node_t {
ListElement data ;
Node next;
}
Struct List_t {
Node* head;
Node* current
}
when I use the following ListGetFirst(), I get wired behavior :
ListElement ListGetFirst(List list)
{
if( list == NULL || list->head==NULL)
{
return NULL;
}
list->current=list->head;
Node* head =list->head; // here is the problem
ListElement data = (*head)->data;
return data;
}
when I used debugger I figured out that the pointer list->head is changed on the marked aformentioned line .
I realy have no idea why, and I didn't knew that '->' can have side effect
thanks in advance
Are you sure this is exactly what you want to do?
typedef struct Node_t* Node;
Node* head =list->head;
Since you defined Node as a pointer to Node_t, shouldn't you be doing:
Node head =list->head;
EDIT:
To summarize the whole thing, I think this typedef is misleading you:
typedef struct Node_t* Node;
It would made more sense if it were simply:
typedef struct Node_t Node;
Gah, pointers hidden behind typedefs; unless the type's meant to be totally opaque, that's almost always bad juju. For my benefit, I'm going to take out the typedefs so I have an easier time seeing what you're really playing with.
struct Node_t {
void *data ;
struct Node_t *next;
};
struct List_t {
struct Node_t **head;
struct Node_t **current;
};
void *ListGetFirst(struct List_t *list)
{
if( list == NULL || list->head==NULL
{
return NULL;
}
list->current=list->head;
struct Node_t **head =list->head; // here is the problem
void *data = (*head)->data;
return data;
}
I got nuthin'. Types all appear to match up. The -> operator most emphatically does not have any side effects; all it does is dereference a pointer. The extra level of indirection for head and current in struct List_t is a head-scratcher, and it makes me wonder if they're being allocated or assigned correctly. All I can figure is that list->head isn't pointing to memory that you actually own, and is getting overwritten somehow when you reach that point (IOW, you've invoked undefined behavior somewhere else in your code).
In short, the problem isn't in the code that you've posted. It's probably where you allocate and assign list elements.
You are using pointers to pointers, where most likely you want pointers.
In List_t you define head as Node*, where Node is already a Node_t* .
hth
Mario