i have implemented a tree in C:
struct node
{
char *key;
struct node *left, *right;
};
// A utility function to create a new BST node
struct node *newNode(char *item)
{
struct node *temp = (struct node *)malloc(sizeof(struct node));
temp->key = item;
temp->left = temp->right = NULL;
return temp;
}
// A utility function to do inorder traversal of BST
void inorder(struct node *root)
{
if (root != NULL)
{
inorder(root->left);
printf("%s\n", root->key);
inorder(root->right);
}
}
/* A utility function to
insert a new node with given key in
* BST */
struct node *insert(struct node *node, char *key)
{
/* If the tree is empty, return a new node */
if (node == NULL)
return newNode(key);
/* Otherwise, recur down the tree */
if (strcmp(key, node->key) < 0)
node->left = insert(node->left, key);
else
node->right = insert(node->right, key);
/* return the (unchanged) node pointer */
return node;
}
/* Given a non-empty binary search
tree, return the node
with minimum key value found in
that tree. Note that the
entire tree does not need to be searched. */
struct node *minValueNode(struct node *node)
{
struct node *current = node;
/* loop down to find the leftmost leaf */
while (current && current->left != NULL)
current = current->left;
return current;
}
/* Given a binary search tree
and a key, this function
deletes the key and
returns the new root */
struct node *deleteNode(struct node *root, char *key)
{
// base case
if (root == NULL)
return root;
// If the key to be deleted
// is smaller than the root's
// key, then it lies in left subtree
if (strcmp(key, root->key) < 0)
root->left = deleteNode(root->left, key);
// If the key to be deleted
// is greater than the root's
// key, then it lies in right subtree
else if (strcmp(key, root->key) > 0)
root->right = deleteNode(root->right, key);
// if key is same as root's key,
// then This is the node
// to be deleted
else
{
// node with only one child or no child
if (root->left == NULL)
{
struct node *temp = root->right;
free(root);
return temp;
}
else if (root->right == NULL)
{
struct node *temp = root->left;
free(root);
return temp;
}
// node with two children:
// Get the inorder successor
// (smallest in the right subtree)
struct node *temp = minValueNode(root->right);
// Copy the inorder
// successor's content to this node
root->key = temp->key;
// Delete the inorder successor
root->right = deleteNode(root->right, temp->key);
}
return root;
}
I have defined a function that takes the Tree as input and deletes a node from it if a condition is met:
void applyFilter(struct node *Tree)
{
if (Tree != NULL)
{
applyFilter(Tree->left);
applyFilter(Tree->right);
for (short i = 0; i < MAX_CONSTRAINTS; i++)
{
if (strchr(Tree->key, constraints[i].letter) != NULL)
{
// delete the word from the tree
Tree = deleteNode(Tree, Tree->key);
break;
}
}
}
}
But i got segmentation fault.
The main goal is to make it work, with as little memory as possible (running).
I think I understand the problem, and it is caused by recursion, because if I delete a node it will give me an empty tree.
If you can give me an example, even a different one i will be really gratefull, because i worked on it a lot, but i am totally stucked.
Thank you!
It happens in minValueNode() after the call of the deleteNode(), because result that the Tree is empty
It happens in minValueNode() after the call of the deleteNode(), because result that the Tree is empty
Basically you already understand what the problem is. You will need to check whether the tree is empty and default the value to something inside minValueNode before you start looping. Because the loop assumes that you have something and if you happen to have nothing, then it's a faulty assumption and causes segfault.
Related
I am trying to understand if this is the fastest way possible to delete a node in the Tree.
struct node *deleteNode(struct node *root, char *key)
{
// base case
if (root == NULL)
return root;
// If the key to be deleted
// is smaller than the root's
// key, then it lies in left subtree
if (strcmp(key, root->key) < 0)
root->left = deleteNode(root->left, key);
// If the key to be deleted
// is greater than the root's
// key, then it lies in right subtree
else if (strcmp(key, root->key) > 0)
root->right = deleteNode(root->right, key);
// if key is same as root's key,
// then This is the node
// to be deleted
else
{
// node with only one child or no child
if (root->left == NULL)
{
struct node *temp = root->right;
free(root);
return temp;
}
else if (root->right == NULL)
{
struct node *temp = root->left;
free(root);
return temp;
}
// node with two children:
// Get the inorder successor
// (smallest in the right subtree)
struct node *temp = minValueNode(root->right);
// Copy the inorder
// successor's content to this node
root->key = temp->key;
// Delete the inorder successor
root->right = deleteNode(root->right, temp->key);
}
return root;
}
It takes a tree and a word and deletes the node from the tree.
The thing I want to understand is when do I reach a "perfect" optimization level? I have worked a lot on this code and I think it is the best way possible. What can I look for more?
First the initial parameter to deleteNode is the root, but you should actually name it just node or subtree.
You should free the key, and strdup the key on node creation.
Finding the value at that node you cannot immediately delete that node.
Either look at the left's max value (left - rights - rightnull node) or rights min value (right - lefts - leftnull node).
You have three pointers, root, xnull node, and the xnull node's other subtree.
One form of deleting is called rotating the pointers.
Notice you do copy the key. In C++ that could be more costly that a pointer copy. In C copying a pointer or at most a struct is similarly costly, though I would favor a pointer copy.
Then you descend the branch again, so there can be optimized a bit; at least more immediate.
struct node *deleteNode(struct node *tree, char *key)
{
// base case
if (tree == NULL)
return tree;
// If the key to be deleted
// is smaller than the tree's
// key, then it lies in left subtree
int cmp = strcmp(key, tree->key); // Thanks to RachidK
if (cmp < 0)
tree->left = deleteNode(tree->left, key);
// If the key to be deleted
// is greater than the tree's
// key, then it lies in right subtree
else if (cmp > 0)
tree->right = deleteNode(tree->right, key);
// if key is same as tree's key,
// then This is the node
// to be deleted
else
{
struct node *result;
// node with only one child or no child
if (tree->left == NULL)
{
result = tree->right;
}
else if (tree->right == NULL)
{
result = tree->left;
}
else
{
// node with two children:
// Get the inorder successor
// (smallest in the right subtree)
struct node **successor = &(tree->right);
while ((*successor)->left != NULL)
{
successor = &(*successor)->left;
}
// rotate pointers:
result = *successor;
result->left = tree->left;
*successor = result->right;
result->right = tree->right;
}
free(tree->key);
free(tree);
return result;
}
return tree;
}
I did not test it. Not successor being an alias of first a right and then of left fields.
I am struggling to implement the following function:
Given a binary search tree, return the smallest node, then move the pointer to the next smallest node in the tree. Upon calling the function another time, it should return the next smallest node and so on.
Any help would be greatly appreciated.
Here is my program so far with some helper functions and their definitions:
#include <stdio.h>
#include <stdlib.h>
/* A binary tree node has data,
the pointer to left child
and a pointer to right child */
struct node {
int data;
struct node *left;
struct node *right;
struct node *parent;
};
struct node *minValue(struct node *node);
struct node *inOrderSuccessor(
struct node *root,
struct node *n)
{
if (n->right != NULL)
return minValue(n->right);
struct node *p = n->parent;
while (p != NULL && n == p->right) {
n = p;
p = p->parent;
}
return p;
}
/* Given a non-empty binary search tree,
return the minimum data
value found in that tree. Note that
the entire tree does not need
to be searched. */
struct node *minValue(struct node *node)
{
struct node *current = node;
/* loop down to find the leftmost leaf */
while (current->left != NULL) {
current = current->left;
}
return current;
}
/* Helper function that allocates a new
node with the given data and
NULL left and right pointers. */
struct node *newNode(int data)
{
struct node *node = (struct node *)malloc(sizeof(struct node));
node->data = data;
node->left = NULL;
node->right = NULL;
node->parent = NULL;
return (node);
}
/* Give a binary search tree and
a number, inserts a new node with
the given number in the correct
place in the tree. Returns the new
root pointer which the caller should
then use (the standard trick to
avoid using reference parameters). */
struct node *insert(struct node *node,
int data)
{
/* 1. If the tree is empty, return a new,
single node */
if (node == NULL)
return (newNode(data));
else {
struct node *temp;
/* 2. Otherwise, recur down the tree */
if (data <= node->data) {
temp = insert(node->left, data);
node->left = temp;
temp->parent = node;
} else {
temp = insert(node->right, data);
node->right = temp;
temp->parent = node;
}
/* return the (unchanged) node pointer */
return node;
}
}
Here are some remarks about your code:
the function minValue is correct, by it should accept a null argument (which is an empty tree) and return null for that.
the function new_node should check for memory allocation failure to avoid undefined behavior.
function inOrderSuccessor should stop scanning when it goes back up to the root node from its right child and return NULL. Also testing for a null parent node will avoid undefined behavior.
you can check for failure in insert and return a null pointer.
Here is a modified version with a functional test:
#include <stdio.h>
#include <stdlib.h>
/* A binary tree node has data,
the pointer to left child
a pointer to right child
and a pointer to parent node
*/
struct node {
int data;
struct node *left;
struct node *right;
struct node *parent;
};
/* Given a binary search tree,
return the node with the minimum data. */
struct node *minValue(struct node *node) {
if (node) {
/* loop down to find the leftmost leaf */
while (node->left != NULL) {
node = node->left;
}
}
return node;
}
struct node *inOrderSuccessor(struct node *root,
struct node *n)
{
if (n == NULL)
return minValue(root);
if (n->right != NULL)
return minValue(n->right);
for (;;) {
struct node *p = n->parent;
/* sanity test */
if (p == NULL)
return NULL;
/* coming back from the left child, return parent node */
if (n != p->right)
return p;
/* coming back from the right child, stop at the root node */
if (p == root)
return NULL;
n = p;
}
}
/* Helper function that allocates a new
node with the given data and
NULL left and right pointers. */
struct node *newNode(int data) {
struct node *node = malloc(sizeof(*node));
if (node) {
node->data = data;
node->left = NULL;
node->right = NULL;
node->parent = NULL;
}
return node;
}
/* Give a binary search tree and
a number, inserts a new node with
the given number in the correct
place in the tree. Returns the new
root pointer which the caller should
then use (the standard trick to
avoid using reference parameters).
Return a null pointer on memory allocation failure */
struct node *insert(struct node *node,
int data)
{
/* 1. If the tree is empty, return a new,
single node */
if (node == NULL) {
return newNode(data);
} else {
struct node *temp;
/* 2. Otherwise, recurse down the tree */
if (data <= node->data) {
temp = insert(node->left, data);
if (temp == NULL) /* return NULL on failure */
return NULL;
node->left = temp;
temp->parent = node;
} else {
temp = insert(node->right, data);
if (temp == NULL) /* return NULL on failure */
return NULL;
node->right = temp;
temp->parent = node;
}
/* return the (unchanged) node pointer */
return node;
}
}
void freeNode(struct node *node) {
if (node) {
freeNode(node->left);
freeNode(node->right);
free(node);
}
}
int main() {
struct node *tree = NULL;
printf("inserting values:");
for (int i = 0; i < 20; i++) {
int data = rand() % 1000;
tree = insert(tree, data);
printf(" %d", data);
}
printf("\n");
printf("enumerate values:");
for (struct node *cur = NULL;;) {
if ((cur = inOrderSuccessor(tree, cur)) == NULL)
break;
printf(" %d", cur->data);
}
printf("\n");
freeNode(tree);
return 0;
}
Output:
inserting values: 807 249 73 658 930 272 544 878 923 709 440 165 492 42 987 503 327 729 840 612
enumerate values: 42 73 165 249 272 327 440 492 503 544 612 658 709 729 807 840 878 923 930 987
Given a binary search tree, return the smallest node, then move the pointer to the next smallest node in the tree. Upon calling the function another time, it should return the next smallest node and so on.
struct node *next_smallest_node(struct node *root, struct node *min)
{
if (!min)
return min_node(root);
if (min->right)
return min_node(min->right);
for (struct node *p = min->parent; p; p = min->parent) {
// Coming from left: return parent
if (min != p->right)
return p;
// Coming from right: stop at root
if (p == root)
return NULL;
min = p;
}
return NULL;
}
min_node() returns the smallest node in a tree:
struct node *min_node(struct node *root)
{
struct node *min = NULL;
for (struct node *i = root; i; i = i->left)
min = i;
return min;
}
Usage:
int main(void)
{
struct node *tree = NULL;
// Fill tree with data ...
struct node *min = NULL;
while (min = next_smallest_node(tree, min)) {
printf("Next smallest = %d\n", min->data);
}
}
Update:
The code in next_smallest_node() now parses the left sub-tree (thanks to #chqrlie).
There's no need to compute the minimum value prior to calling the function.
I have the code for searching a node in a Binary Tree. How can I find the parent of the Node whose KEY is provided as an argument?
Here is the code for Normal search for a Binary Tree -
struct Tree* search(struct Tree *root, int KEY)
{
struct Tree *temp;
if(root == NULL) return NULL; //Base condition
else
{
if(root-> data == KEY) return root;
else
{
temp = search(root-> Lchild, KEY);
if(temp != NULL) return temp;
else return(search(root-> Rchild, KEY));
}
}
return NULL;
}
You are returning the address of the Tree node whose value matches with the KEY, not it's parent. Assuming that all the node values are distinct in this tree, you have to follow the below code to return the parent Tree node address if it's child node (left or right child) value matches with the KEY.
struct Tree* search(struct Tree *root, int KEY){
if(root == NULL){
return NULL;
}
if(root->Lchild != NULL && root->Lchild->data == KEY){
return root;
}
if(root->Rchild != NULL && root->Rchild->data == KEY){
return root;
}
struct Tree *left = search(root->Lchild, KEY);
struct Tree *right = search(root->Rchild, KEY);
if(left != NULL){
return left;
}else if(right != NULL){
return right;
}
return NULL;
}
My findNode is called in my insert function with the address of tree. Since im inserting the first word tree should be NULL but when I debug it skips over this check in my FindWords function.
Im not sure exactly what is the value of tree here if it is not NULL, please advise.
struct TREENODE {
struct TREENODE *parent; // point to parent node of this node
struct TREENODE *left; // point to left child of this node
struct TREENODE *right; // point to right child of this node
char *word; // store a unique word in the text file
int count; // num of times this word appears in the file
};
typedef struct TREENODE NODE;
#define MAXWORDLEN 1000
when I insert the first word, tree should be NULL here because no words exist. But instead of this function returning NULL when no words exists its skips to the if statement (strcmp(word, tree->word == 0)) and causes a segmentation fault.
NODE* findNode(char *word, NODE* tree) {
// return node storing word if exists
if (tree == NULL){return NULL;}
if (strcmp(word, tree->word)==0)
return tree;
if (strcmp(word, tree->word) <0)
return findNode(word, tree->left);
return findNode(word, tree->right);
}
void insertNode(char *word, NODE** address_of_tree ) {
NODE *tree = *address_of_tree;
NODE *node;
node = findNode(word, tree);
if (node == NULL) {
NODE *new_node = malloc(sizeof(NODE));
new_node->word = word;
new_node->parent = *address_of_tree;
new_node->left = NULL;
new_node->right = NULL;
if (tree == NULL) {
*address_of_tree = new_node;
return;
}
if (strcmp(word, tree->word) < 0) {
// word must be added to left subtree
if (tree->left !=NULL) insertNode(word, &tree->left);
else {
new_node->parent = tree;
tree->left = new_node;
}
}
else {
if (tree->right != NULL) insertNode(word, &(tree->right));
else {
new_node->parent = tree;
tree->right = new_node;
}
}
}
else {
// update the count for the word
node->count += 1;
}
}
void printTree(NODE* tree) {
// print each word and its count in alphabetical order
if (tree == NULL) return;
printTree(tree->left);
printf("word: %s\ncount: %d", tree->word, tree->count);
printTree(tree->right);
}
int main() {
// read text from stdin
// each time we read a word
// insert this word into the tree
NODE *tree; // the tree we are using to store the words
char word[MAXWORDLEN];
while(scanf("%s", word) != EOF) {
// insert this word into the tree
insertNode(word, &tree);
}
printTree(tree);
return 0;
}
You are taking input in buffer word and passing it to insertNode(). In the insertNode(), you are doing
new_node->word = word;
which will make all the new node new_node->word pointer point to same buffer word. Any changes in the content of word buffer will reflect in all the nodes nodes->word value. Instead, you should do
new_node->word = strdup(word);
strdup() duplicates the given string. It uses malloc to obtain memory for the new string. Make sure to free it once you are done with it.
You should also initialise the tree with NULL before creating tree
NODE *tree = NULL;
because you are passing tree pointer to insertNode() and in the insertNode() you are checking for tree pointer is NULL or not. So, you should explicitly initialise the tree pointer with NULL.
i was trying to understand this function founded online for deleting a node from a BST. There are some things i can't understand
This is the code :
struct Node* Delete(struct Node *root, int data) {
if (root == NULL) {
return NULL;
}
if (data > root->data) { // data is in the left sub tree.
root->left = Delete(root->left, data);
} else if (data > root->data) { // data is in the right sub tree.
root->right = Delete(root->right, data);
} else {
// case 1: no children
if (root->left == NULL && root->right == NULL) {
delete(root); // wipe out the memory, in C, use free function
root = NULL;
}
// case 2: one child (right)
else if (root->left == NULL) {
struct Node *temp = root; // save current node as a backup
root = root->right;
delete temp;
}
// case 3: one child (left)
else if (root->right == NULL) {
struct Node *temp = root; // save current node as a backup
root = root->left;
delete temp;
}
// case 4: two children
else {
struct Node *temp = FindMin(root->right); // find minimal value of right sub tree
root->data = temp->data; // duplicate the node
root->right = Delete(root->right, temp->data); // delete the duplicate node
}
}
return root; // parent node can update reference
}
Questions :
1) Why it is
if (data > root->data) { // data is in the left sub tree.
root->left = Delete(root->left, data);
shouldn't it be if(data < root->data) ? (same for the two lines of code right after)
2) the function return a pointer to node,does that mean that in the main function i have to do something like this?
int main(){
struct Node *tree=malloc(sizeof(Node));
...
struct Node *new_tree=malloc(sizeof(Node));
new_tree= Delete(tree,24);
So the function replace the old tree with a new tree without the node with the val 24?if i want the function to be of type void should i use double pointers?
For your first question you have right it should be: if(data < root->data).
For the second question not exactly. You obviously should define a pointer head which is the head of the tree and create an function which inserts data to bst, so this function does the malloc. All you nead in your main is the head pointer initialized to NULL in the beginning so it should look like:
int main(){
struct Node *tree=NULL;
int number=...;
...
input_to_bst(&tree,number);
...
new_tree= Delete(tree,24);
Also note that new tree doesn't need to have malloc since your function returns a pointer that already shows to a struct and what you do is that new_tree will also point this struct.
For your final question yes of course you could pass double pointer (in fact I followed this way in the definition of input_to_bst(&tree);).
An example of function input_to_bst definition could be:
void input_to_bst(treeptr* head,int number){
if((*head)==NULL){
(*head)=(treeptr)malloc(sizeof(struct tree));
(*head)->data=number;
(*head)->left=NULL;
(*head)->right=NULL;
}
else{
if(((*head)->data)>number) input_to_bst(&((*head)->left),number);
else (((*head)->data)<number) input_to_bst(&((*head)->right),number);
}
}
where we suppose that we have defined the structs:
struct tree{
int data;
struct tree* right;
struct tree* left;
};
typedef struct tree* treeptr;