Inserting a string into a Binary Search Tree C - c

I need to insert strings into a binary search tree but each run through my insert function updates all the nodes and not just the appropriate one.
Its required that each word placed in the binary search tree has the exact amount of memory allocated for it (+1 for the NULL pointer).
Here's the struct being used:
typedef struct node_t{
char *word;
struct node_t *left, *right;
} node_t;
Here's how I am passing in the word:
for(i=0; i< original_words -1; i++)
{
fscanf(ifp, "%s", y);
head = insert(head, y);
}
And here's my insert function:
node_t *insert(struct node_t *head, char *word)
{
if(strcmp(head->word, word) > 0)
{
if(head->left == NULL)
{
head->left = create_node(word);
}
else
{
head->left = insert(head->left, word);
}
}
else
{
if(head->right == NULL)
{
head->right = create_node(word);
}
else
{
head->right = insert(head->right, word);
}
}
return head;
}
EDIT: Heres an example of the input file.
4
-------
bravo
-------
alpha
-------
gamma
-------
delta

Your answer (the insert function) assumes that head has already been defined on first call, it should start with the line:
if (head == null) return create_node(word);
This will also save you from the need for the null lines in the code. I'm not sure that this is the issue, but it is one.
Perhaps more important: how does create_node set up word? If it's something like:
new_node->word = word
Then all you are doing is creating a collection of pointers to the word pulled out of the file. It is quite possible that each reading of a word from the file reads the word into the same piece of memory, so all you are doing is collecting a tree of pointers to the same place in memory. It should be something like:
new_node->word = malloc(strlen(word)+1);
if (new_note->word == null) {FAIL MEMORY LOSS}
strcpy(new_node->word, word);

Related

I can't open file in C

When executed my code in Codeblocks , there is a problem like "error opening file" and I'm sure that there is a file as named "Names". Should I transport my .txt file to another file or something?
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct node {
char *word;
int count;
struct node *left;
struct node *right;
};
// Function to create a new node
struct node* createNode(char *word) {
struct node *newNode = malloc(sizeof(struct node));
newNode->word = strdup(word);
newNode->count = 1;
newNode->left = NULL;
newNode->right = NULL;
return newNode;
}
// Function to insert a new node into the binary search tree
struct node* insert(struct node *root, char *word) {
if (root == NULL) {
// Create a new node if the tree is empty
return createNode(word);
} else if (strcmp(word, root->word) < 0) {
// Insert the word in the left subtree if it is less than the root node's word
root->left = insert(root->left, word);
} else if (strcmp(word, root->word) > 0) {
// Insert the word in the right subtree if it is greater than the root node's word
root->right = insert(root->right, word);
}
// Increment the count if the word already exists in the tree
else {
root->count++;
}
return root;
}
// Function to find the depth of the binary search tree
int findDepth(struct node *root) {
if (root == NULL) {
return 0;
} else {
// Calculate the depth of the left and right subtrees
int leftDepth = findDepth(root->left);
int rightDepth = findDepth(root->right);
// Return the maximum of the left and right depths
if (leftDepth > rightDepth) {
return leftDepth + 1;
} else {
return rightDepth + 1;
}
}
}
// Function to count the number of nodes in the binary search tree
int main() {
// Open the text file for reading
FILE *file = fopen("C:\C\Names.txt", "r");
if (file == NULL) {
printf("Error opening file\n");
return 1;
}
// Initialize the root node to NULL
struct node *root = NULL;
char word[50];
int wordCount = 0;
// Read each word from the file and add it to the tree
while (fscanf(file, "%s", word) != EOF) {
root = insert(root, word);
wordCount++;
}
static int count = 0;
int countnodes(struct node *root) {
if (root != NULL) {
countnodes(root->left);
count++;
countnodes(root->right);
}
return count;
}
// Close the file
fclose(file);
printf(
"In total %d words were read from the file and saved in the binary search tree.\n",
wordCount);
printf("Number of nodes in the binary search tree: %d\n", countnodes(root));
printf("The depth of the binary search tree: %d\n", findDepth(root));
}
I tried to change my opening text codeblocks with another codes from internet. But it doesn't work again.
Your filepath should be written as "C:\\C\\Names.txt" or "C:/C/Names.txt".
Your fopen("C:\C\Names.txt", "r") should instead be fopen("C:\\C\\Names.txt", "r").
In the C language, the backslash character has a special meaning in string literals: It is used as an "escape character" to allow representing some common characters that are not found on most keyboards. A list of escape sequences can be found here on cppreference.
Taking a look at that list on cppreference, you'll see that to enter a single literal backslash in the interpreted value of a string literal, you must put two consecutive backslash characters in the source-code for the string literal, like "\\". The first one begins the "escape sequence", and the second one specifies that the escape sequence represents a literal backslash character.
Curiously, cppreference says that:
ISO C requires a diagnostic if the backslash is followed by any character not listed here
And your \C and \N are not in the list, so for your compiler to not have warned you, I'm guessing it must have been in a non-ISO-compliant mode, in which case you can search your compiler's documentation to see how to switch to an ISO-compliant mode.

Multi-Occurence delete function in the linked list only deletes first instance of character

I am attempting to delete several occurrences of a word stored in a linked list. However, these words are stored by character in a single node instead of as a whole word in a list. For example, The pink flamingo is stored as:
T->h->e-> ->p->i->n->k-> ->f->l->a->m->i->n->g->o. Say the user wants to find pink. They have to loop through and delete each of these nodes.
I have attempted to create a loop which copies the contents of the linked list into a search function. I think copy this search function into another string. I am able to successfully delete the first occurrence of the desired character. However, I'm unable to do much else. I've attempted several times to push the node to the next node after deletion, but that also hasn't worked. It produced the same error.
void nodeDeletions(struct node** reference_to_headNode, struct node* deleteNode){
if(*reference_to_headNode == NULL){
return;
}
if(*reference_to_headNode == deleteNode){
printf("Test A\n");
*reference_to_headNode = deleteNode->nextNode;
}
if(deleteNode->nextNode != NULL){
printf("Test B\n");
deleteNode->nextNode->previousNode = deleteNode->previousNode;
}
if(deleteNode->previousNode != NULL){
printf("Test C\n");
deleteNode->previousNode->nextNode = deleteNode ->nextNode;
}
free(deleteNode);
}
void deleteWord(struct node** reference_to_headNode, char word_to_delete[]){
struct node *tempNode;
struct node *nextNode;
int searchIndex = 0;
int characterIndex = 0;
const int arraySize = 101;
const int arraySize2 = 202;
char searchWordIndex[arraySize];
char searchWordCopyIndex[arraySize2];
if(*reference_to_headNode == NULL){
return;
}
else {
for (tempNode = *reference_to_headNode; tempNode != NULL; tempNode = tempNode->nextNode) {
searchWordIndex[searchIndex] = tempNode->character;
searchIndex++;
}
strcpy_s(searchWordCopyIndex, searchWordIndex);
int length_of_searchIndex = strlen(searchWordCopyIndex);
int length_of_deletionWord = strlen(word_to_delete);
tempNode = *reference_to_headNode;
for (searchIndex = 0; searchIndex < length_of_searchIndex; searchIndex++) {
printf("Test 1\n");
if(tempNode != NULL) {
if(tempNode->character == word_to_delete[0]) {
for (characterIndex = 0; characterIndex < length_of_deletionWord; characterIndex++) {
printf("Test 2\n");
if (searchWordCopyIndex[searchIndex] == word_to_delete[characterIndex]) {
printf("Test 3\n");
if (tempNode->character == word_to_delete[characterIndex]) {
printf("Test 4\n");
printf("%c\n", tempNode->character);
printf("%c\n%c\n", word_to_delete[characterIndex], searchWordCopyIndex[searchIndex]);
nextNode = tempNode->nextNode;
nodeDeletions(reference_to_headNode, tempNode);
tempNode = nextNode;
}
else {
printf("Test 5\n");
tempNode = tempNode->nextNode;
}
}
}
}
}
tempNode = tempNode->nextNode;
}
}
}
Phew! That's a lot of indented clode blocks. And a lot of auxiliary arrays and indices. And very long variable names. :)
Basically, you are dealing with three different types of iterating forwards through stuff, which are all present in your code:
Traverse a character string:
while (*s) {
// do stuff with *s
s++;
}
Traverse a linked list:
while (p) {
// do stuff with *p
p = p->next;
}
Traverse a linked list via a reference to the source, so that you can modify it:
while (*p) {
// do stuff with **p
p = &(*p)->next;;
}
You only have to combine these three basic loops.
You can walk through the list with the third method (because you need to be able to update the head or next links when you delete). For each node you visit, compare the "tail" of that node with an auxiliary pointer p and the string s using the other two methods simultaneously. When the string matches, *s == '\0' and p points to the first node after the word. Delete all nodes by advancing the head until the head is p.
In other words:
Traverse the list via *head.
At each node:
set p = *head and s to the begining of the string;
traverse the list and the word while the letters match;
If *s == '\0', there is a match. Now, *head points to the start of the word to delete in the list, p points to the first node after the word in the list, which may be NULL.
If there is a match, advance *head until *head == p, deleting the nodes as you go.
Or, in code:
void delete_word(struct node **head, const char *str)
{
while (*head) {
struct node *p = *head;
const char *s = str;
while (p && *s && p->c == *s) {
p = p->next;
s++;
}
if (*s == '\0') {
while (*head != p) {
struct node *del = *head;
*head = (*head)->next;
delete_node(del);
}
} else {
head = &(*head)->next;
}
}
}

Printing binary search tree, segmentation fault error 11

could you help me?
Program reads words from a file and puts them into binary search tree, but I get "Segmentation fault: 11" when running my print function.
struct node {
char * item;
struct node * left;
struct node * right;
};
struct node * new(char * a) {
struct node * new;
new = (struct node *)malloc(sizeof(struct node *));
new->item = a;
new->left = new->right = NULL;
return new;
}
struct node * insert(struct node * a, char * b) {
if(a == NULL) {
a = new(b);
}
else if (b <= a->item) {
a->left = insert(a->left, b);
}
else {
a->right = insert(a->right, b);
}
return a;
}
void print(struct node * a) {
if (a->left == NULL && a->right == NULL)
printf("%s", a->item);
else if (a->left != NULL)
print(a->left);
else
print(a->right);
}
from main.c :
struct node * root = NULL;
struct node * start;
start = root;
while (fscanf(fp, "%s", temp) != EOF) {
root = insert(root, temp); // insert function works ok
}
print(start);
UPDATE:
I've made a change in main.c:
int i = 0;
while (fscanf(fp, "%s", temp) != EOF) {
root = insert(root, temp);
if (!i) {
start = root;
i = 1;
}
}
Now it doesn't show error, but it prints only the last word from the tree instead of printing it recursively. Any suggestions?
UPDATE #2:
Thank you for your help. Following your suggestions I've made changes to this function:
struct node * new(char * a) {
struct node * new;
char * stringcopy;
stringcopy = malloc(strlen(a) + 1);
strcpy(stringcopy, a);
new = malloc(sizeof(* new));
new->item = stringcopy;
new->left = new->right = NULL;
return new;
}
Now everything works fine.
The original problem was almost certainly that start was NULL since you did not update it when you updated root. (Meanwhile it seems that the whole start is unnecessary; just use root directly.)
The new problem (printing only the last word) is that you are not traversing the tree correctly: your print function only prints if both left and right are NULL, so only a leaf node is ever printed, and furthermore it does not descend into the right branch if there is a left branch.
You could try something like this instead (untested code):
void print(struct node * a) {
if (a == NULL) { return; }
print(a->left);
(void) puts(a->item);
print(a->right);
}
In particular, note that if you are at a non-NULL node, you need to print its item unconditionally, or the complete output will be missing that node.
Another problem seems to be that you are not copying item when you create the node. So if your temp in insert(root, temp) is indeed a temporary object that will be overwritten or freed, all of your items (except possibly the last) will be invalid by the time you try to print them. Instead of assigning new->item = a, do the equivalent of new->item = strdup(a) and then remember to free it when you free the node.
(strdup is not in the C standard library, but it is easy to implement: allocate enough space for the string, including NUL terminator, and copy.)
Also, the comparison b <= a->item is almost certainly not doing what you expect it to; see strcmp.

strcmp in Linked List insertion crashing program

As part of an assignment, I'm supposed to implement a singly linked list in c.
I've done this plenty of times before in a few different languages, but after a few hours of pain I've gotten stuck on a problem using strcmp.
This is the structure I'm using:
typedef struct node {
char *name;
float score;
struct node *next;
} node;
The problem is specific to the insertion function, which is supposed to be similar to an insertion sort, since I need to have the nodes in the list sorted in alphabetical order.(my professor specified that the insertion function does the sorting, despite not calling it an insertion sort).
void insert(node **start, char *name, float score) { // to insert a record into the linked list sorted by name in dictionary order.
//create new node
node *n_node = new_node(name, score);
node *current;
current = *start;
if (current != NULL) { //-----------if list is not empty
node *prev = NULL;
if (current->next != NULL) { //--if list has more than 1 element
while (current != NULL && strcmp(name, current->name) > 0) { //cycle through list to sorted insertion point
// ^^^^^^^Problem Here^^^^^^^^
//while name is greater than current name, means lower on alphabet (z>a)
prev = current;
current = current->next;
}
if (current != NULL) { //-----not at end of list
//once current is not < new node, connect between prev and current
prev->next = n_node;
n_node->next = current;
} else { // ------------------at end of list
prev->next = n_node;
}
} else { //-----------------------list has only one element
current->next = n_node;
}
} else { //--------------------------List is empty - assign new node as first element
*start = n_node;
}
}
The problem is that my program crashes and burns without any errors or warnings (I'm using eclipse with CDT).
The program works fine when
while (current != NULL && strcmp(name, current->name) > 0)
is modified to
while (current != NULL /*&& strcmp(name, current->name) > 0*/).
It seems obvious to me that name or current->name are causing a problem with the operation of strcmp, but I can't seem to get around that.
Edit:
I'll add that this function is called from another function, which retrieves and tokenises strings from a file containing pairs of names and marks, but my testing hasn't suggested that it passes a bad string or characters via the call.
For some extra detail, here's my new_node function:
node *new_node(char *name, float score) {
node *new = (struct node*) malloc(sizeof(struct node));
new->name = malloc(strlen(name) + 1);
strcpy(new->name, name);
new->score = score;
new->next = NULL;
return new;
}
(I realise using new as the name of the node isn't smart, and I will change that)
and the function that calls insert:
int data_import(node **startp, char *infilename) { // to import data from the file and insert .
int max_line = 100;
char line[max_line];
char delimiters[] = ",";
char name[500] = "";
char *namep;
namep = &name[0];
float score = 0.0f;
int i = 0;
FILE *fi;
char *token;
// open file to read
fi = fopen(infilename, "r");
if (fi == NULL) { // Cannot open the file.
perror("error");
return 0;
}
// read each line, increase counter, retrieve data
while (fgets(line, max_line, fi) != NULL) {
//fputs(line, stdout); //console output confirmation
token = strtok(line, delimiters);
strcpy(namep, token);
token = strtok(NULL, delimiters); //increment token to mark variable
score = atof(token);
insert(startp, namep, score);
i++;
}
//close file
fclose(fi);
return i;
}
what happens if you have element called apple as your first element and you try to add element called about ?
you will be thrown out of below while loop straight away and your prev will be unassigned :
while (current != NULL && strcmp(name, current->name) > 0) { //cycle through list to sorted insertion point
// ^^^^^^^Problem Here^^^^^^^^
//while name is greater than current name, means lower on alphabet (z>a)
prev = current;
current = current->next;
}
this particular part looks suspicious to me :
after that you will enter in below routine :
if (current != NULL) { //-----not at end of list
//once current is not < new node, connect between prev and current
prev->next = n_node;
n_node->next = current;
}
as your *prev is unassigned and you try to access it (prev->next = n_node;).you will get crash here.

C Function to leave only unique nodes in singly linked list

I'm having an issue: in a function, program needs to compare two nodes and delete one of them if the node values are the same (example: A -> B -> B -> C >>> A -> B -> C). In a few words: it have to leave only unique nodes.
Steps that I've done: created list from data given in data file and it prints out perfectly. Now I'm struggling comparing two nodes.
Steps that I'm thinking of doing: Put two nodes values in two different variables, then comparing those two and if latest node is equal to previous, delete the latest and somehow link previous node to next node. Then the cycle goes all over again.
Question: How do I compare node with further one?
Here's the code that I have right now:
#include <stdio.h>
#include <stdlib.h>
#define LENGTH 255
struct node {
int info;
struct node *next;
} *head = NULL;
int create(FILE **data){
char read[LENGTH];
printf("Write data file name: ");
scanf("%s", read);
*data = fopen (read, "r");
if (data == NULL) {
printf("Error reading given file.");
}
return 0;
}
int put_Symbols_into_list(FILE *data) {
struct node *new_node, *current;
char c;
printf("Data given: ");
while (!feof(data)){
new_node = (struct node*)malloc(sizeof (struct node));
c = fscanf(data, "%s", &new_node -> info);
printf("%s ", &new_node -> info);
if (head == NULL){
head = new_node;
current = new_node;
} else {
current -> next = new_node;
current = new_node;
}
}
}
int delete_Same_Symbols() {
}
int main() {
FILE *data;
struct node *n;
create(&data);
put_Symbols_into_list(data);
delete_Same_Symbols();
//display_List(n);
return 0;
}
An efficient way of remove duplicates is the following:
1. Sort the list
2. Run through the list once deleting any adjacent duplicates.
Whether this solution works for you depends on whether your data can be ordered.
Traverse the list from head to end and maintain a Hash table.
For every element,check whether value is in the hash table: if yes, remove the node; else put value in the hash table.
Time Complexity: O(n) on average (assuming that hash table access time is O(1) on average).
I'll give you the function that will remove the duplicates.
Assuming that your code is working fine and that the info is an int value
Note that if info is a string you will need strcmp function instead of head->info == info.
int exist(NODE *head, int info) {
while (head && head->info != info) // strcmp(info,head->info) != 0
head = head->next;
return head->info == info; // strcmp(info,head->info)
}
void removeDuplicates(NODE **phead, int info) {
NODE *tmp = *phead;
while (tmp->info != info) // strcmp(info,head->info) != 0
tmp = tmp->next;
NODE *tmp1 = tmp->next;
tmp->info = tmp1->info; // strcpy(tmp->info,tmp1->info)
tmp->next = tmp1->next;
free(tmp1);
}

Resources