I'm trying to make a binary tree using a recursive insert function but my root node seems to keep changing(if it didn't, printf should always give the same output in the code below). Any thoughts on how to fix this?
typedef struct Tnode{
char name[30];
int value;
struct Tnode *left;
struct Tnode *right;
} Tnode;
Tnode *insert(Tnode *node, char *name, int value){
if(node==NULL){
Tnode *temp = malloc(sizeof(struct Tnode));
sprintf(temp->name,"%s",name);
temp->value = value;
temp->left = NULL;
temp->right = NULL;
return temp;
}
else if(strcmp(name,node->name)<0)
{
node->left = insert(node->left, name, value);
}
else if(strcmp(name,node->name)>0)
{
node->right = insert(node->right, name, value);
}
else{
printf("something went wrong\n");
}
}
int main(){
Tnode *root = NULL;
root = insert(root,"george",11);
root = insert(root,"dick",12);
printf("%s\n",root->name);
root = insert(root,"walter",13);
root = insert(root,"harry",13);
printf("%s\n",root->name);
root = insert(root,"zink",40);
printf("%s\n",root->name);
}
ok, so now we've figured it out together. your insert() is declared as
Tnode *insert(...)
so it allways needs to return a pointer to a Tnode, but there are cases in your if-clause that lead to an execution branch that doesn't return anything. I guess it's undefined what will be the return value.
Since insert() operates recursively by passing the call "if you're a leaf, put the value here and return yourself", you need to handle the case "and if you're not, do ... and return ...". So imagine you already have a large tree and you insert a value, the root passes the call down the binary structure until the correct position is found. The final call (where the recursion ends) will return a *Tnode to that leaf so that the previous call can correctly set node->left = insert(...);, but there are still "open" calls on the stack (since it's a recursion) that needs to get closed, i.e. the function terminates and every node writes something into node->left or node->right. If you don't return anything, there might be some arbitrary values that break your data structure and lead to undefined behavior.
So, the solution was just to add a return node; at the end so that the recursive calls can be finalized by leaving the "passing" nodes between the root and the new leaf unchanged.
Related
I am trying to create a function which returns the mirrored copy of a binary tree.
By "mirrored" I mean a tree with each left node as its right node and vice versa.
The one on the left gets copied to resemble the one on the right. This is the code of the function, with the definition of the binary nodes and "insert node" function that I use:
typedef struct bNode {
int data;
struct bNode *left;
struct bNode *right;
} bNode;
// =============================================================
bNode* reverse_tree (bNode **tree) {
bNode *copy = malloc(sizeof(bNode));
copy->data = (*tree)->data;
if (!((*tree)->right) && !((*tree)->left)){
return copy;
}
copy->left = reverse_tree(&(*tree)->right);
copy->right = reverse_tree(&(*tree)->left);
return copy;
}
// =============================================================
void insert(bNode **tree, int data) {
bNode *temp, *previous, *current;
if (*tree == NULL) {
temp = (bNode *) malloc(sizeof (bNode));
temp->data = data;
temp->left = NULL;
temp->right = NULL;
*tree = temp;
return;
}
if (data < (*tree)->data) {
insert(&(*tree)->left, data);
} else if (data > (*tree)->data) {
insert(&(*tree)->right, data);
}
}
After some troubleshooting, one single layer of recursion works fine, but after that, the pointers break (that is, they point to an inaccessible part of memory), and the program receives a SIGSEGV Segmentation fault.
Why do I receive this SIGSEGV and how do I avoid it?
P.S I am quite inexperienced with pointers; I hope it's not too bad.
(The one on the left gets copied to resemble the one on the right)
At least the function reverse_tree has a bug.
The sub-statement of this if statement:
if (!((*tree)->right) && !((*tree)->left)){
return copy;
}
gets the control when the both pointers, right and left, are null pointers.
So this code snippet:
copy->left = reverse_tree(&(*tree)->right);
copy->right = reverse_tree(&(*tree)->left);
can get the control when only one of the pointers is a null pointer.
In this case in the next recursive call of the function this statement:
copy->data = (*tree)->data;
invokes undefined behavior for the passed null pointer.
I have a C program that implements trees. my cleanup function looks like this:
void cleanup_tree( TreeNode* root ){
printf("Called\n");
if(root->left!=NULL){
cleanup_tree(root->left);
}
if(root->right!= NULL){
cleanup_tree(root->right);
}
if(root->right==NULL &&root->left==NULL) {
/*free(root);*/
free(root->word);
free(root);
root = NULL;
}
}
My Tree struct has
typedef struct TreeNode_st {
char *word; // the word held in this node
unsigned int frequency; // how many times it has been seen
struct TreeNode_st *left; // node's left child
struct TreeNode_st *right; // node's right child
} TreeNode;
I am initialising a tree like this :
TreeNode* initTreeNode(){
TreeNode *mainNode= (TreeNode*)malloc(sizeof(TreeNode));
mainNode->frequency = 0 ;
mainNode->word = NULL;
mainNode->left = NULL;
mainNode->right = NULL;
return mainNode;
}
in my main, I have called
TreeNode *mainNode =initTreeNode();
and I'm doing operations on it , and just before program exit, i called
cleanup_tree(mainNode);
Valgrind reported memory leaks, so just to test , i did
I put
printf("~~~FINAL NULL TEST %s",mainNode->left->right->word);
below my cleanup_tree line,
And i'm able to see the word even now.
What am I doing wrong ?
There are two ways:
You pass it a pointer-to-a-pointer: void cleanup_tree( TreeNode **root)
You set the fields to NULL after the cleanup returns:
Currently, the changes made by the function are not reflected in the node parameter you passed.
Ad 2:
cleanup_tree(root->right);
root->right= NULL;
You seem to be under the impression that setting root = NULL at the end of this function will be visible in the calling function so that the third if block gets called. That's not the case.
You want to always free() the word as well as the node itself.
void cleanup_tree( TreeNode* root ){
printf("Called\n");
if(root->left!=NULL){
cleanup_tree(root->left);
}
if(root->right!= NULL){
cleanup_tree(root->right);
}
free(root->word);
free(root);
}
I am writing a program that is a linked list of binary search trees. We are supposed to search for a number in the trees and print the tree and line number found. Because of this, we are supposed to use a Breadth-First Search function. I am getting a segmentation fault in my dequeue function and I am unsure why.
These are my structures:
typedef struct BST {
int value;
int treeNum;
struct BST* left;
struct BST* right;
}BST;
typedef struct rootList{
struct BST* root;
struct rootList* next;
}rootList;
typedef struct bfsQ{
struct BST* treeNode;
struct bfsQ* next;
}bfsQ;
This is my BFS function:
void BFS(rootList* listHead, int searchValue)
{
if(listHead->root == NULL)
{
printf("%d/tNO/tN/A/tN/A\n", searchValue);
}
else
{
bfsQ* newQueue = NULL;
BST* temp = NULL;
newQueue = malloc(sizeof(bfsQ));
newQueue->next = NULL;
enqueue(&newQueue, listHead->root);
while(newQueue != NULL)
{
temp = dequeue(&newQueue);
printf("Did this work?");
if(temp->value == searchValue)
printf("HI I WAS FOUND");
else
{
if(temp->left != NULL)
enqueue(&newQueue, temp->left);
if(temp->right != NULL)
enqueue(&newQueue, temp->right);
}
}
BFS(listHead->next, searchValue);
}
}
This is my enqueue:
void enqueue(bfsQ** qHead, BST* new_tree_node)
{
bfsQ *temp = malloc(sizeof(bfsQ));
BST *node;
temp->treeNode = new_tree_node;
temp->next = *qHead;
*qHead = temp;
node = temp->treeNode;
printf("%d\n", node->value);
}
This is my dequeue:
BST* dequeue(bfsQ** qHead)
{
bfsQ *temp, *first;
BST *newBST;
temp = (*qHead);
while(temp->next != NULL)
{
printf("THIS IS NOT NULL YET\n");
temp = temp->next;
}
first = temp;
newBST = first->treeNode;
free(temp);
return first->treeNode;
}
What am I doing wrong? The enqueue is working correctly, however my dequeue is not storing correctly.
EDIT: Apparently I need to:
"This function implements a variant of a level by level search or formally
called as the BREADTH FIRST SEARCH.
-> This function searches for a given value in the binary trees and it does that
by searching for level 1 in each binary trees, then moving on to level 2 if
it fails to find it that value in level 1 and so on.
-> Basically, you have to search for a given value in all the binary trees, one
level at a time, in the linked list simultaneously."
So I'm not sure if I need to search the whole tree, then move on, or look at each tree, line by line.
From the superficial look I had into the code, it looks generally ok (though I would have implemented some parts differently), but the last lines in dequeue() are certainly wrong:
first = temp;
newBST = first->treeNode;
free(temp);
return first->treeNode;
Accessing first->treeNode in the last line is catastrophic: first holds an address that has already been freed (temp and first refer to the same memory location). I think you wanted to return newBST instead:
return newBST;
You might as well throw first away, as it seems useless, and turn that into:
newBST = temp->treeNode;
free(temp);
return newBST;
Please help me with this. I keep getting seg faults!
I want to use recursion to create and insert a new node.
Please help me debug this.
//Create a Binary Search Tree From an array.
struct Tree
{
int data;
struct Tree *lchild;
struct Tree *rchild;
};
struct Tree *root = NULL;
struct Tree *node(int val)
{
struct Tree *tempnode;
tempnode = (struct Tree*)malloc(sizeof(struct Tree));
tempnode->data = val;
tempnode->rchild = NULL;
tempnode->lchild = NULL;
return tempnode;
}
void createTree(struct Tree *curr, int val)
{
struct Tree *newnode = node(val);
if (curr == NULL)
curr = newnode;
else if(val < curr->data)
{
createTree(curr->lchild,val);
}
else if(val > curr->data)
{
createTree(curr->rchild,val);
}
else
printf("Error Similar data found\n");
}
void inorder(struct Tree *root)
{
if (root->lchild != NULL)
inorder(root->lchild);
printf("[%d] ",root->data);
if (root->rchild != NULL)
inorder(root->rchild);
}
int main()
{
// root = NULL;
int i = 0, arr[5] = {41,12,32,23,17};
for(i;i<5;i++)
createTree(root,arr[i]);
inorder(root);
return 0;
}
why do I keep getting seg fault. Can someone explain me?
Am I doing something I should not? Or am I missing at some point?
Learn to use a debugger!
Stepping through the main function, you would have seen that the value of root would have remained NULL after each call to createTree
The createTree function is not modifying the value of root, but only modifying its copy of the value of root.
Your createTree function needs to take a struct Tree **curr, a pointer-to-a-pointer. This allows the function to modify the original value, not the local copy.
The root of the tree is not assigned anywhere; in your function createTree you probably think that it is assigned in:
if (curr == NULL)
curr = newnode;
But curr is local to the function and does not affect root. You need to change the argument curr to be a pointer to pointer, otherwise the function does not work for assigning the root node, or child nodes. The root of the tree is not assigned anywhere; in your function createTree you probably think that it is assigned in:
if (curr == NULL)
curr = newnode;
But curr is local to the function and does not affect root even if you gave it as the argument curr. You need to change the argument curr to be a pointer to pointer, otherwise the function does not work for assigning the root node, or child nodes. That is, the function declaration becomes:
void createTree(struct Tree **curr, int val)
Of course you must then change the use of curr inside the function accordingly (i.e., the address pointed to is *curr where it used to be curr), calls of the function need to pass the address, and not value, of the pointer (e.g., createTree(&root, arr[i])).
edit: Or, indeed, have the function return curr and always assign the return value to the relevant pointer at every place where you call createTree, thanks to #JonathanLeffler for the observation.
This is something of a followup to a question I asked earlier. I'm still learning my way around pointers, and I'm finding it difficult to maintain a reference to the physical address of a struct while iterating through a data structure. For example, I have a simple, barebones linked list that I'd like to delete from via a searching pointer:
struct Node{
int value;
struct Node* next;
};
struct Node* createNode(int value){
struct Node* newNode = malloc(sizeof *newNode);
newNode->value = value;
newNode->next = NULL;
return newNode;
}
void nodeDelete(Node **killptr){
free(*killptr);
*killptr = NULL;
}
int main(){
struct Node* head = createNode(16);
head->next = createNode(25);
head->next->next = createNode(51);
head->next->next->next = createNode(5);
// Working code to delete a specific node with direct reference address
struct Node** killptr = &head->next;
nodeDelete(killptr);
return 0;
}
The above shows deleting by passing nodeDelete a pointer to the address of the head pointer. What I want to do is be able to move my pointer ->next until it finds something that satisfies a delete condition, and call nodeDelete on that. I've tried the following:
struct Node* searchAndDestroy = head;
while(searchAndDestroy->value != NULL){ // Search until the end of the structure
if (searchAndDestroy->value == 25){ // If the value == 25
nodeDelete(&searchAndDestroy); // Delete the node (FAILS: Nullifies the
// address of search variable, not the
break; // original node)
}else{
searchAndDestroy = searchAndDestroy->next;
}
}
I've also tried something along the lines of:
if (searchAndDestroy->value == 25){
struct Node** killptr = (Node**)searchAndDestroy);
nodeDelete(killptr); // Still fails
}
I need to be able to move my pointer to the ->next point, but also maintain a reference to the address of the node I want to delete (instead of a reference to the address of the search node itself).
EDIT: Some clarification: I realize that deleting from a linked list in this fashion is naive, leaks memory, and drops half the list improperly. The point is not to actually delete from a linked list. Ultimately the idea is to use it to delete the leaves of a binary search tree recursively. I just figured a linked list would be shorter to portray in the question as an example.
struct Node **searchAndDestroy;
for (searchAndDestroy = &head;*searchAndDestroy; searchAndDestroy = &(*searchAndDestroy)->next ){
if ((*searchAndDestroy)->value == 25){
nodeDelete(searchAndDestroy); // Function should be changed to assign the ->next pointer to the **pointer
break;
}
}
And change nodeDelete like this:
void nodeDelete(Node **killptr){
Node *sav;
if (!*killptr) return;
sav = (*killptr)->next;
free(*killptr);
*killptr = sav;
}
Unless I'm missing something, your nodeDelete function is working as designed, but you want to keep a way of accessing the next node in the chain. The easiest way of doing this is just to add a temporary variable:
struct Node *searchAndDestroy = head, *temp = NULL;
while(searchAndDestroy != NULL){ // Need to check if the node itself is null before
// dereferencing it to find 'value'
temp = searchAndDestroy->next;
if (searchAndDestroy->value == 25){
nodeDelete(&searchAndDestroy);
break;
}else{
searchAndDestroy = temp;
}
}
if you give the Address of the previous Node that is where the link to deleting node present then it is very simple
code snippet for that:-
void delete_direct (struct Node *prevNode)
{/*delete node but restrict this function to modify head .So except first node use this function*/
struct Node *temp;/*used for free the deleted memory*/
temp=prevNode->link;
prevNode->link=temp->link;
free(temp);
}
struct Node * find_prev(struct Node *trv_ptr,int ele)
{
/*if deleting element found at first node spl operation must be done*/
if(trv_ptr->data==ele)
return trv_ptr;
while((trv_ptr->link)&&(trv_ptr->link->data!=ele))
{
trv_ptr=trv_ptr->link;
}
if(trv_ptr->link==NULL)
{
return NULL;
}
else
return trv_ptr;
}
main()
{
/*finding Node by providing data*/
struct Node *d_link;
struct Node *temp;
d_link=find_prev(head,51);
if(d_link==NULL)
{//data ele not present in your list
printf("\nNOT FOUND\n");
}
else if(d_link==head)
{//found at first node so head is going to change
temp=head;
head=head->link;
free(temp)
}
else
{//other wise found in some where else so pass to function
delete_direct (d_link);
}
}