I am working on a function that searches through a binary search tree in C for a name that is passed in with the function. However, I am stuck on how to format my loop so that the recusion doesn't simply end when the traversal reaches the left-most node with no children. The traversal has to be pre-order (visit myself, then my left child, then my right child).
My find function is as follows:
tnode *bst_find_by_name(tnode *ptr, const char *nom){
if(ptr != NULL){
if(strcmp(ptr->name, nom) == 0){
return ptr;
}
if(ptr->left != NULL){
return bst_find_by_name(ptr->left, nom);
}
if(ptr->right != NULL){
return bst_find_by_name(ptr->right, nom);
}
}
return NULL;
}
As you can see, currently this simply returns NULL once it reaches the left-most node that does not match the string that was passed into the function. I have to have it return NULL if it does not find a match in the tree, but at the same time I do not want it to return NULL too early before it has a chance to search every node in the tree. Any ideas?
Create a temporary variable that holds the return value. And check to see if bst_find_by_name returned something other than NULL if it returned NULL continue checking the tree.
Something like the following.
tnode *ret = NULL;
if(ptr->left != NULL){
ret = bst_find_by_name(ptr->left, nom);
}
if(ret == NULL && ptr->right != NULL){
ret = bst_find_by_name(ptr->right, nom);
}
return ret;
I prefer to write it like this:
tnode *bst_find_by_name(tnode *ptr, const char *nom) {
// accept a null node, just exit early before dereferencing it
if (ptr == NULL) {
return NULL;
}
// is it this node?
if(strcmp(ptr->name, nom) == 0){
return ptr;
}
// remember, if the first part is true, || will skip the second part
return bst_find_by_name(ptr->left, nom) || bst_find_by_name(ptr->right, nom)
}
// get the matching pointer for left or right subtree, and return
tnode *bst_find_by_name(tnode *ptr, const char *nom) {
// accept a null node, just exit early before dereferencing it
if (ptr == NULL) {
return NULL;
}
// is it this node?
if(strcmp(ptr->name, nom) == 0){
return ptr;
}
tnode * ptrtemp = bst_find_by_name(ptr->left, nom);
if(!ptrtemp) {
ptrtemp = bst_find_by_name(ptr->right, nom);
}
return ptrtemp;
}
Related
The successor of an element in a BST is the element's successor in the
sorted order determined by the inorder traversal. Finding the
successor when each node has a pointer to its parent node is presented
in CLRS's algorithm textbook (Introduction to Algorithms by MIT
press).
Is there a way to find the first value that is bigger than X without parent in the struct? Like:
typedef struct tree tree;
struct tree{
int value;
tree *left;
tree *right;
};
//Function:
tree *find_first_bigger(tree *t, int x){}
I tried working with:
tree *find_first_bigger(tree *t, int x){
if(t == NULL)
return NULL;
if((*t)->value > x)
find_first_bigger((*t)->left, x);
else if((*t)->value < x)
find_first_bigger((*t)->right), x);
else if((*t)->value == x){
if((*t)->right != NULL)
return tree_first_bigger((*t)->right);
else
return tree;
}
}
With this example(it's using letter but there its not a problem), if I try to search the first bigger than N(It should return me O) but it returns me N.
You have done 90% of the job.Allow me to do the remaining 10%.
Since t is a pointer to structure you should use t->left instead of (*t)->left and same applies while accessing right and value fields of the struct.
Now, Just modify your function as:
Add this as first line of your function
static tree* PTR=NULL;
Modify the second if condition as:
if(t->value > x)
{
PTR=t;
find_first_bigger(t->left, x);
}
Modify the second else if condition as:
else if(t->value == x)
{
if(t->right != NULL)
{
t=t->right;
while(t->left!=NULL)
t=t->left;
return t;
}
else return PTR;
}
Hence the correct function is
tree *find_first_bigger(tree *t, int x)
{
static tree* PTR=NULL;
if(t == NULL)
return NULL;
if(t->value > x)
{
PTR=t;
find_first_bigger(t->left, x);
}
else if(t->value < x)
find_first_bigger(t->right, x);
else if(t->value == x)
{
if(t->right != NULL)
{
t=t->right;
while(t->left!=NULL)
t=t->left;
return t;
}
else return PTR;
}
}
In the main function if pointer returned is NULL, this means that :the key itself is the largest key. Feel free for any queries.
I haven't tested this, but I think it should work. Let me know if it is wrong.
//c++ 11
#include<iostream>
using namespace std;
struct BSTNode{
int val;
BSTNode* left;
BSTNode* right;
};
int FindJustLarger(BSTNode*& node, int token, int sofarlarge){
// for invalid inputs it will return intial value of sofarlarge
// By invalid input I mean token > largest value in BST
if(node == nullptr)
return sofarlarge;
else if(node->val > token){
sofarlarge = node->val;
return FindJustLarger(node->left, token, sofarlarge);}
else
return FindJustLarger(node->right, token, sofarlarge);}
int main(){
BSTNode* head = new BSTNode{5, nullptr, nullptr};
FindJustLarger(head, 5, NULL);
delete head;
return 0;}
Some changes you can do in your code:
You have to return the values from the recursive calls
If the value is not found, return NULL. This means returning NULL if t->right == NULL on the last if.
When going to the left, if the value is not found there, the answer must be the node itself. In the case of N, it is the last node where we turn left: O. If it were P, the answer would be T itself.
After all those changes, the code should look like this:
tree *find_first_bigger(tree *t, int x){
if(t == NULL)
return NULL;
if(t->value > x) {
tree *answer = find_first_bigger(t->left, x);
if (answer != NULL)
return answer;
return t;
} else if(t->value < x) {
return find_first_bigger(t->right, x);
} else if(t->value == x) {
if (t->right != NULL)
return tree_first_bigger(t->right);
return NULL;
}
}
You can find the entire code I used to test in this gist.
In your question, you seemed to indicate that you want to find out InOrderSuccessor() of the the given value 'x'.
If 'x' does not necessarily exist in the tree, we need to change the algorithm. Given the example you provided and the problem statement, here is code for finding the next element in a BST.
The key cases are :
No greater element exists, because 'x' is the biggest.
'x' has a right child ?
YES: get left-most child of x's right sub-tree.
NO : return parent.
Key observation is that we don't update the parent pointer, whenever we go right in the tree.
tree *ptr = root;
tree *prnt = NULL;
while (ptr != NULL) {
if (x == ptr->key) {
if (ptr->right != NULL) {
return GetLeftMostChild(ptr->right);
} else {
return prnt;
}
} else if (x > ptr->key) {
ptr = ptr->right;
} else {
prnt = ptr;
ptr = ptr->left;
}
}
Here is the definition for leftMostChild()
tree *GetLeftMostChild(tree *n) {
tree *ptr = n;
while (ptr->left != NULL) {
ptr = ptr->left;
}
return ptr;
}
I've been trying to get this BST working for the last few days, and I am getting stuck on the search function. The logic seems to be correct (unless I'm missing very important details) but there still is something wrong with the code. Could it be because I am dealing with strings? Anyways, here is some code:
EDIT: I've pinpointed somewhere that seems to be going wrong. It turns out that my root is always null. I placed a printf to test if the NULL-case were true, and it always printed true. I've added my tree initialization at the bottom of this question.
The (Updated) Search Function:
//Thank you, M Oehm
node* search(node * tree, char *key)
{
/* char *key is user input */
int cmp;
if(tree == NULL) {
return NULL;
}
cmp = strcmp(key, tree->key);
if(cmp < 0) return search(tree->left, key);
if(cmp > 0) return search(tree->right, key);
return tree;
}
Implementation in main function:
printf("Enter a string to search the tree with: ");
fgets(findNode, MAX_WORD, stdin);
findString = malloc(sizeof(char)*strlen(findNode)+1);
strcpy(findString,findNode);
printf("findString: %s\n", findString);
searched = search(&root, findString);
if(searched == NULL) {
printf("No_such_key\n");
free(findString);
}
else {
printNode(searched);
free(findString);
}
break;
Tree Initialization (via file parsing):
/* Loop through each line in the file*/
while(fgets(buffer, sizeof(buffer), file) != NULL) {
tempToken = strtok(buffer, " \n");
while(tempToken != NULL) {
/* Assign default values */
curr = (node *)malloc(sizeof(node));
curr->left = curr->right = NULL;
curr->key = malloc(sizeof(char)*strlen(tempToken)+1); /* +1 for '\0' */
strcpy(curr->key, tempToken);
curr->frequency = 1;
/* Insert node into tree */
insert(&root, curr);
/* Get next token */
tempToken = strtok(NULL, " \n");
}
}
/* Test insertion worked; close file */
print_inorder(root);
fclose(file);
Insertion Function:
void insert(node ** tree, node * item)
{
/* If no root, item is root */
if(!(*tree)) {
*tree = item;
return;
}
/* If item value is less than node in tree, assign to left */
if(strcmp(item->key,(*tree)->key) < 0) {
insert(&(*tree)->left, item);
}
else if(strcmp(item->key,(*tree)->key) > 0) {
insert(&(*tree)->right, item);
}
else if(strcmp(item->key,(*tree)->key) == 0) {
(*tree)->frequency++;
}
}
The print function shows me that the insertion works properly.
There are two errors in your code: You don't check whetehr the root node, to which you pass a pointer, is null and you don't return the results from the recursive functions.
Your function doesn't modify the tree, so you don't have to pass a pointer to the nodes. That method is useful for functions that modify the tree, e.g. for inserting or deleting nodes. Your function shoul pass a pointer to the root node. That also signals to the user that the tree won't be modified.
So here's a corrected version:
node* search(node *tree, const char *key)
{
int cmp;
if (tree == NULL) return NULL;
cmp = strcmp(key, tree->key);
if (cmp < 0) return search(tree->left, key);
if (cmp > 0) return search(tree->right, key);
return tree;
}
That version must be called like this:
node *hit = search(tree, "bingo!");
Note that this function does the string comparison only once and saves the result in a temporary variable. Your code calls strcmp up to three times.
You don't have to use recursion here. It's even a bit wasteful, because you have to percolate the answer up the first call. Recursion is useful when each step has to maintain a state, which you can represent as local variables. Here, you just change the input node.
Here's a non-recursive variant of the search function:
node* search(node *tree, const char *key)
{
while (tree) {
int cmp = strcmp(key, tree->key);
if (cmp == 0) return tree;
tree = (cmp < 0) ? tree->left : tree->right;
}
return NULL;
}
search(&(*tree)->left, key);
Should be:
return search(&(*tree)->left, key);
Same for the right case.
Try changing your function to something like this.
node* search(node * tree, char * key)
{
if(tree == NULL) {
return NULL;
}
if(strcmp(key,tree->key) < 0) {
return search(tree->left, key);
}
else if(strcmp(key,tree->key) > 0) {
return search(tree->right, key);
}
printf("Success!\n");
return tree;
}
A simple node* would suffice for your problem. No need for double pointers.
i am trying to write a program that will do the following
-read a file from std in
-read each line, and add each line to a binary tree
*if name is already in binary tree,dont add the name to the tree again but update its count of repititions
-print out the binary tree
the file being read in looks something like
dylan
bob
dylan
randall
randall
so when i print out the binary tree i would like it to print out
bob 1
dylan 2
randall 2
i was able to successfully print out the names without worrying about repetitions. I have commented out the blocks of code that mess my program up which is anything interacting with my search function that i added after the fact to take care of repetitions. The code builds a binary tree with each "leave" being a structure of 4 parts,the name,thecount,and the pointers to left and right childs.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct node {
char* name;
int count;
struct node* left;
struct node* right;
};
struct node* addNode(char* string);
void insert(struct node *root, char* stringgg);
void preorder(struct node *root);
int search(struct node* leaf,char* string2find);
int main()
{
char buffer[20];
struct node *root = NULL;
while( fgets(buffer, sizeof(buffer), stdin) != NULL )
{
if(root == NULL)
root = addNode(buffer);
else
insert(root,buffer);
}
preorder(root);
}
struct node* addNode(char* string)
{
struct node *temp = malloc(sizeof(struct node));
temp->name = malloc(strlen(string) + 1);
strcpy(temp->name,string);
temp->left = NULL;
temp->right = NULL;
return temp;
}
void insert(struct node *root, char* stringgg)
{
/* int flag = 5;
flag = search(root,stringgg);
if(flag == 1)
return; */
if(strcmp(stringgg,root->name) < 0)
{
if(root->left == NULL)
root->left = addNode(stringgg);
else
insert(root->left, stringgg);
}
else
{
if(root->right == NULL)
root->right = addNode(stringgg);
else
insert(root->right,stringgg);
}
}
/*int search(struct node* leaf,char* string2find)
{
if(strcmp(string2find,leaf->name) == 0)
{
leaf->count = leaf->count + 1;
return 1;
}
else if(strcmp(string2find,leaf->name) < 0)
{
return search(leaf->left,string2find);
}
else
{
return search(leaf->right,string2find);
}
return 0;
} */
void preorder(struct node *root)
{
if(root == NULL)
return;
printf("%s",root->name);
preorder(root->left);
preorder(root->right);
}
the above code prints out all the names even if there already in a tree. I was hoping that someone would be able to point out my search function errors so that it wont cause a segmentation fault when printing. Possible causes may be my inappropriate use of the return function in which i am trying to return to main if flag == 1 which means match was found so dont addnodes. but if flag does not equal 1 no match was found so go about adding nodes.
at main
while( fgets(buffer, sizeof(buffer), stdin) != NULL ){
char *p = strchr(buffer, '\n');
if(p) *p=0;//remove '\n'
at addNode
temp->count = 1;//initialize counter
return temp;
at insert
void insert(struct node *root, char* stringgg){
int cmp_stat = strcmp(stringgg,root->name);
if(cmp_stat == 0)
root->count++;
else if(cmp_stat < 0) {
if(root->left == NULL)
root->left = addNode(stringgg);
else
insert(root->left, stringgg);
} else {
if(root->right == NULL)
root->right = addNode(stringgg);
else
insert(root->right,stringgg);
}
}
at preorder
printf("%s %d\n",root->name, root->count);
The error is in searching for the very first item in the empty tree — you call
search(root, stringgg)
but root is NULL, so in search() you call
strcmp(string2find, leaf->name)
with leaf == NULL and the program crashes.
A cure: do not search BEFORE you update your tree, but rather search TO update it.
struct node* update(struct node* nd, const char* str)
{
int cmp;
// (sub)tree is empty? - create a new node with cnt==1
if(nd == NULL)
return CreateNode(str);
// test if the node found
cmp = strcmp(str, nd->name);
if(cmp == 0) // YES
nd->count ++; // update the counter
else if(cmp < 0) // NO - search in a subtree
nd->left = update(nd->left, str);
else
nd->right = update(nd->right, str);
return nd; // return the updated subtree
}
Then in main() you just update the tree and store it:
root = update(root, buffer);
Actually, the root value will change only once, on the first call, and all subsequent assignments will not change its value. However that makes the code much more readable.
The problem with my code is that,when a left child value is searched then due do to recursion levels it goes back and checks the right child.And the return gets incorrect. I can't find a way to get past it.
node * search(node *ptr,int key)
{
if(ptr->data==key)
return ptr;
else
{
if(ptr->lchild!='\0')
search(ptr->lchild,key);
else
return '\0';
if(ptr->rchild!='\0')
search(ptr->rchild,key);
else
return '\0';
}
}
Perhaps like this
node * search(node *ptr,int key)
{
node *pwk;
if(ptr == NULL) return NULL;
if(ptr->data==key)
return ptr;
if(NULL!=(pwk=search(ptr->lchild,key)))
return pwk;
if(NULL!=(pwk=search(ptr->rchild,key)))// or return search(ptr->rchild,key);
return pwk;
return NULL;
}
That's right. Try this:
node * search(node *ptr,int key)
{
if(ptr->data==key)
return ptr;
else
{
node *current = NULL;
if(ptr->lchild != NULL)
current = search(ptr->lchild,key);
if(current == NULL) /* not found in the left subtree */
{
if(ptr->rchild != NULL)
current = search(ptr->rchild,key);
}
return current;
}
}
Node *search(Node *ptr, int key)
{
Node *found;
if ( !ptr || ptr->data == key) return ptr;
found = search(ptr->lchild, key);
return (found) ? found : search(ptr->rchild, key);
}
Note: both || and ?: use the short circuit evaluation, which allows me to reduce the number of if(...) conditions to one.
UPDATE: If we are allowed to use the the "crippled ternary" operator gnu-extension, we can also avoid the variable:
Node *search2(Node *ptr, int key)
{
if ( !ptr || ptr->data == key) return ptr;
return search2(ptr->lchild, key) ?: search2(ptr->rchild, key);
}
Adding yet another ternary completely removes the if(...):
Node *search3(Node *ptr, int key)
{
return ( !ptr || ptr->data == key)
? ptr
: search3(ptr->lchild, key) ?: search3(ptr->rchild, key);
}
node * search(node *ptr,int key)
{
Node * p;
// Found the key, return the pointer to the node
if (ptr->data==key)
return ptr;
if (ptr->lchild != NULL)
{
p = search(ptr->lchild,key);
// Found the key, return the pointer to the node
if(p != NULL)
return p;
}
// Didn't find it in the lchild, so search rchild
if (ptr->rchild != NULL)
{
p = search(ptr->rchild,key);
// Found the key, return the pointer to the node
if(p != NULL)
return p;
}
// Not found in left or right child, return NULL
return NULL;
}
Avoid using \0 in this case. \0 is used for a null character. NULL is used for a null pointer. Though both are usually 0, it's preferable to use NULL for pointers. Check this question for more details - What is the difference between NULL, '\0' and 0
The following code snippet is not working right.
void deleteNode(list **start, int pos) {
int currentPosition=0;
list *currentNode;
list *nodToDelete;
currentNode = *start;
if (currentNode == NULL) {
printf("Empty List\n");
} else if (pos == 0 ) {
nodToDelete = *start;
*start = nodToDelete->next;
free(nodToDelete);
} else {
while (currentNode->next != NULL) {
if (currentPosition >= pos -1) {
break;
}
currentPosition++;
currentNode = currentNode->next;
}
if (currentPosition < pos -1 || currentNode->next == NULL) {
printf("No node at given position exists\n");
} else {
nodToDelete = currentNode->next;
currentNode = nodToDelete->next;
free(nodToDelete);
nodToDelete = NULL;
}
}
}
void displayList(list *node) {
if (node == NULL) {
printf("Empty List");
}
while (node != NULL) {
printf("%d\t", node->data);
node = node->next;
}
printf("\n");
}
int main()
{
list *start, *node;
start = NULL;
insertNode(&start, 2);
insertNode(&start, 3);
insertNode(&start, 4);
insertNode(&start, 1);
insertNode(&start, 5);
deleteNode(&start, 3);
displayList(start);
}
When executed the output is
Before Deletion 2 3 4 1 5
After Deletion 2 3 4 0 5
It is supposed to delete 1 but it is inserting 0 at its place.
Here is something that might work --
Replace
currentNode = nodToDelete->next;
with
currentNode->next = nodToDelete->next;
You basically need the node before the nodetodelete to have its next to point to the node that nodetodelete used to point to
Once you've found the node you want to take out of the list, you need to actually take it out. =)
...
nodToDelete = currentNode->next;
currentNode->next = nodToDelete->next;
free(nodToDelete);
...
Besides the problem with currentNode->next = nodToDelete->next; and negative positions you are mixing your ui and your logic. As much as possible you should separate the two.
Sending something to the ui is a way of reporting progress; whether the ui is a command line, a browser or a speaker. Within deleteNode, an empty list or a position that is out of bounds, is not progress. Sequentially both are the same as success - you are done. If you want failure to be to be reported, that should be done where it can lead to a separate sequence...i.e the caller. Also, by mixing in ui, you introduce an unnecessary dependency and failure (what if there's a bug in printf, YOUR function will crash when it doesn't doesn't have to). If you're function returns a defined result, the caller can decide if/how to report that result, including success (your function currently doesn't do so, and the caller has no way telling the difference between sucess or failure).