Passing arguments in a BST insert function by extracting data using sscanf - c

I am writing a program that reads students (id, name, surname, grade) from a text file (line by line) and then stores them to a Binary Search Tree by using id as a key. To read each line i use fgets() and to extract the words from the line is use sscanf().
struct TreeNode* root = NULL;
FILE *text;
char *id, *onoma, *epitheto, *word, *line;
onoma = (char *)malloc(20 * sizeof(char));
epitheto = (char *)malloc(30 * sizeof(char));
id = (char *)malloc(9 * sizeof(char));
float vathmos;
text = fopen("students.txt", "r");
if (text == NULL) {
printf("Cannot read from the file!");
exit(1);
}
This is the loop where the data are extracted for each student:
while (fgets(line, 50, text) != NULL) {
printf("%d \n", root);
sscanf(line, "%s %s %s %f", id, onoma, epitheto, &vathmos);
printf("%s %s %s %.3f \n", id, onoma, epitheto, vathmos);
root = Insert(root, id);
}
And this is the insert function for the node:
TreeNode *Insert(struct TreeNode* root, char *data) {
if (root == NULL) { // empty tree
root = CreateNewNode(data);
}
// if data to be inserted is lesser, insert in left subtree.
else if ((strcmp(data, root->id)) <= 0) {
root->left = Insert(root->left,data);
}
// else, insert in right subtree.
else if ((strcmp(data, root->id)) > 0) {
root->right = Insert(root->right,data);
}
return root;
}
When I insert nodes "by hand" e.g.:
root = Insert(root, "AY881159");
root = Insert(root, "AA564510");
root = Insert(root, "AB784123");
the program works and the nodes are created and the tree can be manipulated.
But when the tree is created in the fgets() loop by getting the data from the sscanf(), there is a problem. While the variables store the data correctly (that's why I have the printf() after the sscanf() to check this), the root seems to reset and only the last student is kept in the tree.
Any ideas?
The code for the nodes is:
typedef struct TreeNode {
char *id;
struct TreeNode *left;
struct TreeNode *right;
} TreeNode;
and
TreeNode *CreateNewNode(char *data) {
struct TreeNode *NewNode = (TreeNode *)malloc(sizeof(TreeNode));
NewNode->id = data;
NewNode->left = NewNode->right = NULL;
return NewNode;
}

The code you posted cannot be compiled, it makes it more difficult to answer questions.
You create all nodes in the loop from the same id buffer. You need to make a copy of the buffer, either when calling Insert of preferably in the CreateNewnode() function. You did not post the code for that, nor did you post the definition of type TreeNode. Here is a possibility:
TreeNode *CreatNewNode(const char *data) {
TreeNode *node = calloc(1, sizeof(*node));
if (node != NULL) {
node->id = strdup(data);
node->left = node->right = NULL;
}
return node;
}
There is no need to allocate the arrays for the parse phase, local char arrays are fine for this, but data you store to the tree should be duplicated so you can reuse the buffers from the parsing code. Make the argument to Insert a const char *data to indicate the buffer will not be modified, nor owned by the tree after the call.
You must pass extra information to scanf to prevent buffer overflow.
Here is a modified version of the calling code:
int main(void) {
struct TreeNode *root = NULL;
FILE *text;
char id[9], onoma[20], epitheto[30], line[256];
float vathmos;
text = fopen("students.txt", "r");
if (text == NULL) {
printf("Cannot read from the file!");
exit(1);
}
// This is the loop where the data are extracted for each student:
while (fgets(line, sizeof line, text) != NULL) {
printf("%d \n", root);
if (sscanf(line, "%8s %19s %29s %f", id, onoma, epitheto, &vathmos) == 4) {
printf("%s %s %s %.3f \n", id, onoma, epitheto, vathmos);
root = Insert(root, id);
} else {
printf("invalid line: %s", line);
}
// I'm curious how you are going to store the other data...
}
...
}
The Insert function can be simplified:
TreeNode *Insert(struct TreeNode *root, const char *data) {
if (root == NULL) { // empty tree
root = CreateNewNode(data);
} else {
if (strcmp(data, root->id) <= 0) {
// if data to be inserted is lesser or equal, insert in left subtree.
root->left = Insert(root->left, data);
} else {
// else insert in the right subtree
root->right = Insert(root->right, data);
}
}
return root;
}
A better API for InsertNode would be to take a pointer to the root pointer and return a pointer to the new node:
TreeNode *Insert(struct TreeNode **nodep, const char *data) {
while (*nodep != NULL) {
if (strcmp(data, (*nodep)->id) <= 0) {
nodep = &(*nodep)->left;
} else {
nodep = &(*nodep)->right;
}
}
return *nodep = CreateNewNode(data);
}

Related

I have a segmentation fault that I can't seem to find in C

Hey I have a segmentation fault somewhere when I assign child. It seems to be in my for loop specifically in my add method when i==2.
When you compile the code, you input your name, the function, and "father('name of parent', 'name of child')" or respectively the same format for the mother. There are only parents no other child besides the root child.
UPDATE: I put my variables inside main.
For Example Input:
Brandon
add
father(a,Brandon)
Expected result:
father
a
Brandon
But it's:
father
a
Code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
//definition of whats in a node
typedef struct BST {
char *data;
int generation;
struct BST *left;
struct BST *right;
}node;
//starting nodes
node *root = NULL;
//method declarations
node *create(char[]);
node scan(node *, char[]);
void delete (char[]);
void preorder (node *);
void add (char[]);
void ridChild (node *);
void quit(void);
int main(){
char name[12];
char array[50];
char option[12]; //options: add, remove or print
char parent[12];
node *temp;
printf ("Please enter your name :");
fgets(name, 12, stdin);
name[strlen(name) -1] = '\0';
temp = create(name); //create root
root = temp;
printf("Please specify whether to add or delete an entry, or print the tree\n");
fgets(option, 12, stdin);
option[strlen(option) -1] = '\0';
if(strcmp(option,"quit")!=0) {
do {
if(strcmp(option,"add")==0) {
add(name); //put the add method here
}
if(strcmp(option,"delete")==0) {
//put the delete method here
delete(array);
}
if(strcmp(option,"print")==0) {
preorder(root); //print method
}
printf("Please specify whether to add or delete an entry, or print the tree\n");
getchar();
fgets(option, 12, stdin);
option[strlen(option) -1] = '\0';
} while (strcmp (option, "quit") != 0);
}
quit();
}
//methods
void quit(){
exit(1);
}
node *create(char inputName[]){
node *temp;
temp = (node *) malloc (sizeof(node));
temp->data = inputName;
temp->left = temp->right = NULL;
return temp;
}
void preorder (node * root){
if (root != NULL) {
printf ("%s\n", root->data);
preorder (root->left);
preorder (root->right);
}
}
void add(char array[]){
node *child = NULL, *parent = NULL;
char *pch;
printf ("Please specify a relation to add\n");
fgets(array, 50, stdin); // store the command in array
array[strlen(array) -1] = '\0';
pch = strtok (array, "(,)");
int parentdir = 2; //father = 0, mother = 1;
for (int i = 0; pch != NULL && i < 3; i++) {
if(i==0) {
if (strcmp (pch, "father") == 0) { //father
parentdir = 0;
}
if (strcmp (pch, "mother") == 0) { //mother
parentdir = 1;
}
}
else if(i==1) { //make parent
parent = create (pch);
}
else if (i==2) {
//where we assign
if (parentdir == 0) { //father
//find function to find the node and return
*child = scan(root, pch);
//assign left of the found node as the temp (father)
child->left = parent;
}
else if (parentdir == 1) { //mother
//find function to find the node and return
*child = scan(root, pch);
//assign left of the found node as the temp (mother)
child->right = parent;
}
}
printf ("%s\n", pch); //REFERENCE PRINTING
pch = strtok (NULL, "(,)");
}
}
void delete(char array[]){
node *toDelete;
//takes in name of node to be deleted
//scan method to find the node to delete and deletes all of the children of the node first before deleting
printf ("Please specify a name to delete\n");
fgets(array, 50, stdin);
array[strlen(array) -1] = '\0';
*toDelete = scan(root, array); //return which node to delete
//helper method here to go through and delete each children
ridChild(toDelete);
}
void ridChild(node * trash){
if(trash->left == NULL && trash->right == NULL) { //no parents
free(trash);
}
else if(trash->left == NULL && trash->right != NULL) { //have mother
ridChild(trash->right);
}
else if(trash->left != NULL && trash->right == NULL) { //have father
ridChild(trash->left);
}
else if(trash->left != NULL && trash->right == NULL) { //have both
ridChild(trash->left);
ridChild(trash->right);
}
}
node scan(node * temp, char inputName[]){
//returns node that is searched for associated with one of parents
if (temp != NULL) {
if(strcmp(temp->data,inputName)==0) {
return *temp;
} else {
scan (temp->left, inputName);
scan (temp->right, inputName);
}
}
return *temp;
}
The problem is the scan function which I recommend by default should return an empty structure, for clarity. I have included some revised code below.
Insert to the beginning of the function:
node ret;
And then instead of always returning *temp at the end, use the following to conclude the function:
if (temp != NULL) {
ret=*temp;
}
return ret;
Also some minor changes along with this are needed in the add function, beginning with child now defined as follows:
node child, *parent = NULL;
...
child = scan(root, pch);
child.left = parent;

Hashtable & BST Implementation

I'm working on an assignment that can accept commands from keyboard to insert people into a hashtable. After someone is inserted into the hastable, they can be "friended" with another person in the table. The way I have to store who is friends with who is a binary search tree. What I have to do is for the hashtable the first part of the node would be the person's name, then then next is a pointer to the bst for that person's friends, and finally the end is a pointer to the next node for chaining if there is a collision. Here is a visual example...
I have been able to insert people into my table, but the problem that I can not figure out is how to access the BST and add in friends for that person. Here is my code...
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// Structures
struct linkedList{
char *name;
struct linkedList *next;
struct linkedList *tree;
};
typedef struct linkedList list;
struct hashTable{
int size;
list **table;
};
typedef struct hashTable hash;
struct bst{
char *val;
struct bst *l;
struct bst *r;
};
int main(){
char input[50];
char *ch, cmd_str[50], name[30];
// Make hash table for names
hash *profiles;
profiles = createHashTable(1001);
while(1){
// Get keyboard input
fgets(input, 50, stdin);
input[strlen(input)-1] = '\0';
// parse the input
ch = strtok(input, " ");
strcpy(cmd_str,ch);
if(strcmp("CREATE", cmd_str) == 0){
ch = strtok(NULL, " \n");
insertPerson(profiles, ch);
}
else if(strcmp("FRIEND", cmd_str) == 0){
ch = strtok(NULL, " \n");
strcpy(name, ch);
ch = strtok(NULL, " \n");
friendPerson(profiles, name, ch);
}
else if(strcmp("UNFRIEND", cmd_str) == 0){
ch = strtok(NULL, " \n");
}
else if(strcmp("LIST", cmd_str) == 0){
ch = strtok(NULL, " \n");
printFriends(profiles, ch);
}
else if(strcmp("QUERY", cmd_str) == 0){
}
else if(strcmp("BIGGEST-FRIEND-CIRCLE", cmd_str) == 0){
}
else if(strcmp("INFLUENTIAL-FRIEND", cmd_str) == 0){
}
else if(strcmp("EXIT", cmd_str) == 0){
printf("\nExiting...\n");
return 0;
}
else{
printf("\nBad Command.\n");
}
}
}
// Creates Hash Table
hash *createHashTable(int size){
int i;
hash *new_table;
if((new_table = malloc(sizeof(hash))) == NULL)
return NULL;
if((new_table->table = malloc(sizeof(list *) * size)) == NULL)
return NULL;
for(i=0; i < size; i++)
new_table->table[i] = NULL;
new_table->size = size;
return new_table;
}
// hashing function
int keyHash(char *name){
int c;
unsigned long key;
while(c = *name++)
key = ((key<<5) + key) + c;
return key%1000;
}
// insert a person into the hash table
void insertPerson(hash *profiles, char *name){
struct linkedList *item = (struct linkedList*)malloc(sizeof(struct linkedList));
int hash_val = keyHash(name);
item->name = name;
item->next = NULL;
item->tree = new_tree;
// Collision case
if(profiles->table[hash_val] != NULL){
while(profiles->table[hash_val]->next != NULL){
profiles->table[hash_val] = profiles->table[hash_val]->next;
}
profiles->table[hash_val]->next = item;
}
// Empty cell
else{
profiles->table[hash_val] = item;
}
}
// friend two people inside the hash table
void friendPerson(hash *profiles, char *name, char *_friend){
int hash1 = keyHash(name);
int hash2 = keyHash(_friend);
// check if the names are already in system
if(!profiles->table[hash1]){
printf("%s is not yet in the system", name);
return;
}
if(!profiles->table[hash2]){
printf("%s is not yet in the system", _friend);
return;
}
// add first friend
if(strcmp(profiles->table[hash1]->name, name) == 0){
insertBST(profiles->table[hash1]->tree, _friend);
}
else{
while(profiles->table[hash1]->next != NULL){
if(strcmp(profiles->table[hash1]->name, name) == 0)){
break;
}
profiles->table[hash1] = profiles->table[hash1]->next;
}
insertBST(profiles->table[hash1]->tree, _friend);
}
// add second friend
if(strcmp(profiles->table[hash2]->name, _friend) == 0){
insertBST(profiles->table[hash2]->tree, name);
}
else{
while(profiles->table[hash2]->next != NULL){
if(strcmp(profiles->table[hash2]->name, name) == 0)){
break;
}
profiles->table[hash2] = profiles->table[hash1]->next;
}
insertBST(profiles->table[hash2]->tree, name);
}
}
// creates a new bst node
struct bst *newBSTNode(char *name){
struct bst *temp = (struct bst* )malloc(sizeof(struct bst));
temp->val = strdup(name);
strcpy(temp->val, name);
temp->l = temp->r = NULL;
return temp;
}
// Inserts the a friend into a BST
struct bst *insertBST(struct bst *node, char *name){
if(!node)
return newBSTNode(name);
else{
if(strcmp(name, node->val) < 0){
node->l = insertBST(node->l, name);
}
else if(strcmp(name, node->val) > 0){
node->r = insertBST(node->r, name);
}
}
return node;
}
// Inorder print of names
void inorder(struct bst *root){
if(!root){
inorder(root->l);
printf("%s ", root->val);
inorder(root->r);
}
}
// Sends to function to print names
void printFriends(hash *profiles, char *name){
int hash_val = keyHash(name);
inorder(profiles->table[hash_val]->tree);
}
How would I be able to access the BST of said person? The struct bst *tree = profiles->table[hash1]->tree; was my previous attempt, but it was more of a shot in the dark. Thanks in advance!
Update: Okay, so I have been able to add friends (I think) and now I'm trying to print them with void printFriends(). However when I run the function nothing prints out. Would anyone know where I'm messing it up? I've updated the code above.
Your linkedList stores the name and a tree pointer. But the type of tree is linkedList. Change that to struct bst *. You'll have to reorder the declarations, or insert a forward declaration.
Implement a search. You check if the hash buckets are empty, but you don't search for the matching name.
Once you find a matching name in the bucket, the same node contains the aforementioned tree pointer. Insert the friend in the tree. Depending on your logic, you may or may not want to insert a backreference in the other person's tree. (If Alice is friends with Bob, does that make Bob friends with Alice automatically? Or wait for confirmation?)

elements storing

code print elements after store them :
void print(struct node* root)
{
while ( c != NULL )
{
printf( "\n%d ", c->line1);
printf( "%s", c->curr );
c = c->next;
}
}
print method
Just looking at the code, this line seems like a potential issue:
temp->curr=current_input;
It looks like all the nodes .curr will get set = current_input. I'm guessing you need to do something like:
temp->curr = malloc(1 + strlen(current_input));
strcpy(tmp->curr, current_input);
Use strcpy_s if strcpy causes a warning.
First you should realize a list consists of nodes, which contain pieces of your data — so you need to allocate a new node for each piece of data you want to store in a list.
Then you insert each newly created node into the list and finally print the list when done.
Additionaly remember that data need to be either copied into the node (like line1) or copied somewhere else, for example onto the heap, and then linked to the node with a pointer, like curr (see the answer by #rcgldr).
struct node *root = NULL;
struct node *createnode(int line, const char *input)
{
struct node *n = malloc(sizeof(struct node));
if(n != NULL)
{
n->line1 = line;
n->curr = input;
n->next = NULL;
}
return n;
}
void insertnode(struct node* n)
{
n->next = root;
root = n;
}
void printlist(struct node* n)
{
for( ; n != NULL; n = n->next)
{
printf( "%d: %s\n", n->line1, n->curr);
}
}
int main(int argc, const char * argv[])
{
char *input;
struct node *temp;
type t;
do
{
t=getword(); //call to get the type of t
switch (t)
{
case number:
case keyword:
input = strdup(current_input); // make a copy of user input
if(input != NULL)
{
temp = createnode(line, input);
if(temp != NULL) // created?
insertnode(temp); // insert into the list
else
{
free(input); // free unused input copy
t = EOF; // make the loop terminate
}
}
else // user input copy failed
t = EOF; // make the loop terminate
break;
default:
break;
}
}
while (t != EOF);
print(root);
return 0;
}

Binary Tree segmentation fault after implementing search function

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.

Is binary tree search lying to me?

Hey I'm trying to write a program that will take a list of strings (these are in order):
polymorphism
object
templates
structure
class
pointer
reference
traversal
inheritance
exceptions
recursive
overloading
And then store these strings in a binary tree and finally do an in-order traversal.
However, I'm having a problem that I just can't figure out. My function to add nodes keeps telling me that I've already added the node but, it never actually gets added?? My output is like this:
ADDED NODE: polymorphism
ERROR: Same Data: object, object
ERROR: Same Data: templates, templates
ERROR: Same Data: structure, structure
ERROR: Same Data: class, class
ERROR: Same Data: pointer, pointer
(etc...)
ERROR: overloading, overloading
ERROR: overloading, overloading
FINISHED BUILDING
overloading
Finally, here's the source code:
#include <stdlib.h>
#include <stdio.h>
struct tree {
char* data;
struct tree *left;
struct tree *right;
};
void buildTree(struct tree**);
void printAlpha(struct tree*);
void insert(struct tree **root, char *n);
int main(int argc, char* argv[]) {
struct tree* myTree = NULL;
buildTree(&myTree);
printf("FINISHED BUILDING\n\n");
printAlpha(myTree);
system("PAUSE");
return 0;
}
/*Builds tree from text file*/
void buildTree(struct tree **root) {
FILE* fIn = fopen("./in.txt", "r");
char* input = (char*) malloc(sizeof(char));
if(!fIn) {
printf("ERROR: Cannot find file\n");
return;
}
while(!feof(fIn) && fscanf(fIn, "%s", input)) {
// printf("INP:%s\n", input);
insert(root, input);
}
}
void insert(struct tree **root, char *n) {
if (*root == NULL) {
// found the spot, create and insert the node
struct tree *newNode = NULL;
newNode = (struct tree*) malloc(sizeof(struct tree) );
newNode->data = n;
newNode->left = NULL;
newNode->right = NULL;
*root = newNode;
printf("ADDED NODE: %s\n", newNode->data);
}
else if(strcmp(n, (*root)->data) < 0)
insert(&((*root)->left), n);
else if(strcmp(n, (*root)->data) > 0)
insert(&((*root)->right), n);
else
printf("ERROR: Same data: %s, %s\n", (*root)->data, n);
}
/*In order traversal*/
void printAlpha(struct tree *root) {
struct tree *curNode = root;
/*If empty something went wrong*/
if(!curNode) {
printf("Error: Binary Tree Is Empty!\n");
// return;
}
if(curNode->left != NULL) {
printAlpha(root->left);
}
printf("%s\n", curNode->data);
if(curNode->right != NULL) {
printAlpha(curNode->right);
}
}
You are creating a single string (char* input = (char*) malloc(sizeof(char));) and overwriting its contents each time. You insert this single string into the tree, then the next time compare it against itself.
Solution: Move the malloc inside the loop.

Resources