Delete left subtree node (binary search tree) in C - c

I am working on a C binary search tree library and I'm trying to write a function that will delete the left node of the tree subtree. Here's the struct of my tree:
struct Node {
int value;
struct Node *left;
struct Node *right;
};
typedef struct Node TNode;
typedef struct Node *binary_tree;
The tree is created like this:
binary_tree NewBinaryTree(int value_root) {
binary_tree newRoot = malloc(sizeof(TNode));
if (newRoot) {
newRoot->value = value_root;
newRoot->left = NULL;
newRoot->right = NULL;
}
return newRoot;
}
Adding element to it:
void Insert(binary_tree *tree, int val) {
if (*tree == NULL) {
*tree = (binary_tree)malloc(sizeof(TNode));
(*tree)->value = val;
(*tree)->left = NULL;
(*tree)->right = NULL;
} else {
if (val < (*tree)->value) {
Insert(&(*tree)->left, val);
} else {
Insert(&(*tree)->right, val);
}
}
}
The delleftsubtreenode I did:
void delleftsubtree(binary_tree *tree){
if( (*tree)->value!=NULL )
{
free(&(*tree)->left);
delleftsubtree( &(*tree)->left);
}
else
{
printf("end");
}
}
This method compile,however when I try to call it the program just crash.I dont understand why or how else to do that function.
thank you!

As Olaf said in his comment, you have several problems; calling "free()" with a pointer then attempting to dereference it, and use of double indirection.
binary_tree* tree; /* why use a pointer to a pointer? */
Unless you intend to change the value of the pointer, this is extra work you don't want to be doing. All those "(*tree)->member" expressions are not needed if you follow the usual conventions, and I'm not convinced that you understand what "&(*tree)->left" is doing.
I'm not saying that you should never use a pointer to a pointer, I've done it myself, however you should only do it when there is a reason to be changing the value of the referenced pointer, or with a volatile pointer that might be changed by some external actor; this is unlikely and fairly rare outside of garbage collected and compacted pools (strings in some BASIC interpreters, for example) and the like.
Keep in mind that the "left" subtree of a node in the tree is a binary tree itself; it has both left and right nodes. You need to write a function that deletes a subtree at and below a given node. Once that's understood, removal of the left subtree below a node is as simple as:
void delSubtree(NodeT* subRoot)
{
if (subRoot != NULL)
{
delSubtree(subRoot->left);
delSubtree(subRoot->right);
free(subRoot);
}
}
void delLeftSubtree(NodeT* root)
{
if (root != NULL)
{
delSubtree(root->left);
root->left = NULL;
}
}
If this is homework, you should be solving these problems yourself, and I'm doing you no favors in giving you code samples. Understanding how to use pointers and linked data structures (lists, trees, and other graphs) is essential to becoming an accomplished C programmer.

Related

SIGSEGV on access to pointer to left node of binary tree, even though the pointer is initialized

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.

Unable to print elements in binary search tree

My code is not printing the elements of binary search tree:
//x is the element to be inserted
//structure of program
typedef struct BST
{
int info;
struct BST *left;
//pointer to left node
struct BST *right;
//pointer to right node
}
bst;
//global root variable
bst *root;
void insert(int x)
{
bst *ptr,*sptr=root;
ptr=(bst*)malloc(sizeof(bst));
ptr->info=x;
if(root==NULL)
{
ptr->left=ptr->right=NULL;
root=ptr;
}
while(sptr!=NULL)
{
if(x<sptr->info)
{
sptr=sptr->left;
}
else
sptr=sptr->right;
}
sptr=ptr;
}
edit:
//following is the show function
void show()
{
bst *ptr=root;
while(ptr!=NULL)
{
//it will print untill the ptr is null
printf("%d",ptr->info);
ptr=ptr->left;
ptr=ptr->right;
}
}
Where is the value of root coming from? You're not passing in the value anywhere? Also, it is tough to help when we don't know the design of type bst.
It appears that you have the right idea. Create a node, and give it some data. If the root is null, then the new value is the root of the BST. After that you go ahead and find the first null node either in the left or right subtree of the root using the standard BST behavior. Finally, when you reach the end you go ahead and insert the last node in the proper place.
void insert(int x)
{
bst *ptr, *sptr=root; //<-- the value right here?
ptr = malloc(sizeof(bst));
ptr->info = x;
if(root == NULL)
{
ptr->left=ptr->right=NULL;
root=ptr;
}
while(sptr!=NULL)
{
if(x<sptr->info)
{
sptr=sptr->left;
}
else
sptr=sptr->right;
}
sptr=ptr; // <-- What is this line trying to do?
}
However, where did your updated tree go?
Since in C everything is passed by value, you're running into the problem where you're not seeing your updated tree after you leave this function. You need to go ahead and change the function to return a bst* type, and also maintain the root node during the entire function. Now the first line of code (*sptr = root) makes more sense! Finally, you were not setting the left and right fields of ptr to NULL. This means you were jumping over your if statements.
bst* insert(int x, bst *root)
{
bst *ptr, *sptr=root;
ptr = malloc(sizeof(bst));
ptr->left = NULL;
ptr->right = NULL;
ptr->info = x;
if(root == NULL)
{
ptr->left=ptr->right=NULL;
root=ptr;
return root;
}
while(sptr!=NULL)
{
if(x<sptr->info)
{
sptr=sptr->left;
}
else
sptr=sptr->right;
}
sptr=ptr;
return root;
}
What about the next function?
I just started looking at this one too. I am not used to the global variables in c, so I will go ahead and make two modifications. Let's make this recursive, and pass in the value of the root rather than using the global.
void show(bst *root)
{
if(root == NULL){
return;
}
printf("%d",root->info);
show(root->left);
show(root->right);
}
This will take in some value, and solve the tree recursively, and print as it reaches each node. Thus, it will print the root node (if it exists), and then print the left entire left subtree before it prints the right subtree.
Finally, looking at your main
I added the local variable root and thus you will have to remove the global variable named root outside of your main function. I also set the value of it to null so your first insert will fire correctly.
int main()
{
int i,n,x;
bst *root = NULL; //<-- I added this line of code to remove the global
puts("Enter number of elements");
scanf("%d",&x);
for(i=0;i<x;i++)
{
puts("Enter elements");
scanf("%d",&n);
root = insert(n, root);
}
show(root);
return 0;
}
I hope this helps!

Linked List of BST in C: Breadth First Search

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;

BST pre-order deletion

I would like to know if it is possible to free an entire binary search tree in preorder mode. I've got this function:
void preorder_del(struct s_nodo ** tree)
{
if (*tree != NULL)
{
free(*tree);
preorder_del(&(*tree)->left);
preorder_del(&(*tree)->right);
}
}
I don't think this works, freeing the first leaf of the tree wont let me recall preorder, right?
You should record left and right locally to avoid accessing freed pointer after free(*tree).
if (*tree != NULL)
{
struct s_nodo *l = (*tree)->left;
struct s_nodo *r = (*tree)->right;
free(*tree);
preorder_del(&l);
preorder_del(&r);
}

binary tree: user decides whether it should be a number tree or a word tree in c programming

in my assignment i have to create a binary tree where the user inputs the details.
the first thing the user does is enter 1 if they want to create a number tree or 2 if they want a word tree.
the type of tree they pick is the type it will be for the duration of the running of the program.
there are many functions (and a few structs) that must be written in order to complete the assignment.
my question is how can i write general functions that will work for both int and char?
for example if it is a number tree then the struct for node would include:
int key;
list_t* valueslist;
node* left;
node* right;
but if it was a word list than the struct would look the same except instead of int key it would be char key.
thanks in advance for any help!
The way you may go about it, is to define that data in the struct as a union like so:
struct _Node
{
...
union
{
char* c;
int i;
} data;
};
Than when user makes the choice, access the correct union member according to it.
EDIT
So, let's say the user picked a type, int for instance. And you wish to insert a new value into the tree. (I'll omit error checking fro brevity, but remember to check memory allocation succeeded).
struct _Node* newElem = allocNode();
if (get_user_elected_type() == INT)
newElem->data.i = user_input.i; // Your methods will also need to accept a union
This way has it's serious drawbacks (it's not easy to add a new type, for instance). And most of all it demonstrates how yucky generic programming can be in C. (Using void* can get just as yucky eventually).
There are few solutions to resolve this problem (what you are trying to do is called generic programming)
Use void * key, and fill it with the right data (this is
recommended, because is more generic, but it is also more complicated)
Use a union with 2 fields: an int and a char*
For a homework assignment, the simpler approach will be to use a union type for your data:
struct node {
union {
char *s
int i;
} data;
struct node *left;
struct node *right;
};
and create two sets of functions, one to manage integer values and the other to manage string values:
void insertIntNode(struct node *root, struct node *newNode)
{
if (newNode->data.i < root->data.i)
if (root->left != NULL)
insertIntNode(root->left, newNode);
else
root->left = newNode;
else
if (root->right != NULL)
insertIntNode(root->right, newNode);
else
root->right = newNode;
}
void insertWordNode(struct node *root, struct node *newNode)
{
if (strcmp(root->data.s, newNode->data.s) < 0)
if (root->left != NULL)
insertWordNode(root->left, newNode);
else
root->left = newNode;
else
if (root->right != NULL)
insertWordNode(root->right, newNode);
else
root->right = newNode;
}
bearing in mind you'll need to do some additional memory management for word data:
struct node *createWordNode(char *str)
{
struct node *r = malloc(sizeof *r);
if (r)
{
r->data.s = malloc(strlen(str) + 1);
if (r->s)
strcpy(r->data.s, str);
r->left = r->right = NULL;
}
return r;
}
void destroyWordNode(struct node **n)
{
free((*n)->data.s);
free(*n);
*n = NULL;
}
A more flexible approach is to use a void * to point to your data item, and then delegate all type-aware operations (allocation, assignment, comparison, display, etc.) to other functions which are hidden behind a set of function pointers. For example:
struct node {
void *data;
struct node *left;
struct node *right;
};
struct node *newNode(void *data, void *(*copy)(const void *))
{
struct node *n = malloc(sizeof *n);
if (n)
{
n->left = n->right = NULL;
n->data = copy(data);
}
return n;
}
void insert(struct node *root, struct node *newNode,
int (*compare)(const void *, const void *))
{
if (compare(newNode->data, root->data) < 0)
if (root->left != NULL)
insert(root->left, newNode, compare);
else
root->left = newNode;
else
if (root->right != NULL)
insert(root->right, newNode);
else
root->right = newNode;
}
In the examples above, the details of allocating memory for a node's data element and comparing two data elements are delegated to other functions, and pointers to those functions are passed as parameters to the list management functions. This way you wind up writing a single newNode and insert function, but one that's capable of handling arbitrary node data types. So, for your integer tree, you'd write functions like
void *copyInt(const void *data)
{
const int *src = data;
int *dst = malloc(sizeof *dst);
if (dst)
{
*dst = *src;
}
return dst;
}
int compareInt(const void *lhs, const void *rhs)
{
const int *ilhs = lhs;
const int *irhs = rhs;
if (*ilhs < *irhs)
return -1;
else if (*ilhs == *irhs)
return 0;
else
return 1;
}
then you'd call newNode and insert like
void insertIntValue(struct node *root, int value)
{
struct node *n = newNode(&value, copyInt);
if (n)
insert(root, n, compareInt);
}
The big disadvantage of this approach is that you throw type safety right out the window and into oncoming traffic; because we're using void * for everything. the compiler won't be able to catch type mismatches for us. There's nothing to stop you from passing the wrong copy or comparison function to the generic routines for a particular type.
Which brings us to our second disadvantage - you still need to write a type-aware interface (such as the insertIntValue function above) for each data type you want to support (insertFloatValue, insertStringValue, insertMyObnoxiousDataTypeValue, etc.) along with all of the delegates. Partly to avoid type-safety issues, and partly because our "generic" functions really aren't designed to be called directly. For example, the newNode function expects a pointer as the first parameter, meaning you can't write something like
struct node *n = newNode(10, copyInt);
or
struct node *n = newNode(3.14159, copyDouble);
IOW, you can't pass a literal as the first argument; you must pass the address of an object.
The third main disadvantage is you wind up doing a lot of memory management, which is a pain. You have to create copies of your inputs; otherwise, you wind up assigning the same pointer value (the one passed to newNode) to every node in your tree. Every malloc must have a matching free or you will wind up leaking a lot of memory. You have to be disciplined in how you allocate and deallocate your data items.
Building robust generic containers in C is, frankly, a massive pain in the ass. The only real reason to do it is so you can truly appreciate the value of templates in C++ and generics in Java and C#.

Resources