C - Binary Search Tree's Initial Insertion is NULL - c

I wrote this binary search tree data structure but I can't seem to figure out why or how the first node being printed is ZERO (0).
My code is publicly accessible here on ideone
void insert_tree(tree **l, int x, tree *parent)
{
tree *p; /* temp pointer */
if (*l == NULL) {
p = malloc(sizeof(tree));
p->item = x;
p->left = p->right = NULL;
p->parent = parent;
*l = p; /* link into parent's record */
return;
}
if (x < (*l)->item)
insert_tree(&((*l)->left), x, *l);
else
insert_tree(&((*l)->right), x, *l);
}
I think my confusion lies in the indirection and use of pointers (and pointer to pointer). I understand there are other ways to go about it but I am trying to stick with this particular use of pointers for good reason.

The problem in your code is that you are adding an empty node in the main, without initializing its item, left, or right: when you do this
tree *root = malloc(sizeof(tree));
the root gets assigned an uninitialized chunk of memory. You could place the first element into the first element directly, but this is not necessary: your insertion code already handles the situation when the root is NULL.
When you change your code to assign root a NULL, like this,
tree *root = NULL;
your program works correctly (demo).

Related

Changing value of Linked List without flexiblility in parameters

I have an assignment. I was provided with a function declaration that I cannot modify.
The function declaration is void Insert (Item x, int p, List *L); where I am supposed to change the values of the linked list struct, L.
Now, the code that invokes that method in my main function is
struct List *L = malloc(sizeof(List));
Insert(x,p,L);
How would I change my code so I can pass the address of the struct List instead of making another copy of it?
Like I said, I cannot change the function declaration at all.
/*********************************************************************
* FUNCTION NAME: Insert
* PURPOSE: Inserts an Item in a List.
* ARGUMENTS: . The Item to be inserted (Item)
* . The position in the List
* where the Item should be inserted in (int)
* . The address of the List (List *L)
* REQUIRES (preconditions):
* . The position should be a nonnegative integer
* not greater than the size of the List.
* . The List should not be full.
* ENSURES: . Empty will return false (0).
* . Size will return the first integer greater
* than the size of the List before the call.
* . Peek in the same position will find
* the Item that was inserted.
*********************************************************************/
extern void Insert (Item X, int position, List *L);
What I tried that didn't work was
head->next = L; //changing the next item in list to L
L = head; //changing the address of L so it remains the head of the list
I think this will work:
void Insert (Item x, int p, List *L) {
struct List newnode, last = *L;
newnode = (struct List)malloc(sizeof(struct List));
newnode->item = x;
newnode->next = NULL;
if (last == NULL){
if (p == 0) { *L = newnode; }//first node
else { printf("List is empty and index %d does not exist", p); }
} else if (p == 0) {
newnode->next = *L;
*L = newnode;
}
else{
int counter = 0;
while (1) {
if (counter == p) {
newnode->next = last->next;
last->next = newnode;
break;
}
last = last->next;
counter++;
if (last->next == NULL && counter != p){ break; }
}
}
}
You might want to review the differences (or similarities) between addresses, pointers in C because the answer to your question is that you are already passing the address of your struct List.
I will try and explain a bit. malloc() returns a memory address, AKA a pointer, which is denoted by the * symbol in struct List *L. When I read this line in my head, I would say "L contains a pointer to a struct List object" or "L contains the memory address of a struct List object".
So in that case, when you write Insert(L, x) you are already passing a pointer to your struct List object. And no other copy is being made. So any actions you perform inside of your Insert() function will be acting on the original list.
The only exception to this rule is if you try to reassign L in your insert method, like this:
void Insert(struct List* L, int x) {
L = NULL; // or L = malloc(sizeof(struct List));
}
This will not do what you might expect, but the reason for that is more complicated and something you probably don't need to know for the time being.
There are irregularities with your function declarations.
Your code suggests that you have a function with the following signature :
void Insert (struct List *L, int x);
The header file that you have linked uses a function insert with this signature :
extern void Insert (Item X, int position, List *L);
Note the additional parameter Item X.
There isn't much to go on since you haven't showed us much of your code, but I would suggest taking a look at your missing parameter.
First of all:
How would I change my code so I can pass the address of the struct List instead of making another copy of it?
You are already doing it: void Insert (Item x, int p, List *L); takes pointer to List structure, not structure itself, so you are not copying it.
Secondly:
What I tried that didn't work was head->next = L; //changing the next item in list to L
L = head; //changing the address of L so it remains the head of the list
It won't work. Inside Insert function L is value of pointer to List. So if you change this value inside the function it won't be change outside, as value of L is passed by copy. Only changes to *L will be seen, as they will not change value of L itself, but value of what L is pointing to.
I can assure you, that no L = is needed inside Insert function to complete this task.
Moreover take good look at prerequisites - they should discribe situations, which you do not need to trouble yourself with (probably, I don't know person which created this task, but I would understand it this way) - meaning, if user doesn't comply to them, it's users own fault, anything bad can happen.

Double pointer to binary search-tree node

This might seem like a silly question to some of you and I know that I get things mixed up quite often but I need to understand the code so I can stop obsessing about it and focus on the real matter of why I need to use it.
So, in the code I see several assignments like this:
struct bst_node** node = root;
node = &(*node)->left;
node = &(*node)->right;
is there an invisible parenthesis here?
node = &((*node)->right);
This example is taken from literateprograms.org.
So to me it seems &(*node) is unnecessary and I might as well just write node->left instead, but the code seems to work where I can't make sense of it and I'm wondering if it's because I'm misunderstanding what's happening at those lines. Particularly, at one place in the code where it is deleting a node by constantly moving the "deleted" data to the bottom of the tree to safely remove the node without having to "break things", I'm lost because I don't get how
old_node = *node;
if ((*node)->left == NULL) {
*node = (*node)->right;
free_node(old_node);
else if ((*node)->right == NULL) {
*node = (*node)->left;
free_node(old_node);
} else {
struct bst_node **pred = &(*node)->left;
while ((*pred)->right != NULL) {
pred = &(*pred)->right;
}
psudo-code: swap values of *pred and *node when the
bottom-right of the left tree of old_node has been found.
recursive call with pred;
}
can keep the tree structure intact. I don't understand how this makes sure the structure is intact and would appreciate some help from somebody who knows what's going on. I interpret node being a local variable on the stack, created at the function call. Since it is a double pointer it points to a location in the stack (I assume this, since they did &(*node) previously to the function call), of either it's own stack or the function before, which then points to said node on the heap.
In the example code above what I think it is supposed to do is switch either left or right, since one of them is NULL, and then switch the one that isn't (assuming the other one isn't NULL?) As I said, I'm not sure about how this would work. My question mostly relates to the fact that I think &(*node) <=> node but I want to know if that's not the case etc.
node = &(*node)->right;
is there an invisible parenthesis here?
node = &((*node)->right);
Yes. It is taking the address of the right member of *node. The -> takes precedence over &; see C++ Operator Precedence (-> is 2 and & is 3 in that list) (it's the same general precedence as C).
So to me it seems &(*node) is unnecessary and I might as well just write node->left instead,
Your premise is off. There is no expression &(*node), as explained above, the & applies to the entire (*node)->left, not (*node).
In that code the double pointers are just that, a pointer to a pointer. Just as this works:
int x = 0;
int *xptr = &x;
*xptr = 5;
assert(x == 5);
This is the same, it changes the value of the pointer x:
int someint;
int *x = &someint;
int **xptr = &x;
*xptr = NULL;
assert(x == NULL);
In that code snippet you posted, assigning a pointer to *node changes the value of the pointer that node points to. So, e.g. (pseudo-ish code):
typedef struct bst_node_ {
struct bst_node_ *left;
struct bst_node_ *right;
} bst_node;
bst_node * construct_node () {
return a pointer to a new bst_node;
}
void create_node (bst_node ** destination_ptr) {
*destination_ptr = construct_node();
}
void somewhere () {
bst_node *n = construct_node();
create_node(&n->left); // after this, n->left points to a new node
create_node(&n->right); // after this, n->right points to a new node
}
Noting again that &n->left is the same as &(n->left) because of precedence rules. I hope that helps.
In C++ you can pass arguments to a function by reference, which is essentially the same as passing a pointer except syntactically it leads to code that is a bit easier to read.
That is useful
&(*node)->left <=>&((*node)->left)
The variable edited by this code is *node. I need the context fo this code to give more info

Binary Search Tree node insertion not working properly

I have a homework assignment to implement a binary search tree (create, delete, search). I used the example provided by the teacher but I can't make it work.
Here's my code so far:
void insert_node(int k){
struct node *nodnou,*flow,*parent;
nodnou = (struct node*)malloc(sizeof(node));
nodnou->st = NULL;
nodnou->dr = NULL;
nodnou->nr = k;
if (root==NULL)
{
root = (struct node*)malloc(sizeof(node));
root = nodnou;
}
else
{
flow = (struct node*)malloc(sizeof(node));
parent = (struct node*)malloc(sizeof(node));
flow = root;
parent = root;
while (((flow->st!=NULL) || (flow->dr!=NULL)) && flow!=NULL)
{
if (k<flow->nr)
{
parent = flow;
flow = flow->st;
}
else
{
parent = flow;
flow = flow->dr;
}
}
if (k<flow->nr)
{
parent->st = nodnou;
}
else
{
parent->dr = nodnou;
}
}
}
The way of thinking: This function gets the value of the node we want to insert as the k parameter. The function will only insert the root of the tree (root is global variable).
I think my biggest problem is the while loop that sweeps through the tree to find the spot for the new node.
If I use while (flow!=NULL) it won't work because the flow pointer gets an assignment to something that does not exist. Please help me understand where I am wrong (homework).
Your code has several important flaws, not the least of which is a misunderstanding of how dynamic memory allocation works in C. Never follow a pattern like this:
Type *pointer = malloc(sizeof(Type));
pointer = <<something else>>
It literally leaks memory and gains you nothing in two short lines. This isn't an object-reference based language like Java or C#. Pointers are variables that hold memory addresses. Just like an int can hold an integer, a pointer holds an address. And just like the following example:
int n = 6;
n = 5; //Hmm. Where did the 6 go? Oh yeah, We overwrote it with 5.
You will lose your allocation link doing the same thing with pointers:
struct node *root = malloc(sizeof(*root));
root = nodnou; // memory allocated above is gone. forever leaked.
Pointers are variables. Just like any other variable, they hold values. In the case of a pointer, however, its value is an address. You can have pointers to almost anything in C, including pointers to pointers; variables that hold the address of pointer variables. And I bring them up because they proffer a particularly elegant solution to your insertion requirements.
The following is a general implementation for a binary tree insertion that supports no duplicates in the tree (the code gets even shorter if you allow duplicates). Furthermore, it does this using exactly zero local variables beyond the provided function parameters, and I challenge you to dissect this and determine how it works. It even works on an initially NULL tree root pointer, eliminating the need to special casing if (root) {} else {} logic:
void insert_node(struct node **pp, int k)
{
while (*pp)
{
if (k < (*pp)->nr) // move down left side?
pp = &(*pp)->st;
else if ((*pp)->nr < k) // move down right side?
pp = &(*pp)->dr;
else return; // found the key, no dupes. leave
}
// pp contains the address of the pointer we need to set.
*pp = malloc(sizeof(**pp));
(*pp)->st = (*pp)->dr = NULL;
(*pp)->nr = k;
}
If your tree should support duplicates you need to be consistent about which side they are inserted on, but it shortens the above algorithm considerably:
void insert_node(struct node **pp, int k)
{
while (*pp)
pp = (k < (*pp)->nr) ? &(*pp)->st : &(*pp)->dr;
// pp contains the address of the pointer we need to set.
*pp = malloc(sizeof(**pp));
(*pp)->st = (*pp)->dr = NULL;
(*pp)->nr = k;
}
In either case, invoked on the caller side like this:
struct node *root = NULL;
insert(&root, 5);
insert(&root, 10);
insert(&root, 7);
...etc...
I think you should use while(flow != NULL) and insert your element as flow after that. The way it is right now it will stop in cases when it shouldn't and do weird things whenever it stops. Try working through some examples with pen and paper.
You almost got it. Keep Up!
First you need to understand a bit better memory allocation. In reality, you only need the very first malloc() call in your function. That is the memory you allocate for the node you are appending to the tree during each insert_node() call. All remainingr mallocs you are performing are unnecesary. It seems that you intuitively feel you need to allocate memory for the other pointers you are using, but all of them are temporary and don't require any allocation, just assignment to a valid node before attempting to de-reference them. In fact, those unnecesary allocations will create what is known as a memory leak (memory you request and fail to release) in code like this:
root = (struct node*)malloc(sizeof(node));
root = nodnou;
The second assignmet (root = nodnou) overwrites the result of the previous malloc() call and since you didn't save the overwritten pointer value in any other place, you will no longer be able to release that memory, it will be marked as used for the lifetime of your application!
Next, you can simplify the code that is walking the tree looking for the insertion point.
You seem to worry that flow becomes NULL, but it doesn't matter. The important node is parent. After the while loop ends, it will be pointing to the actual node where the inserted node needs to be linked. Here is a modified version of your code.
void insert_node(int k) {
struct node *nodnou, *flow, *parent;
// this is the only memory allocation that should be done
nodnou = (struct node*)malloc(sizeof(node));
nodnou->st = NULL;
nodnou->dr = NULL;
nodnou->nr = k;
parent = NULL;
if( root == NULL ) {
root = nodnou;
} else {
flow = root;
// We will walk the tree in order until we reach the bottom of the
// tree (flow becomes null). What we are trying to do is to find out
// the node that will become the parent of the new node we are inserting
// It doesn't matter if flow becomes NULL, the important value after the
// while loop ends is parent
while( flow != NULL ) {
// update the current potential parent node
parent = flow;
if( k < flow->nr ) {
// the number we are inserting is lower than the flow node, walk to the left
flow = flow->st;
} else {
// the number we are inserting is greater or equal than the flow node,
// walk to the right
flow = flow->dr;
}
}
// We have reached the bottom, now compare number again with the node that
// will become parent, to find out if we need to link this node to the left
// or to the right of the parent node
if( k < parent->nr ) {
parent->st = nodnou;
} else {
parent->dr = nodnou;
}
}
}
That's it. Try to code the rest of the tree operations and don't hesitate to ask if you become confused. =)

Inserting to Binary Search Trees

So i need to define a destructive function BST insertbst(int i, BST t) that inserts i into BST t. That is, it modifies t to include i and returns the resulting tree. The running time must be O(height t).
I have come up with this version ... but gives me the following error's ideone.com/xhxb6
BST insertbst(int i, BST t) {
if (t == NULL) {
return BSTmake(i, NULL, NULL);
} else if (i < BSTkey(t)) {
return BSTmake(BSTkey(t), insertbst(i ,BSTleft(t)), BSTright(t));
} else {
return BSTmake(BSTkey(t), BSTleft(t), insertbst(i ,BSTright(t)));
}
}
The other functions used in my code can be found in: http://pastebin.com/TVYRE4Nd
Any suggestions to make it destructive, or to change it to avoid my heap error?
.h file: http://ideone.com/QeSTl
Test file: http://pastebin.com/9ca5i4My
You cannot use BSTmake() like you are doing now as you are creating a new BST node everytime it is called, where as you can definitely be sure that the new node should only be created only once for each insert.
Now for the following two statements in main
BST x = BSTmake(2, (BSTmake( 1, NULL, NULL)), (BSTmake(3, NULL,NULL)));
BST y = insertbst(4, x);
I don't know your assumption about where y should be pointing to. In general, implementations of a BST would just call insert on their own, passing in the node to be inserted and the root node. You will see that using this approach makes code much simpler to comprehend. You'll have to change your second line as insertbst( 4, &x) to use this pattern.
You would then have to change the implementation of your insertbst as below:
Pass the second argument as a pointer to the node pointer which represents the parent of the newnode.
if BST *t is null, create a new node and assign the pointer to the node to point to it.
if i < BSTkey(t) or i > BSTKey(t), you need to call insertbst() recursively by passing the pointer to either the left subtree or the right subtree as the second argument.
This is what it may look like:
BST insertbst(int i, BST *t) {
if (*t == NULL) {
*t = BSTMake( i, NULL, NULL);
}
else if (BSTkey(newnode) < BSTkey(*t)) {
insertbst( i, &t->left);
}
else {
insertbst( i, &t->right);
}
}
Also malloc() returns a void * which point to the allocated memory chunk. I am assuming BST is has been typedefed something like typedef struct bst_node * BST. Hence you should change the newnode declaration as BST newnode = (BST)malloc(sizeof(struct bst_node));.

C Linked list only contains first element...not sure what happens to rest

I posted a question a few days ago about a linked list in C. I thought everything was ok then the prof emails us saying that instead of this signature:
int insert_intlist( INTLIST* lst, int n); /* Inserts an int (n) into an intlist from the beginning*/
He accidentally meant:
int insert_intlist( INTLIST** lst, int n); /* Inserts an int (n) into an intlist from the beginning*/
I thought to myself cool now that I have a pointer to a pointer I can move the pointer outside of main and when I return to main I will still have my complete linked list.
He starts off with giving us this:
INTLIST* init_intlist( int n )
{
INTLIST *lst; //pointer to store node
lst = (INTLIST *)malloc(sizeof(INTLIST)); //create enough memory for the node
lst->datum = n; //set the value
lst->next = NULL; //set the pointer
return lst; //return the new list
}
Which is simply to initialize the list like so in main:
if (lst==NULL)
lst = init_intlist(i);
else
insert_intlist(lst, i);
lst is of type INTLIST* so its defined as INTLIST* lst. So I read in some numbers from a text file like 1 3 4 9.
It is supposed to create a linked list from this...so the first number would go to init_intlist(1); And that was defined above. Then it grabs the next number 3 in this case and calls insert_intlist(lst, 3). Well here is my insert_intlist and all I want to do is insert at the beginning of the list:
int insert_intlist(INTLIST** lst, int n )
{
INTLIST* lstTemp; //pointer to store temporary node to be added to linked list
lstTemp = (INTLIST *)malloc(sizeof(INTLIST)); //create enough memory for the node
lstTemp->datum = n; //assign the value
//check if there is anything in the list,
//there should be, but just in case
if(*lst == NULL)
{
*lst=lstTemp;
lstTemp->next=NULL;
}
else
{
lstTemp->next = *lst; //attach new node to the front
*lst = lstTemp; //incoming new node becomes the head of the list
}
return 0;
}
So if the list contained 1 initially this function would simply create a new node and then make this temp node->next point to the head of the list (which I thought was lst) and then reassign the head of the list to this new temp node.
It all looks like it is running right but when I try to print my list to the screen it only prints the number 1.
Anyone have any clues as to what I am doing wrong?
You're being passed a pointer to a pointer. You want to alter the pointer that is being pointed to, not the pointer to a pointer itself. Does that make sense?
if(lst == NULL)
Here you're checking to see if you were passed a NULL pointer. Good practice for error checking, but not for what you were doing right there. If lst is NULL, then you don't even have a pointer to a pointer, can't do anything, and should return a nonzero error code without doing anything else.
Once you're sure your pointer is not NULL, then you look at the pointer it's pointing to (*lst). The pointed-to pointer is the pointer to the first list item. If that pointer is NULL, then you change it to the new item's pointer. Basically, where you use lst you should be using *lst or (*lst). (REMEMBER: the * operator runs after the -> operator! So to get a field in the object pointed to by the pointer that is pointed to by lst [pant, pant], you use (*lst)->whatever.)
P.S. This kind of pointer work is critical to learn to be a good programmer, especially with C.
P.P.S. The other thing you got wrong is that instead of
insert_intlist(lst, i);
you're supposed to call it like
insert_intlist(&lst, i);
... and, for brownie points, check the return code for errors.
The first problem that pops into my mind is that in insert_intlist() you're doing lst = lstTemp;. This should be *lst = lstTemp;. This way you assign to the list pointer that you were supplied rather than to the list pointer pointer (which does not update anything outside of the function).
Since you are using a pointer to another pointer in the insert function, you can change at which memory location the latter actually points to. I changed the insert code a bit and it works fine:
int insert_intlist(INTLIST** lst, int n )
{
INTLIST* lstTemp; //pointer to store temporary node to be added to linked list
lstTemp = (INTLIST *)malloc(sizeof(INTLIST)); //create enough memory for the node
lstTemp->datum = n; //assign the value
//check if there is anything in the list,
//there should be, but just in case
if(*lst == NULL)
{
lstTemp->next=NULL;
*lst = lstTemp;
}
else
{
lstTemp->next = *lst; //attach new node to the front
*lst = lstTemp; //incoming new node becomes the head of the list
}
return 0;
}
EDIT: I see you edited your question. In that case, maybe you are calling the insert function in the wrong way in main(). Try this
int main()
{
INTLIST *head;
head = init_intlist(42);
insert_intlist(&head, 41);
display(head);
return 0;
}
Check to see if lstTemp->next == NULL after lstTemp->next=lst;

Resources