What is wrong with my trie suggestion algorithm? - c

I am coding an algorithm that uses the trie data structure. Basically, if input is "he", it will return ["hello","help","held","hen",other_words_starting_with_he]. code:
//NOTE: The expression sizeof(array)/sizeof(node) must always evaluate to 26. Not more; not less, for all the node arrays.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char *chars="abcdefghijklmnopqrstuvwxyz";
struct node{
char ch;
struct node *next[26];
};
void init_w_null(struct node **n, int len){
register int counter=0;
while(counter<len){
*(n+counter)=NULL;
counter++;
}
}
int index_of_char(char ch){
register int counter=0;
while(*(chars+counter)!='\0'){
if(*(chars+counter)==ch){
return counter;
}
counter++;
}
return -1;
}
void insert(struct node **root, char *key){
if(*root==NULL){
*root=(struct node*)malloc(sizeof(struct node));
if(*root==NULL){
perror("[malloc]");
exit(EXIT_FAILURE);
}
init_w_null((**root).next,26);
struct node *selected=*root;
(**root).ch=key[0];
register int counter=1;
while(counter<strlen(key)){
int ind=index_of_char(key[counter]);
(*selected).next[ind]=(struct node *)malloc(sizeof(struct node));
if(selected==NULL){
perror("[malloc]");
exit(EXIT_FAILURE);
}
(*(*selected).next[ind]).ch=key[counter];
selected=(*selected).next[ind];
counter++;
}
return;
}
register int counter=1;
struct node *selected=*root;
while(counter<=strlen(key)){
int ind=index_of_char(key[counter]);
if((*selected).next[ind]!=NULL){
selected=(*selected).next[ind];
counter++;
continue;
}
(*selected).next[ind]=(struct node*)malloc(sizeof(struct node));
if((*selected).next[ind]==NULL){
perror("[malloc]");
exit(EXIT_FAILURE);
}
(*(*selected).next[ind]).ch=key[counter];
selected=(*selected).next[ind];
init_w_null((*selected).next,26);
counter++;
}
}
void find(struct node *root, char *key){
register int counter=1;
struct node *selected=root;
int ind=0;
while(counter<=strlen(key)){
//if key param ends, and tree doesn't
if(key[counter]=='\0'){
printf("List of possible keys:\n");
construct_str(selected,key);
return;
}
ind=index_of_char(key[counter]);
//a character of key not found.
if((*selected).next[ind]==NULL){
puts("Similar keys not found.");
return;
}
selected=(*selected).next[ind];
counter++;
}
puts("Key found.");
}
void construct_str(struct node *n, char *str){
puts("[construct_str]");
//end of recursion
if(all_children_null(n)&&n!=NULL){
printf("%s\n",str);
return;
}
register int counter=0;
while(counter<26){
if((*n).next[counter]!=NULL){
char nstr[2];
nstr[0]=(*(*n).next[counter]).ch;
nstr[1]='\0';
str=strcat(str,nstr);
construct_str((*n).next[counter],str);
}
counter++;
}
}
int all_children_null(struct node *n){
register int counter=0;
while(counter<26){
if((*n).next[counter]!=NULL){
return 0;
}
counter++;
}
return 1;
}
void insert_full(struct node **arr, char *key){
int first=index_of_char(key[0]);
insert(&arr[first],key);
}
//a debugging function to see whether insertion is successful.
/*void raw_print(struct node *n){
//puts("[raw_print]");
if(n!=NULL){
putchar((*n).ch);
register int counter=0;
for(;counter<26;counter++){
raw_print((*n).next[counter]);
}
if(all_children_null(n)){
printf("\nAll children of %c are NULL.\n",(*n).ch);
}
}
}*/
int main(){
struct node *nds[26];
init_w_null(nds,26);
insert_full(nds,"hello");
insert_full(nds,"help");
insert_full(nds,"bruh");
insert_full(nds,"lmao");
find(nds[index_of_char('l')],"lm");
return 0;
}
output:
List of possible keys:
[construct_str]
Segmentation fault (core dumped)
I've narrowed the problem down to construct_str. Please tell me if I'm wrong, though.
FOR PEOPLE WHO DON'T KNOW WHAT A TRIE IS:
It's a structure which can store strings with the same prefix, so if we add "hello" and "help", the fourth character in both the strings would be siblings. the node of a trie contains a character and a node array with 26 members.

I see that my crystal ball is well attuned today.
Starting with ...
find(nds[index_of_char('l')],"lm");
... you are passing a string literal as the second argument to find(). In that function, a pointer to its first character is associated with parameter key.
Within find(), you are forwarding that pointer to construct_str():
construct_str(selected,key);
, wherein it is associated with parameter str.
In construct_str(), you are passing that pointer as the first argument to strcat():
str=strcat(str,nstr);
strcat appends the contents of the second string to the array containing the first. It does not create a new array for the concatenated result. Therefore, the left pointer must point (in)to an array that
is modifiable, and
contains enough space after the end of the string to accommodate the extra contents.
String literals do not satisfy either criterion. Oops. Undefined behavior results.

In the line
str=strcat(str,nstr);
str is a pointer to a string literal. You are not allowed to modify them. Attempting to modify a string literal using the function strcat will invoke undefined behavior.
When calling strcat, you must ensure that the first argument is pointing to a memory buffer which
you are allowed to write to, and
has sufficient space for adding the character(s) to the string.

Related

How to delete a word from tree dictionnary in C?

I've implemented a dictionnary in C using tree, this tree stores a word and its definition as followed:
As you can see, some words are sharing same letters.
But now i'd like to implement a delete function but don't know how to proceed ... I know that I should begin to delete the end of the word ...
Here is my code, thank you for your future help !
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct _noeud{
char *value;
struct _noeud *child[26];
}noeud_t;
typedef struct tree{
node_t root;
}Tree;
Tree dict;
int getPos(char letter){
char alpha[26]={'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z'};
int i;
for(i=0;i<strlen(alpha);i++){
if(alpha[i]==letter){
return i;
}
}
return -1;
}
void addWord(node_t *node, char *word, char *def){
int i;
for(i = 0; i < strlen(word);i++){
int letter=getPos(word[i]);
if(letter==-1){
printf("Unknown letter... \n");
}
node_t *parent = node;
node = node->child[letter];
if(!node){
node = malloc(sizeof(node_t));
parent->child[letter]=node;
}
}
node->value = malloc(strlen(def)+1);
strncpy(node->value,def,strlen(def)),
printf("Word %s added to dictionnary.\n",word);
fflush(stdin);
}
void findWord(node_t *node, char *word){
printf("Looking for word %s \n",word);
int i;
for(i=0;i<strlen(word);i++) {
int letter = getPos(word[i]);
if(NULL ==node->child[letter]){
printf("Unknown word ...\n");
return;
}
else{
node = node->child[letter];
}
}
printf("Word found, its definition is : %s\n",node->value);
}
void deleteWord(node_t *node, char *word){
int i=0;
for(i=0;i<strlen(word);i++) {
//...
}
printf("Word deleted !\n");
}
int main(){
addWord(&dico.root,"dog","it's an animal");
addWord(&dico.root,"pineapple","it's a fruit");
addWord(&dico.root,"car","something to drive");
findWord(&dico.root,"dog");
findWord(&dico.root,"car");
findWord(&dico.root,"pineapple");
deleteWord(&dico.root,"pineapple");
return 0;
}
I can give you an idea on how to solve it, but sorry I didn’t write the code.
So from your code, I can see that you have findWord function, if that works perfectly then use it inside your delete go find the word at this stage you’re pointing to it now you have to think of three possibilities.
If the word that will be deleted doesn’t have any child then delete it with no more complication.
If the word that will be deleted has a single child then make the parent of the word point to the word’s child.
If the word that will be deleted has more than one child then replace the word with one of the children and then delete it.
I hope this will help you

Reading multiple value types from input to produce a linked list in c

I need to create a linked list based on info put in by the user. The user will put all info in at once and will contain a command and potentially be followed by a string to be loaded to the list or an int value (to reference nodes). ie input: ins monkey OR prv 1 8 (In these cases the first 3 characters are the command 1st requires an insert of 'monkey' the 2nd would require the printing of nodes with a value between 1 and 8)
I have been working on this for weeks and cannot figure out how to have my program process the second part of the input. I store all the input into an array and then parse it into various arrays depending on the string values but I can't even figure out how to set the struct value for the string to the string that was put in after the command.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
//self referring struct (keystone to LL)
struct Node
{
char symbol [11];// each Node contains a array 'symbol'
int count; // each Node contains a symbol counter
struct Node *next; // points to the next node (this is the self referential aspect)
};// end struct
int main()
{
//function prototypes to be used by main
void insert(struct Node**, struct Node **,char y[]);
//char delSym(NodePtr *xPtr, char y[]);
//char forceDel(NodePtr *xPtr, int z);
void printList(struct Node*);
struct Node *head, *tail;
head = tail= NULL;
//Declare variables needed for input and output
char input[15];
char cmd [4];
char info [11];
char str [11];
char *x= info;
int val=0;
//possible command strings
char ins[4]={'i','n','s'};
char del[4]={'d','e','l'};
char fde[4]={'f','d','e'};
char pst[4]={'p','s','t'};
char prl[4]={'p','r','l'};
char pcr[4]={'p','c','r'};
char ppr[4]={'p','p','r'};
char psu[4]={'p','s','u'};
char end[4]={'e','n','d'};
// Prompt user for command and corresponding input
puts("Please enter a command with corresponding value(s) where necessary");
fgets(input,15, stdin);
//Read the command
memcpy(cmd, &input[0], 4 );// I am getting the command twice followed by the info????
memcpy(info, &input[4], 11 );
printf("%s\n", cmd);// put in to check memcpy
printf("%s\n", info);
//While command is not 'end':
while (memcmp(end,cmd,3) != 0)
{
// Read value(s) for the command, in necessary
if (memcmp(ins,cmd, 3)==0)
{
insert(&head, &tail, info);
}
.....
void insert(struct Node **h, struct Node **t, char y[])
{
struct Node *temp; // New node
struct Node *previous; // Previous node
struct Node *nodePtr; // To walk the LL
//Allocates a new node and store the appropriate data
if((temp =(struct Node *)maaloc(sizeof(struct Node)))==NULL)
{
printf("Node allocation failed.\n");
exit(1);
}
temp->symbol = y;
temp->count = count++;
if(!*h)// If the list is empty make newNode the first node
{
*h = temp;
temp->next = NULL;
}
else// Insert node if list is not empty
{
nodePtr = *h;
previousNode = NULL;
while(nodePtr !=NULL)
{
if(memcmp(nodePtr->symbol, temp->y, 11)!=0)
{
while(nodePtr != NULL && nodePtr->count < temp->count)
{
previousNode = nodePtr;
nodePtr = nodePtr ->next;
}
if (previousNode == NULL)
{
*h = temp;
temp->next = nodePtr;
}
else
{
previousNode->next = temp;
temp->next = nodePtr;
}
}
else
{
nodePtr->count=count++;
}
nodePtr= nodePtr->next;
}
}
The particular problem that you describe can be traced to this line:
memcpy(cmd, &input[0], 4 );
This code copies the first 4 characters of input into cmd[]. Presumably the fourth character is a space, so cmd is not a string, i.e., cmd is not a null-terminated character array. Thus, printf("%s\n", cmd) is undefined behavior, likely printing characters until a 0 is encountered somewhere past the end of cmd.
You could change the offending line so that it only copies three characters from input:
memcpy(cmd, &input[0], 3);
Note that cmd is still not a string, as the final element has not been initialized to \0 yet. You could now do cmd[3] = '\0'. It may be better to initialize cmd at the point of declaration:
char cmd[4] = {0};
Note also that fgets() keeps the newline (so long as the input buffer is large enough), so info will likely contain a \n before the \0 that you may wish to remove.
None of the "possible command strings" are actually strings, they are character arrays, and only the first three bytes have been initialized in each of them.
One fix would be to add null terminators:
char ins[4]={'i', 'n', 's', '\0'};
It would be better to initialize from a string literal, and let the compiler determine the sizes of the arrays:
char ins[] = "ins";

Linked list doesn't work as expected

I implemented this code to add an item to the list (it must be a string) and remove a specific string. However it has two problems: FIRST, the order is wrong after insert the nodes to the list. SECOND, after removing a node, it remains a "blank" space, see below when I remove the Third node.
Initial list:
First
Fourth
Third
Second
After list_remove():
First
Fourth
Second
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct myStruct {
char str[20];
struct myStruct * pNext;
};
struct myStruct *list_create(const char *str)
{
struct myStruct *node;
if(!(node=malloc(sizeof(struct myStruct))))
return NULL;
strcpy(node->str, str);
node->pNext = NULL;
return node;
}
int add_to(struct myStruct * list, const char *str)
{
struct myStruct *newnode;
newnode = list_create(str);
newnode->pNext = list->pNext;
list->pNext = newnode;
return 1;
}
char * remove_to(struct myStruct * list, const char *str)
{
while(list->pNext && (strcmp(list->str, str)))
list = list->pNext;
free(list);
return 0;
}
int list_foreach(struct myStruct *node, int(*func)(void*))
{
while(node) {
if(func(node->str)!=0) return -1;
node=node->pNext;
}
return 0;
}
int printstring(void *s)
{
printf("%s\n", (char *)s);
return 0;
}
int main(void)
{
struct myStruct *list;
// Create initial elements of list
list = list_create("First");
add_to(list, "Second");
add_to(list, "Third");
add_to(list, "Fourth");
printf("Initial list:\n");
list_foreach(list, printstring);
putchar('\n');
remove_to(list, "Third");
printf("After list_remove():\n");
list_foreach(list, printstring);
putchar('\n');
return 0;
}
I think I see a few problems:
add_to() always operates on the current list's first item. So every time you add, the new node will be inserted between the first and second (if there is a second).
And the method remove_to does not check if a matching string was actually found. After exhausting the while loop, if not match was found, I think you will free the last item.
C++ has a whole suite of "Collections" and a built in linked list is one of them. Unless this is a homework exercise where you have to implement your own, consider using that.

segmentation error in ubuntu

Please tell me why the segmentation error is there in my program there is no error .
I also tried to debug it but it never goes inside the for statement.
#include<stdio.h>
#include<malloc.h>
struct node
{
int data;
struct node* link;
} *start;
main()
{
int i,n,m;
start=NULL;
printf("enter the number of nodes you want");
scanf("%d",&n);
for(i=0;i<n;i++)
{
printf("enter the element you want to insert");
scanf("%d",&m);
create_list(m);
}
}
create_list(int data)
{
struct node *q,*temp;
temp=(struct node *)malloc(sizeof(struct node));
temp->data=data;
temp->link=NULL;
if(start==NULL)
start=temp;
else
{
while(q->link!=NULL) q=q->link;
q->link=temp;
}
}
You forgot to initialize q before using it:
q = start;
while(q->link!=NULL)
1.You haven't initialized q in create_list() and used it -
while(q->link!=NULL)
Intialize q=start; before this loop.
2.Also free the allocated memory for temp in function.
3.main() should be int main(void) and what is type of create_list? Declare its prototype before main.
In the function create_list local pointer q was not initialized
struct node *q,*temp;
^^^
However it is accessed in the loop
while(q->link!=NULL)
I think you mean the following
else
{
q = start;
while ( q->link != NULL ) q = q->link;
q->link = temp;
}
Take into account that the function should be declared before its usage. Place it declaration for example before main. And its return type shall be void and specified explicitly. Also function main shall have return type int.
For example
void create_list( int data );
int main( void )
{
//...
And it is a good idea to free all dynamically allocated memory before exiting the program.
Also header <malloc.h> is not a standard C header. You should use <stdlib.h> instead.
After all suggestions from the above it will be clearly in the future if you try to show some work of your clear codding and to respect the minimum standard.
Here is a way of how should be looking your code:
#include<stdio.h>
#include<stdlib.h> /* You need stdlib not malloc */
void create_list(int data); /* If you don't declare your function the compiler doesn't know nothing about create_list */
struct node{
int data;
struct node* link;
}*start;
int main(void){ /* Here return type of main is int and if no arg needed should be used void */
int i,n,m;
start=NULL;
printf("enter the number of nodes you want");
if((scanf("%d",&n)) != EOF) /* always check scanf's return */
for(i=0;i<n;i++){
printf("enter the element you want to insert");
if((scanf("%d",&m)) != EOF) /* here the same: always check scanf's return */
create_list(m);
}
return 0; /* return of main should be 0 or one of the following: EXIT_SUCCESS or EXIT_FAILURE, but 0 will be ok because it is standard*/
}
void create_list(int data){ /* here should be explicit what kind of function is */
struct node *q,*temp;
temp=(struct node *)malloc(sizeof(struct node)); /* if you allocate memory dynamically ..... */
temp->data=data;
temp->link=NULL;
if(start==NULL){
start=temp;
}else{
q = start;
while(q->link!=NULL){
q=q->link;
q->link=temp;
}
}
free(temp); /* .....then always free it */
}

Creating a binary tree but it doesn't work — tree is always null

I am creating a code to insert the elements in tree, but tinsert function does not insert; what is wrong with my code? I have checked many times but tree is always NULL.
The code has only 2 functions: one to insert, and second to show it in preorder.
#include<stdio.h>
#include<stdlib.h>
struct btree {
int val;
struct btree *left;
struct btree *right;
};
static int c=0;
typedef struct btree node;
void tinsert( node *n,int a)
{
c++;
printf("%d\n",c);
if(n==NULL)
{
n=(node *)malloc(sizeof(node));
n->left=NULL;
n->right=NULL;
n->val=a;
//printf("adding root %d\n",n->val);
//n=temp;
}
else if(a>=(n->val))
tinsert(n->right,a);
else
tinsert(n->left,a);
return ;
}
void preorder_display(node *n)
{
if(n!=NULL)
{
printf("%d\n",n->val);
preorder_display(n->left);
preorder_display(n->right);
}
else
printf("tree is null\n");
}
int main()
{
//int N;
//int num[100];
//int i;
node *ntree=NULL;
tinsert(ntree,4);
tinsert(ntree,6);
tinsert(ntree,8);
tinsert(ntree,1);
printf("tree is \n");
preorder_display(ntree);
return 0;
}
tinsert works on a local copy of your ntree, it doesn't change the one in your main. You can fix it by passing a pointer to it (i.e.: double pointer, pointer to a pointer).
So your tinsert will look like this:
void tinsert( node **n,int a)
And in your main you'll call it like this:
tinsert(&ntree,4);
Of course, you'll need to adjust the code in tinsert to de-reference the pointer and access it correctly.
Or allocate the root node in your main.
you pass your root node ntree to tinsert function by value, so when when the function is done you will stay with original value of ntree which is NULL.
You better rewrite your function, so you will pass pointer to pointer
void tinsert( node **n,int a)
//and invocation is like that :
tinsert(&ntree,4);
when you pass ntree from main to tinsert function,
new copy is created to your node*n;
One way is to make use of pointer to pointer
Or second solution is here:
Here is a solution:
#include<stdio.h>
#include<stdlib.h>
struct btree{
int val;
struct btree *left;
struct btree *right;
};
static int c=0;
typedef struct btree node;
node* tinsert( node *n,int a)
{
c++;
printf("%d\n",c);
if(n==NULL)
{
n=(node *)malloc(sizeof(node));
n->left=NULL;
n->right=NULL;
n->val=a;
//printf("adding root %d\n",n->val);
//n=temp;
}
else if(a>=(n->val))
tinsert(n->right,a);
else
tinsert(n->left,a);
return n;
}
void preorder_display(node *n)
{
if(n!=NULL)
{
printf("%d\n",n->val);
preorder_display(n->left);
preorder_display(n->right);
}
else
printf("tree is null\n");
}
int main()
{
//int N;
//int num[100];
//int i;
node *ntree=NULL;
ntree=tinsert(ntree,4);
ntree=tinsert(ntree,6);
ntree=tinsert(ntree,8);
ntree=tinsert(ntree,1);
printf("tree is \n");
preorder_display(ntree);
return 0;
}
C supports the pass by value only. However, this does not prevent you from modifying the value of a variable from another function, because you can always refer to a variable using it's memory; and in C it's done through pointers, an abstraction representing a memory location.
When you pass a value to the function, the value of the actual parameter is copied to the value of formal parameter. Note that a pointer's value is the address it points to. So, this value is copied into the formal parameter. So the new pointer inside the function points to the exact same location your original variable. You can deference the pointer anytime to manipulate it's value.
Here, you are required to manipulate a pointer. So you pass a pointer-to-pointer to the function:
tinsert(&ntree,4);
In your function, you deference it to get your original pointer; like the following:
void tinsert(node **n, int a)
{
//...
*n = malloc(sizeof(node));
//...
}

Resources