Adding nodes to Binary Search Tree - c

Disclaimer: This is for an assignment. I am not asking for explicit code answers, only help understanding why my code isn't working.
I am trying to implement a basic Binary Search Tree, but I am having problems with my _addNode(...) function.
Here's the problem. When I walk through my code with the debugger, I notice that leaf nodes are created infinitely on both sides (left and right) so aside from the creation of the root, there is never any point when a leaf node is NULL. The problem is that I am asking my program to create a new node whenever it finds a NULL value where a leaf would be. Therefore, if there are never any NULL values, there will never be any new leaves created, right?
The other issue I'm running into is with my compare(...) function. Stepping through it in the debugger shows it to iterate through the function several times, never actually returning a value. When it returns to the calling function, it drops back into the compare(...) function and loops infinitely. Again, I don't know why this is happening considering I have valid return statements in each if statement.
Here is all the code you'll probably need. If I left something out, let me know and I'll post it.
struct Node {
TYPE val;
struct Node *left;
struct Node *right;
};
struct BSTree {
struct Node *root;
int cnt;
};
struct data {
int number;
char *name;
};
int compare(TYPE left, TYPE right)
{
assert(left != 0);
assert(right != 0);
struct data *leftData = (struct data *) left;
struct data *rightData = (struct data *) right;
if (leftData->number < rightData->number) {
return -1;
}
if (leftData->number > rightData->number) {
return 1;
} else return 0;
}
void addBSTree(struct BSTree *tree, TYPE val)
{
tree->root = _addNode(tree->root, val);
tree->cnt++;
}
struct Node *_addNode(struct Node *cur, TYPE val)
{
assert(val != 0);
if(cur == NULL) {
struct Node * newNode = malloc(sizeof(struct Node));
newNode->val = val;
return newNode;
}
if (compare(val, cur->val) == -1) {
//(val < cur->val)
cur->left = _addNode(cur->left, val);
} else cur->right = _addNode(cur->right, val);
return cur;
}
Edit: Adding the below function(s)
int main(int argc, char *argv[])
{
struct BSTree *tree = newBSTree();
/*Create value of the type of data that you want to store*/
struct data myData1;
struct data myData2;
struct data myData3;
struct data myData4;
myData1.number = 5;
myData1.name = "rooty";
myData2.number = 1;
myData2.name = "lefty";
myData3.number = 10;
myData3.name = "righty";
myData4.number = 3;
myData4.name = "righty";
/*add the values to BST*/
addBSTree(tree, &myData1);
addBSTree(tree, &myData2);
addBSTree(tree, &myData3);
addBSTree(tree, &myData4);
/*Print the entire tree*/
printTree(tree);
/*(( 1 ( 3 ) ) 5 ( 10 ))*/
return 1;
}

Maybe you could try setting right and left to NULL right after malloc:
struct Node * newNode = malloc(sizeof(struct Node));
newNode->left = NULL;
newNode->right = NULL;

Check this line here (or the corresponding for left):
cur->right = _addNode(cur->right, val);
If cur->right == 0, it's fine. But if cur->right != 0, the node that was sitting there will be replaced by the return value of _addNode, which ultimately is not a whole branch, but just one node.
I like to explicitly 0-out values in a struct after a malloc using memset(newNode, 0, sizeof(struct Node)). Others might disagree.

Related

How to use double pointers in binary search tree data structure in C?

I'm working on implementing a binary search tree data structure in C, but I got stuck at the part where you point to the left or right child. I understand that if the value you're inserting is smaller than the root, it goes to the left and to the right if it's larger. I'm just struggling with the double pointers part as shown in the code below. Let's take bs_tree_insert_left for example, I want pos->left_child to point to the left_child in order to place the value given there, but I'm not sure how I would write this.
For context regarding the main function, the numbers in arr[] will be randomly shuffled but I removed that part of the code to keep the post short and compact.
struct node
{
int value;
struct node *left_child;
struct node *right_child;
};
typedef struct node BSTree;
typedef struct node* BSTreePos;
BSTree *bs_tree_make(int value){
// Allocate memory for new node
struct node* origin = (struct node*)malloc(sizeof(struct node));
// Assign data to this node
origin->value = value;
// Initialize left and
// right children as NULL
origin->left_child = NULL;
origin->right_child = NULL;
return (origin);
}
BSTreePos bs_tree_insert_left(int value, BSTreePos pos){
pos->left_child = bs_tree_make(value);
return pos->left_child;
}
void insert_value(int value, BSTreePos pos)
{
if (pos == NULL) return bs_tree_make(value);
if (value < pos->value)
{
pos->left_child = bs_tree_insert_left(value, pos->left_child);
}
else if (value > pos->value)
{
pos->right_child = bs_tree_insert_right(value, pos->right_child);
}
}
int main(void)
{
// Create an array with the values 1, 2, ..., 10 and print out the content.
int n = 10;
int arr[n];
for (int i = 0 ; i < n ; i++) {
arr[i] = i + 1;
}
print_array(n, arr);
BSTree *tree = bs_tree_make(arr[0]);
for (int i = 1 ; i < n ; i++) {
BSTreePos pos = bs_tree_root(tree);
insert_value(arr[i], pos);
}
return 0;
}
You know what, I'm just going to write the correct algorithm that uses the double pointer to maximum effect.
void insert_value(int value, struct node **node)
{
if (*node == NULL) {
*node = malloc(sizeof(struct node));
node[0]->value = value;
node[0]->left_child = NULL;
node[0]->right_child = NULL;
} else if (node[0]->value < value)
insert_value(value, &node[0]->left_child);
else if (node[0]-> value > value)
insert_value(value, &node[0]->right_child);
/* else duplicate value found -- don't insert (from OP's code) */
}
BSTree *tree = NULL;
for (int i = 0 ; i < n ; i++) {
insert_value(arr[i], &tree);
}
node[0]->value is the idiomatic way of accessing a struct through a double pointer.
But let's look at how this works and how much value this gets out of the double pointer. The empty tree is stored as the NULL pointer. This makes initializing the tree and adding a node to the tree the same code. Notice how insert_value takes a double pointer to the tree; this allows it to fill out the root node when adding the first node, or any child node thereof. Thus double pointer which is pointer to pointer is used to update a pointer to a node when we make a new one.
With this algorithm, having a bs_tree_insert_left literally makes no sense. The whole idea is the code that does the insertion doesn't know where it is inserting the node.
Fun fact: the compiler will transform away the recursion for us when compiling with optimizations.

Binary Search Tree in C causing a Heap Corruption Error

So I'm a Python programmer and I'm trying to teach myself C. Just as practice, I've been trying to implement a simple Binary Search Tree in C. I've never had to work with memory allocation or pointers before and its been causing a lot of errors.
My program has been giving me exit code -1073740940 (0xC0000374) which I understand means that the heap has been corrupted. It's a bit of a long program, so I just included the offending function.
This insert function is repeatedly called using a for loop to insert the contents of an array into the binary search tree. The array's contents are 5, 4, 6, 3, 7, 2, 8, 1, 9, and 0 (designed to make the tree balanced).
So the function first has 5 passed to it. The pointer called by pBST->listRoot is NULL (pBST is a pointer to a list struct), so insert 5 as a the root node. This works fine. Then 4 is passed to the function. Since there is already a root, it checks the children of that root. 4 is less than 5 so check 5's left child. The pointer for 5's left child is null, so it attempts to insert 4 as a new node. This is the line that crashes the program:
struct Node* pTemp = calloc(1, sizeof(struct Node));
I've tried a couple variations of this line. Here's the kicker: cLion's debugger cannot reproduce this. When I run it through the debugger, it works perfectly. I think it has to do with the fact that the debugger uses the same memory addresses every time for reproducibility. I left the debugging printf statements and added the code for the Node and binarySearchTree structs.
typedef struct Node BSTNode;
struct Node {
BSTNode* parent;
BSTNode* left;
BSTNode* right;
int* data;
};
typedef struct {
BSTNode* listRoot;
int nodeCount;
} binarySearchTree;
void insert(int Value, binarySearchTree* pBST) {
/*
* This function
*/
//====DEBUG CODE============
int debugIterations = 0;
printf("Now inserting %d \n", Value);
//=====END DEBUG CODE=======
//if no root, make it the root
if (pBST->listRoot == NULL) {
struct Node* newNode = calloc(1, sizeof(binarySearchTree));
(*pBST).listRoot = newNode;
(*pBST).listRoot->data;
(*pBST).listRoot->data = Value;
//pBST->listRoot->data = Value;
pBST->listRoot->parent = NULL;
pBST->listRoot->right = NULL;
pBST->listRoot->left = NULL;
return;
} else {
struct Node* pCursor = pBST->listRoot;
while (1){
printf("Iterations: %d \n", debugIterations);
debugIterations++;
//Check if the number is the same
if (pCursor->data == Value){
printf("ERROR: Tried to insert duplicate value into tree");
return;
}
//Is the value > the node?
else if (pCursor->data < Value) {
//DEBUG
printf("== check succeeded, now value > data\n");
// Is the value a Null?
if (pCursor->right == NULL) {
//DEBUG
printf("Running function to insert %d as a new node to the right\n", Value);
//If yes, then insert the value as a nul
//Create Node
struct Node* pTemp = calloc(1, sizeof(binarySearchTree));
pTemp->data = Value;
pTemp->parent = pCursor;
pCursor->right = pTemp;
pTemp->left = NULL;
pTemp->right = NULL;
return;
}
//If no, then iteravely continue.
else {
printf("Iteravely continuing to the right");
pCursor = pCursor->right;
continue;
}
}
//Is the value < the root?
else {
//DEBUG
printf("== check succeeded, now value < data\n");
//Is the value a Null?
if (pCursor->left == NULL) {
//DEBUG
printf("Running function to insert %d as a new node to the left\n", Value);
//If yes, then insert the value where the null is.
//Create Node
struct Node* pTemp = (struct Node*)calloc(1, sizeof(struct Node));
printf("Successfully declared and allocated memory");
pTemp->data = Value;
pTemp->parent = pCursor;
pCursor->left = pTemp;
pTemp->left = NULL;
pTemp->right = NULL;
return;
}
//If no, then iteravely continue
else{
printf("Iteravely continuing to the right");
pCursor = pCursor->left;
continue;
}
}
}
}
}
The line
struct Node* pTemp = calloc(1, sizeof(binarySearchTree));
is wrong. The structure binarySearchTree has one pointer and one int, but the structure struct Node has 4 pointers, so struct Node should be larger than binarySearchTree and this allocation will allocate less space than required, leading to out-of-range access.
It should be:
struct Node* pTemp = calloc(1, sizeof(*pTemp));
or
struct Node* pTemp = calloc(1, sizeof(struct Node));
Also it looks very weird to store the data int Value in the member int* data; with (*pBST).listRoot->data = Value;. It looks like the member should be int, not int*.

Recursive Binary Tree Insert in C

So I am trying to learn how to create a binary tree in C so far I have got this.
void addRecordsToTree(struct date *in, struct date *root) {
if (root == NULL) {
root = malloc(sizeof(struct date));
root = in;
return;
} else {
//Right side of tree processing
if (compareTwoRecords(in, root) >= 0) {
addRecordsToTree(in, root->right);
return;
} else {
root->right = in;
return;
}
//Left side of tree processing.
if (compareTwoRecords(in, root) < 0) {
addRecordsToTree(in, root->left);
return;
} else {
root->left = in;
return;
}
}
}
int main() {
loadFiles();
struct date treeRoot;
struct date *old = malloc(sizeof(struct date));
old = loadContentsIntoHeap(files[file2014]);
addRecordsToTree(&old[0], &treeRoot);
addRecordsToTree(&old[1], &treeRoot);
addRecordsToTree(&old[2], &treeRoot);
addRecordsToTree(&old[3], &treeRoot);
addRecordsToTree(&old[4], &treeRoot);
addRecordsToTree(&old[5], &treeRoot);
printRecord(7, old);
return 0;
}
The problem is when I check the state of the program in a debugger there is just jumbled up data. I think it could be a type problem somewhere, I find pointers are bit of a mind boggling concept. Im not sure if I have used them right. So here is a screen shot of the debugger.
As you can see at the bottom struct called 'old' is the data I am trying to make the tree out of and treeRoot is where I am trying to place it but I can't understand why I get these garbage values.
Also what is up with the memory address of left and right? am I not creating them correctly.
Another observation I made is when I watch my code in the debugger it seems that root is never == NULL and never gets set, why?
You just did the following:
int x = 2;
int y = x;
y = 5;
Is the second line here necessary or the third one. It is a totally illogical program if you did this. You just did the same thing with a pointer instead of integer. You firstly had a pointer to the base address of dynamic memory then you just overwrote it by initializing it the second time.
And, the iterative approach is far better in comparison to the recursive one. I share the code for inserting a node in a binary tree both recursively and iteratively:
void insert(struct node *temp, struct node **root)
{
while (*root != NULL)
root = (*root)->element < temp->element ? &(*root)->left : &(*root)->right;
*root = temp;
}
#if 0
/* Recursive approach */
void insert(struct node *temp, struct node **root)
{
if(*root == NULL)
*root = temp;
else if ((*root)->element < temp->element)
insert(temp, &(*root)->left);
else
insert(temp, &(*root)->right);
}
#endif
void create_node(int x, struct node **root)
{
struct node *temp = (struct node *) malloc(sizeof(struct node));
if (temp == NULL)
printf("Unable to allocate memory. Free some space.\n");
else
{
temp->left = NULL;
temp->right = NULL;
temp->element = x;
insert(temp, root);
}
}
int main()
{
struct node *root = NULL;
create_node(1, &root);
create_node(2, &root);
create_node(3, &root);
return 0;
}
I saw an additional Problem in your "addRecordsToTree"-function:
the IF-block of the
"//Right side of tree processing"
will allways return from the function. regardless wether the "IF"-Expression is true or false.
So your left-leaves of thew tree will never be inserted. So you probalby should check/debug that function.

"Invalid operands to binary expressions" Error

I keep getting this error:
"Invalid operands to binary expressions ('int' and 'Primenumber'(aka
'struct number'))
on two lines that i've marked below with **'s. Whats wrong and how can I fix it? The code is for a data structure assignment.
typedef struct number
{
int num[3];
} Primenumber;
typedef struct node
{
int data;
struct node *next;
} Node;
Node *head = NULL;
int AddPrimeNumber(Primenumber x)
{
Node *n;
Node *newNode;
//Create a new node
newNode = (Node*)malloc(sizeof(Node));
**newNode->data=x;**
newNode->next=NULL;
if (head == NULL)
{
head = newNode;
}
else
{
n= head;
while (n-> next != NULL)
{
n= n->next;
}
n->next= newNode;
}
return 0;
}
int SearchPrimeNumber(Primenumber x)
{
int pos=0;
Node *n = head;
while (n != NULL)
{
**if (n->data ==x)**
{
return pos;
}
else
{
pos++;
n= n->next;
}
}
return 0;
}
int DisplayPrimeNumber()
{
Node *n =head;
while (n != NULL)
{
printf("%d -> ", n->data);
n= n->next;
}
printf("\n");
return 0;
}
First time
newNode->data=x;
you are assigning an struct of PrimeNumber type to an int,
Second time you are comparing an struct of PrimeNumber type to an int
n->data ==x
both are wrong, may be what you want is
typedef struct Node {
PrimeNumber data;
struct Node *next;
};
the assignment part will be ok, but you will have to elaborate on the comparison part I would use a function
areEqualPrimeNumbers(PrimeNumber *x, PrimeNumber *y)
{
return ((x->num[0] == y->num[0]) && (x->num[1] == y->num[1]) && (x->num[2] == y->num[2]));
}
or if you want to use memcmp
areEqualPrimeNumbers(PrimeNumber *x, PrimeNumber *y)
{
return (memcmp(x->num, y->num, sizeof(x->num)) == 0);
}
and then
areEqualNodes(&x, &(n->data));
the memcmp version is better since it does not depend on the definition of PrimeNumber.
newNode->data is of type int while x is of type Primenumber (struct number). C provides no operation on entire structure except assignment.
In first **s you are trying to assign x of type Primenumber to n->data of type int;This is your first mistake.
In second **s you are trying the same for comparing;This is your second mistake.
And,please mark your errors with simple //error comments,not using **s ;).
// always comment your code so others (or yourself later)
// do not have to 'reverse engineer' it
// <-- declutter code by just defining a struct type, not typedef struct
struct PrimeNumber
{
int num[3];
};
struct Node
{
int data;
struct node *next;
};
// <-- due to better definition of struct, need to use the 'struct' modifier
struct Node *head = NULL;
// <-- pass as pointer so compiler does not generate two hidden calls to memcpy())
// <-- nor allocate memory space that is unusable for anything else
//int AddPrimeNumber(PrimeNumber x)
// <-- due to better definition of struct, need to use the 'struct' modifier
int AddPrimeNumber(struct PrimeNumber* x)
{
// <-- due to better definition of struct, need to use the 'struct' modifier
// <-- initialize local variables to a 'safe' value
struct Node *n = NULL;
struct Node *newNode = NULL;
//Create a new node
// <-- always check the returned value from malloc() to assure operation successful
if( NULL == (newNode = malloc(sizeof(Node)) ) )
{ // then malloc failed
perror( "malloc failed" );
exit( EXIT_FAILURE );
}
// implied else, malloc successful
// <-- x contains 3 integer fields, newNode contains 1 integer field.
// <-- what were you expecting to happen?
// <-- perhaps you meant: newNode->data = x->num[0]; which only copies one int, not all three
**newNode->data=x;**
newNode->next=NULL;
if (head == NULL) // this handles special case of empty list
{
head = newNode;
}
else
{ // else, list already contains one or more nodes
n= head;
while (n->next != NULL)
{
// step to next node in linked list
n= n->next;
}
// <-- currently 'n' points to last node in linked list
// <-- add new node to end of linked list
n->next= newNode;
}
return 0;
} // end function: AddPrimeNumber
// similar considerations need to be applied to the other posted function

insertion binary search tree in C

I've been stuck on the insertion part of the binary search tree. I get so confused with nested structs. The basic idea of this program is to create a bst that is able to hold names and double values which get stored by value (obviously).
Example: I want to store
Jane 3.14
John 3.233
Luke 6.4
Mike 1.4
so the bst would look like
3.14
/ \
1.4 3.233
\
6.4
however I'm having trouble with the insertHelper recursion portion of the code. The hash table is a bonus part of the code that I'll try implementing at a later time. Thank you for your help!
typedef struct name_val // holds name and value
{
char *name;
double value;
}NAME_VAL;
typedef struct node //binary search tree
{
NAME_VAL *nV;
struct node *left;
struct node *right;
}NODE;
struct tmap_struct //handle for bst and hashtable
{
int nL; //nodes on left
int nR; //nodes on right
NODE *root;
NODE **table;
};
int tmap_insert(TMAP_PTR hashTree, char * name, double val)
{
if(hashTree->root == NULL)
{
NODE *bst = (NODE *)malloc(sizeof(NODE));
NAME_VAL *root = (NAME_VAL *)malloc(sizeof(NAME_VAL));
bst->nV = root;
bst->nV->value = val;
strcpy(bst->nV->name, name);
hashTree->root = bst;
hashTree->nL = 0;
hashTree->nR = 0;
}
else
insertHelper(hashTree->root, val, name);
}
void insertHelper(TMAP_PTR hashTree, int val, char * name)
{
if(val < hashTree->root->nV->value)
{
if(hashTree->root->left == NULL)
{
hashTree->root->left = (NODE *)malloc(sizeof(NODE));
hashTree->root->left->nV = (NAME_VAL *) malloc(sizeof(NAME_VAL));
strcpy(hashTree->root->left->nV->name, name);
hashTree->root->nV->value = val;
(hashTree->nL)++;
}
else
insertHelper(hashTree->root->left, val, name);
}
else
{
if(hashTree->root->right == NULL)
{
hashTree->root->right = (NODE *)malloc(sizeof(NODE));
hashTree->root->right->nV = (NAME_VAL *)malloc(sizeof(NAME_VAL));
strcpy(hashTree->root->left->nV->name,name);
hashTree->root->nV->value = val;
(hashTree->nR)++;
}
else
insertHelper(hashTree->root->right, val, name);
}
}
I doubt this compiles. Is that the problem you're having?
From what I can see, you have declared insertHelper with the wrong type for its first parameter. It should take NODE* values, not TMAP_PTR values. That's because you always call it with nodes out of your tree.
So the first part of the function should look like this:
void insertHelper(NODE *node, int val, char * name)
{
if(val < node->nV->value)
{
if(node->left == NULL)
{
node->left = (NODE *)malloc(sizeof(NODE));
node->left->nV = (NAME_VAL *) malloc(sizeof(NAME_VAL));
strcpy(node->left->nV->name, name);
node->left->nV->value = val;
}
else
insertHelper(node->left, val, name);
}
//.....
Note that I removed the line:
(hashTree->nR)++;
It hardly even makes sense to track this information, unless maybe you do it at the node level.
But if you must, you could have insertHelper recursively return a positive or negative value to indicate what side it inserted on. But that doesn't makes sense. What is it on the right of? You may have inserted it on the right of a node that was in the left half of the tree.
If you store this information on each node, you can recursively update the node above as you return from insertHelper. Maybe that's what you were trying to do. Balanced tree implementations do something similar - AVL trees store the maximum depth of the tree at a node and use that to do branch rotations for rebalancing.
You'll have to adapt mine(It's almost standard C besides the unneeded template and class), but it's a similar algorithm: (I believe, I didn't look at any source for my own purposes.)
template<typename T>
class BST {
protected:
typedef struct node_t {
struct node_t * dir[2];
T data;
} node;
node * root;
void insert_node(node * active_node, T data){ //call with node *root;
int next = data < active_node->data ? 0 : 1;
if(active_node->dir[next] == NULL){
active_node->dir[next] = new node;
active_node->dir[next]->dir[0] = NULL;
active_node->dir[next]->dir[1] = NULL;
active_node->data = data;
} else
insert_node(active_node->dir[next], data);
}
public:
BST() : root(new node){root->dir[0] = NULL; root->dir[1] = NULL; root->data = 0;}
~BST(){}
}

Resources