I'm having trouble building a binary tree in C. I'm suppose to be able to add books to the tree where books with a later publishing year get added to the left and earlier publishing year gets added to the right. I keep getting a run error and i'm not really sure why.
#include <stdio.h>
#include <stdlib.h>
struct book {
char* name;
int year;
};
typedef struct tnode {
struct book *aBook;
struct tnode *left;
struct tnode *right;
} BTree;
BTree* addBook(BTree* nodeP, char* name, int year){
if( nodeP == NULL )
{
nodeP = (struct tnode*) malloc( sizeof( struct tnode ) );
(nodeP->aBook)->year = year;
(nodeP->aBook)->name = name;
/* initialize the children to null */
(nodeP)->left = NULL;
(nodeP)->right = NULL;
}
else if(year > (nodeP->aBook)->year)
{
addBook(&(nodeP)->left,name,year );
}
else if(year < (nodeP->aBook)->year)
{
addBook(&(nodeP)->right,name,year );
}
return nodeP;
}
void freeBTree(BTree* books)
{
if( books != NULL )
{
freeBTree(books->left);
freeBTree(books->right);
//free( books );
}
}
void printBooks(BTree* books){
if(books != NULL){
}
}
int main(int argc, char** argv) {
BTree *head;
head = addBook(head,"The C Programming Language", 1990);
/*addBook(head,"JavaScript, The Good Parts",2008);
addBook(head,"Accelerated C++: Practical Programming by Example", 2000);
addBook(head,"Scala for the impatient",2012);*/
}
You're trying to access an uninitalized pointer nodeP->aBook:
nodeP = (struct tnode*) malloc( sizeof( struct tnode ) );
(nodeP->aBook)->year = year;
You have to allocate space with malloc.
or, store the data directly in the node (with a struct, and not a pointer to a struct).
One problem is that you aren't initializing to NULL:
BTree *head;
should be
BTree *head = NULL;
I would recommend setting your compiler warnings higher. Your two recursive calls are not right and the compiler should have warned about them:
addBook(&(nodeP)->left,name,year );
should be:
addBook( nodeP->left,name,year );
from a parameter passing standpoint. However, this function won't work as it is right now since you are adding when the node pointer is NULL, which means you can't attach a parent to a child since the parent pointer node is gone. I think the logic should look at the applicable right/left node and if NULL, add right there in the routine, else call recursively till a node is found that has a NULL right/left pointer.
Something like this:
BTree *makeNode(char *name, int year)
{
// NOTE: 3 frees required for every node
BTree *nodeP = malloc( sizeof( struct tnode ) ); // 1
nodeP->aBook = malloc( sizeof(struct book) ); // 2
(nodeP->aBook)->year = year;
(nodeP->aBook)->name = malloc(strlen(name) + 1); // 3
strcpy((nodeP->aBook)->name,name);
/* initialize the children to null */
nodeP->left = NULL;
nodeP->right = NULL;
return nodeP;
}
BTree* addBook(BTree* nodeP, char* name, int year)
{
if ( nodeP == NULL )
{
nodeP = makeNode(name,year);
}
else if (year > (nodeP->aBook)->year)
{
if ( nodeP->left == NULL )
nodeP->left = makeNode(name,year);
else
addBook( nodeP->left,name,year );
}
else if(year < (nodeP->aBook)->year)
{
if ( nodeP->right == NULL )
nodeP->right = makeNode(name,year);
else
addBook( nodeP->right,name,year );
}
return nodeP;
}
void printBooks(BTree* books)
{
if (books != NULL) {
printf("book: %s %d\n",books->aBook->name,books->aBook->year);
printBooks(books->right);
printBooks(books->left);
}
}
Related
#include <stdlib.h>
#include <stdio.h>
#include "sorted_tree.h"
int insert_value(int value, struct TreeNode *n) {
if (value < n->value) {
if (n->left_child == NULL) {
struct TreeNode t = {0};
struct TreeNode *tpointer = &t;
tpointer->value = value;
tpointer->left_child = NULL;
tpointer->right_child = NULL;
n->left_child = tpointer;
printf("links eingefügt\n");
}
else {
insert_value(value, n->left_child);
}
return 0;
}
else if (value > n->value) {
if (n->right_child == NULL) {
struct TreeNode t = {0};
struct TreeNode *tpointer = &t;
tpointer->value = value;
tpointer->left_child = NULL;
tpointer->right_child = NULL;
n->right_child = tpointer;
printf("rechts eingefügt\n");
}
else {
insert_value(value, n->right_child);
}
return 0;
}
else {
return 1;
}
}
void print_inorder(struct TreeNode *n) {
if (n == NULL) {
printf("r");
return;
}
else {
print_inorder(n->left_child);
printf("%d ", n->value);
print_inorder(n->right_child);
}
}
int main() {
struct TreeNode t = {0};
struct TreeNode *tpointer = &t;
tpointer->value = 5;
tpointer->left_child = NULL;
tpointer->right_child = NULL;
insert_value(6, tpointer);
printf("%d", tpointer->right_child->value);
printf("%d", tpointer->right_child->value);
}
The first printf() in the main outputs "6" which is correct, but the second one outputs a hug random nummer as if the address has been changed. The 6 should be inserted into the right child node so I expected 66 as the output. Why those this happen and how can I fix it?
struct TreeNode t={0};
struct TreeNode *tpointer=&t;
tpointer->value=value;
Those blocks of code inside the if/else if of insert_value are not correct. This is because struct TreeNode t={0}; creates an automatic variable with lifetime only within the enclosing scope. In this case, the lifetime is only within the if/else if block. Keeping a reference to the variable and using it beyond that will result in Undefined Behaviour.
The solution is to create variables that have lifetimes beyond the function. The most common way to do that is to use dynamically allocated memory:
struct TreeNode *tpointer = malloc(sizeof(*tpointer));
Don't forget to free all dynamically allocated memory when it is no longer needed.
The approach when a first node of a tree is not allocated dynamically as in your program
struct TreeNode t={0};
struct TreeNode *tpointer=&t;
tpointer->value=5;
tpointer->left_child=NULL;
tpointer->right_child=NULL;
is not good. In fact you can not create or use an empty tree.
All nodes in a tree should be allocated dynamically when it is required.
So the pointer n within the function insert_value in general can be equal to NULL for an empty tree. You need to check it. Otherwise statements like this
if(value<n->value){
can invoke undefined behavior.
Also within the function you are setting a pointer to a local object of the type struct TreeNode as for example
struct TreeNode t={0};
struct TreeNode *tpointer=&t;
After exiting the function the local object t will not be alive. So an invalid pointer will be added to the tree.
You need to allocate a new node dynamically.
Also the function in fact returns an arbitrary number 0 or 1 because the function does not return the final value of subsequent recursive calls of the function.
Also it would be logically more consistent to return 1 in case when a new node is added and 0 otherwise.
I would declare and define the function the following way
int insert_value( struct TreeNode **n, int value )
{
if ( *n == NULL )
{
*n = malloc( sizeof( struct TreeNode ) );
( *n )->value = value;
( *n )->left_child = NULL;
( *n )->right_child = NULL;
return 1;
}
else if ( value < ( *n )->value )
{
return insert_value( &( *n )->left_child, value );
}
else if ( ( *n )->value < value )
{
return insert_value( &( *n )->right_child, value );
}
else
{
return 0;
}
}
And if in main you have a pointer to the root node declared like
struct TreeNode *root = NULL;
then the function is called like
insert_value( &root, value );
I want to insert data to the tree using this function:
struct treeNode{
data* val;
struct treeNode *left, *right, *parent;
};
void insert(data *d, struct treeNode **leaf, struct treeNode **leaf_par)
{
if( *leaf == 0 )
{
*leaf = (struct treeNode*) malloc( sizeof( struct treeNode ) );
(*leaf)->val = d;
/* initialize the children to null */
(*leaf)->left = 0;
(*leaf)->right = 0;
/* initialize the parent */
(*leaf)->parent = *leaf_par; //here I receive segmentation fault
}
else if(strcmp(d->name, (*leaf)->val->name) < 0)
{
insert( d, &(*leaf)->left, &(*leaf) );
}
else if(strcmp(d->name, (*leaf)->val->name) > 0)
{
insert( d, &(*leaf)->right, &(*leaf) );
}
}
In main I have:
struct treeNode *root = NULL;
data d1 = {"Smith"};
insert(&d1, &root, NULL);
Segmentation fault is there:
(*leaf)->parent = *leaf_par;
At first time *leaf_par is NULL and I don't know why it's not running correctly. How should I fix my insert function? Without "parent" pointer it's easy, but I have to do that with "parent" and it's not working.
You are trying to dereference NULL; don't do that.
A simple fix for your first insert is:
insert(&d1, &root, &root);
Deeper inserts into the recursion will fix the pointer.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
typedef struct node *tree_ptr;
typedef struct table * Table;
struct node
{
char* element;
tree_ptr left, right;
};
typedef struct table
{
tree_ptr head;
int tree_h;
}table;
char* key = NULL;
Table insert(char* insert_key,Table t)
{
int height = 0;
//tree_ptr ptr = t->head;
tree_ptr *ptr = &(t->head);
key = strdup(insert_key);
tree_ptr new_node = malloc(sizeof(struct node));
new_node->element = key;
new_node->left = NULL;
new_node->right = NULL;
if ( t->head==NULL ){
*ptr = new_node;
t->tree_h = 0;
printf("head:%s\n",t->head->element);
return t;
}
while(1){
if ( strcmp(insert_key, (*ptr)->element)<0 ){
if ( (*ptr)->left ==NULL ){
(*ptr)->left = new_node;
height++;
if ( height > t->tree_h)
t->tree_h = height;
break;
}
else{
(*ptr) = (*ptr)->left;
height++;
}
}
else if ( strcmp(insert_key, (*ptr)->element)>0 ){
if ( (*ptr)->right ==NULL ){
(*ptr)->right = new_node;
height++;
if ( height > t->tree_h)
t->tree_h = height;
break;
}
else{
(*ptr) = (*ptr)->right;
height++;
}
}
else break;
}
return t;
}
int main() {
Table t = malloc(sizeof(table));
t->head = NULL;
t = insert("one", t);
t = insert("two", t);
t = insert("three", t);
printf("%s\n",t->head->element);
return 0;
}
The above is a simplified program, some definition code is given, so I could not change the basic structure, like table, Table, node, tree_ptr, while others could be changed.
What I am trying to implement is a spellchecking, the table stored the head of the tree and some other properties of the tree(which is omitted here), the tree is implemented as an ordered binary tree.
I find that, insert() works well up to two times, after the (*ptr) = (*ptr)->right; the t->head is changed as well. So after using it two times, I lost the head of the tree.
How to modify my insert()?
To insert a node into a tree you first have to search for an empty leaf. Apart from this you do not modify t, so there is no need of writing it back by return value:
void insert( char* insert_key, Table t )
{
// serach empty leaf, where to insert the new node
tree_ptr *ptr = &(t->head); // start at head
while ( *ptr != NULL ) // end if empty leaf is found
{
int cmpRes = strcmp( insert_key, (*ptr)->element );
if ( cmpRes == 0 )
return; // insert_key already is member of tree
if ( cmpRes < 0 )
ptr = &((*ptr)->left); // step down to left child
else
ptr = &((*ptr)->right); // step down to right child
}
// create new node
tree_ptr new_node = malloc( sizeof(struct node) );
new_node->element = strdup( insert_key );
new_node->left = NULL;
new_node->right = NULL;
// place new node at empty leaf
*ptr = new_node;
}
With this recursive function you can print your tree:
void printTree( tree_ptr ptr )
{
if ( ptr == NULL )
return;
printTree( ptr->left );
printf( "%s\n", ptr->element );
printTree( ptr->right );
}
printTree( t->head );
And with this one you can free all nodes of your tree:
void deleteTree( tree_ptr ptr )
{
if ( ptr == NULL )
return;
deleteTree( ptr->left );
deleteTree( ptr->right );
free( ptr );
}
deleteTree( t->head );
t->head = NULL;
The problem is ptr is pointing to the address of the pointer to a struct node, instead of directly pointing to a struct node:
tree_ptr *ptr = &(t->head);
Then when iterating in the while loop, you aren't changing the pointer ptr, but the pointer it is pointing to, which is t->head:
(*ptr) = (*ptr)->left;
This overwrites the pointer, t->head on every iteration, effectively erasing the nodes that pointer pointed to, and leaking memory.
Instead use a normal pointer to the struct node:
struct node* iter = t->head;
...
if ( strcmp(insert_key, iter->element)<0 ){
...
}
else{
iter = iter->left;
....
And I would highly suggest removing those typedefs that hide the pointer, because they make the code hard to read and obfuscate the types, which is not desirable in this context:
typedef struct node *tree_ptr;
typedef struct table * Table;
Also note that if the loop finds a duplicate, the allocated node is not freed, leaking the memory.
I'm pretty new to C, and I don't have a very good grasp of pointers. I'm trying to build a hash table. Here's what I have so far.
I've been cobbling this together from several sources, and I've lost any idea I have of what the pointers are doing. If anyone could even give me a hint as to where my problem is I'd be very grateful.
Header File
typedef struct {
char * word;
char * defn;
struct entry *next;
} entry;
typedef struct {
int size;
struct entry **table;
} hashTable;
typedef hashTable * Dictionary;
Code
#include "hashP.h"
#include <stdlib.h>
#include <string.h>
#include <limits.h>
Dictionary create(int initial_capacity, int delta_capacity){
Dictionary new_table;
int i;
if ((new_table = malloc(sizeof(Dictionary))) == NULL){
return NULL;
}
if ((new_table->table = malloc(sizeof(entry *) * initial_capacity)) == NULL){
return NULL;
}
for(i=0; i < initial_capacity; i++){
new_table->table[i] = NULL;
}
return new_table;
}
/* Insert a key-value pair into a hash table. */
void insertEntry(Dictionary table, char *index, char *value) {
int bin = 0;
entry *newpair = NULL;
entry *next = NULL;
entry *last = NULL;
unsigned long int hashval;
int i = 0;
char *word = index;
char *defn = value;
/* Convert our string to an integer */
while( hashval < ULONG_MAX && i < strlen(word) ) {
hashval = hashval << 8;
hashval += word[i];
i++;
}
bin = hashval % table->size;
next = table->table[bin];
while( next != NULL && next->word != NULL && strcmp(word, next->word ) > 0 ) {
last = next;
next = next->next;
}
/* There's already a pair. Let's replace that string. */
if( next != NULL && next->word != NULL && strcmp( word, next->word ) == 0 ) {
free( next->defn );
next->defn = strdup(defn);
/* Nope, could't find it. Time to grow a pair. */
} else {
if( ( newpair = malloc( sizeof(entry) ) ) == NULL ) {
return NULL;
}
if( ( newpair->word = strdup(word) ) == NULL ) {
return NULL;
}
if( ( newpair->defn = strdup(defn) ) == NULL ) {
return NULL;
}
newpair->next = NULL;
/* We're at the start of the linked list in this bin. */
if( next == table->table[ bin ] ) {
newpair->next = next;
table->table[bin] = newpair;
/* We're at the end of the linked list in this bin. */
} else if ( next == NULL ) {
last->next = newpair;
/* We're in the middle of the list. */
} else {
newpair->next = next;
last->next = newpair;
}
}
}
Sorry for the huge wall of text. It gives me an "Assignment from incompatible pointer type" every time I use "next".
If you declare your struct as:
struct entry { ... };
Then you should use it as
struct entry* next;
If you declare it as:
typedef struct { ... } entry;
Then you should use it as
entry* next;
In this case struct entry* next will still compile (as in your case), but will refer to an incomplete type, which is not the entry that you defined. Assigning from something that is entry* to something that is struct entry* will, therefore, give you an error.
To fix your problem just replace all occurrences of struct entry with entry.
UPDATE: it will not work, because by the time you define entry* next entry itself is not defined yet. You can fix it like this, for example:
typedef struct entry_t {
char * word;
char * defn;
struct entry_t *next;
} entry;
Beside other problems, Dictionary is not defined. From how you use it, I am guessing it is a typedef for a pointer to a hashTable. The allocation is wrong, you allocate the size for a pointer, not for the hashTable structure. You should write:
Dictionary new_table = malloc(sizeof(*new_table));
I cannot figure out how to run this correctly, gives segmentation error. A piece of code is below. Can you look at head too , i am not sure if it is right way of initialising head to null in another file , it is run as follows :
Table tb ;
tb= initialise_table (table_size);
tb = insert(text_words,tb);
//these 3 typedef declarations are in a "some.h" file
typedef struct node * tree_ptr;
typedef char* Key_Type;
typedef struct table* Table;
struct node {
Key_Type element;
tree_ptr left;
tree_ptr right;
};
struct table {
tree_ptr head;
};
Table init_table() {
Table head = NULL;
}
Table insert(Key_Type key ,Table temp ) {
tree_ptr t = (tree_ptr)malloc(sizeof(tree_ptr));
t->element = key;
// t->left = t->right = NULL;
if (temp->head==NULL) {
temp = (Table)malloc (sizeof (Table));
temp->head = t;
printf("empty tree ");
}
else {
temp = insert(t->element,temp);
printf("inserted into ");
}
return temp;
printf("wowo!");
}
The primary issue is in the code which, you say, is used to invoke the functions:
Table tb;
tb = insert(text_words, tb);
You have an uninitialized pointer, tb, which you pass to the function. Inside the function, you have:
Table insert(Key_Type key, Table temp)
{
tree_ptr t = (tree_ptr)malloc(sizeof(*t)); // Fixed size
t->element = key;
// t->left = t->right = NULL;
if (temp->head==NULL)
{
You're therefore accessing (dereferencing) the undefined pointer, and your program is crashing.
You should, I assume, be initializing your table with table_init(), but that function is actually no help whatsoever. It defines and initializes a local variable, but doesn't return anything even though it promises to do so.
Please see Is it a good idea to typedef pointers? The short answer is 'No, it usually isn't a good idea'.
You still have problems even if you fix the calling code like this (a necessary but not sufficient step):
Table tb = NULL;
tb = insert(text_words, tb);
or maybe:
Table tb = init_table();
tb = insert(text_words, tb);
but you need a seriously upgraded version of init_table(), such as:
Table init_table(void)
{
Table root = malloc(sizeof(*head));
root->head = NULL;
return root;
}
Your code in insert() needs to ensure that it does not dereference a null pointer (instead of an indeterminate pointer).
Table insert(Key_Type key, Table root)
{
tree_ptr t = (tree_ptr)malloc(sizeof(*t)); // Fixed size
t->element = key;
t->left = t->right = NULL;
if (root == NULL)
{
root = init_table();
root->head = t;
}
else
{
…
}
return root;
}
Given the Key_Type is a char * in disguise, you may need to review how you save the keys in the tree structure; you may need to use strdup() to copy the data. It is impossible to say for sure without seeing how you are managing the strings that you pass to the insert() function. It could be OK to just save the pointer if the calling code ensures that a new pointer is passed each time. OTOH, if the same pointer is passed each time, you definitely need to copy the data, and using strdup() is a sensible way of doing that. Note that strdup() is standard on POSIX; it is not part of standard C.
Here's one major problem:
tree_ptr t = (tree_ptr) malloc(sizeof(tree_ptr));
should be:
tree_ptr t = (tree_ptr) malloc(sizeof(struct node));
Your code doesn't actually do any binary search. Indeed, it just infinitely recurses creating new nodes. Try something more like this:
#include <stddef.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
typedef struct Node
{
char *element;
struct Node *left;
struct Node *right;
} Node;
typedef struct
{
Node *root;
size_t size;
} Tree;
void Tree_init(Tree *t);
Node *Tree_insert(Tree *t, const char *key);
void Tree_insert_r(Node *subtree, Node *n, size_t size);
void Tree_pre_order_r(Node *subtree);
void Tree_init(Tree *t)
{
t->root = NULL;
t->size = 0;
}
Node *Tree_insert(Tree *t, const char *key)
{
Node *ret = (Node*) malloc(sizeof(Node));
if (ret)
{
ret->left = ret->right = NULL;
if ((ret->element = strdup(key))) /* make a copy of key */
{
if (NULL != t->root)
Tree_insert_r(t->root, ret, t->size);
else
t->root = ret;
++t->size;
}
else
{
free(ret);
ret = NULL;
}
}
return ret;
}
void Tree_insert_r(Node *subtree, Node *n, size_t size)
{
int cmp = strcmp(n->element, subtree->element);
if (cmp < 0 || (cmp == 0 && size % 2 == 0))
{
if (NULL != subtree->left)
subtree = subtree->left;
else
{
subtree->left = n;
return;
}
}
else
{
if (NULL != subtree->right)
subtree = subtree->right;
else
{
subtree->right = n;
return;
}
}
Tree_insert_r(subtree, n, size);
}
void Tree_pre_order_r(Node *subtree)
{
if (NULL == subtree)
return;
fprintf(stdout, "'%s'\n", subtree->element);
Tree_pre_order_r(subtree->left);
Tree_pre_order_r(subtree->right);
}
int main()
{
Tree t;
Tree_init(&t);
Tree_insert(&t, "Hello");
Tree_insert(&t, "World!");
Tree_insert(&t, "etc.");
Tree_pre_order(t.root);
return 0;
}