linked list insertion sort in c - c

the program should do the insertion ascending sort for the nodes,first it should check the names and if the names are equal it should sort the ids,i do not know what is the issue that does not sort properly.
# include <stdio.h>
# include <string.h>
# include <stdlib.h>
typedef struct nd{
int id;
char name[20];
float gpa;
struct nd *next;
}node;
typedef node *list;
//---------------------------------------------------------------
int insertlist(list *head,char *buffer)
{
list p,q,n;
int m,num,k,sc;
p=(list)malloc(sizeof(node));
num=sscanf(buffer,"%d %s %f",&(p->id),(p->name),(&p->gpa));
if(num!=3)
{
printf("info not complete\n");
free(p);
return 1;
}
else
{
if(!*head)
{
*head=p;
p->next = NULL;
}
//******** sorting tthe names and ids for equal names
else if(sc=strcmp((*head)->name,p->name)> 0 || ((sc == 0) && ((*head)->id > p->id)))
{//head is modified
p->next=*head;
*head=p;
}
else{
n=*head;
q=n->next;
while(q && ((sc=strcmp(q->name,p->name)<0) || ((sc == 0) && (q->id < p->id))))
{
n=q;
q=q->next;
}
n->next=p;
p->next=q;
}
}
return 0;
}
//------------------------------------------------------
int main()
{
int id,r;
list head,p;
FILE *fp;
char c,buffer[100],filename[10];
if ((fp=fopen("student.txt","r"))==NULL)
{
printf("error opening %s",filename);
exit(1);
}
else
{
head=NULL;
while(fgets(buffer,100,fp)!=NULL)
{
buffer[strlen(buffer)-1]=='\0';
r=insertlist(&head,buffer);
}
fclose(fp);
}
for(p=head;p!=NULL;p=p->next)
printf("%d %s %f\n\n",p->id,p->name,p->gpa);
}
An example of the contents of student.txt:
121513 ala 45.00
121510 wang 21.00
145852 frank 26.00
151515 ala 25.00

Your sorting issue is one of operator precedence
< and > have a higher precedence than =, meaning it will be evaluated first, then an assignment will take place.
So your string compares in these two places:
else if(sc=strcmp((*head)->name,p->name)> 0 || ((sc == 0) && ((*head)->id > p->id)))
...
while(q && ((sc=strcmp(q->name,p->name)<0) || ((sc == 0) && (q->id < p->id))))
are wrong. sc is getting the value of strcmp((*head)->name,p->name)> 0 and strcmp(q->name,p->name)<0 respectively (note this is going to always be 1 or 0, never -1)
If you simply adjust your code as such:
else if((sc=strcmp((*head)->name,p->name))> 0 || ((sc == 0) && ((*head)->id > p->id)))
...
while(q && (((sc=strcmp(q->name,p->name))<0) || ((sc == 0) && (q->id < p->id))))
You'll see it working. Moral of the story: don't try to be stingy with your parens or brackets, it doesn't cost you anything to put more in, it makes the code clearer, and it saves you debugging headaches like this one.

head is a pointer so in order to change it in a function you need to pass a pointer to it. A pointer to the pointer.
Declare like this:
int insertlist(list **head,char *buffer)
Call it like this:
r=insertlist(&(&head),buffer);
And then in the function change everywhere you reference it to de-reference the pointer.

First, fix this:
buffer[strlen(buffer)-1]=='\0';
That's an equality comparison; not an assignment. I believe you're attempting to throw out the newline at the end of the buffer. If that is the case you may want to make sure it has a newline to throw out to begin with (the last line of the input file, for example may not end in one. Regardless, this is still broken, and needs to be fixed.
Next, your sort loop has issues. I'm including one below that is hopefully easier to read, and therefore understand, with logic flaws removed (and quite a bit of other extracurricular activity as well):
int insertlist(list *head, char *buffer)
{
list p=NULL, q=NULL;
int sc=0;
/* allocate new node */
p = calloc(1, sizeof(*p));
if(3 != sscanf(buffer,"%d %s %f",&(p->id),(p->name),(&p->gpa)))
{
printf("info not complete\n");
free(p);
return 1;
}
/* initially wire p->next to our list head. then, walk list,
advancing p->next. break on first "less" condition */
p->next = *head;
while (p->next)
{
/* broken out here for clarity; break on first "less" */
sc = strcmp(p->name, p->next->name);
if (sc < 0 || (sc == 0 && p->id < p->next->id))
break;
q = p->next;
p->next = q->next;
}
/* non-null means we wire q->next to p */
if (q)
q->next = p;
/* else p is the new head; what head was prior is already in p->next */
else
*head = p;
return 0;
}
Tested with the following input file:
0001 Brook 3.50
0002 James 3.51
0003 Katie 3.52
0004 James 3.87
0005 Brook 2.70
Results:
1 Brook 3.500000
5 Brook 2.700000
2 James 3.510000
4 James 3.870000
3 Katie 3.520000
I strongly suggest you single-step through the code in a debugger when trying to fix these problems and when you want to see how code works.
Finally, not to add insult to injury, you never free your list. I.e it leaks memory on program exit, which is second only to leaking memory during execution in levels of "bad". Walk that list and release that memory. It is a good habit to get into.
EDIT OP Request for freeing the linked list:
For now, at the end of main() before the return statement will suffice. At some time you should consider writing a function to do this for you:
while (head)
{
list p=head;
head=head->next;
free(p);
}

Related

C Linked List allways returns null

I'm trying to do a project to check if the word in a list is repeated, but it seens AuxLista allways returns temp-palavra as null?
void AuxLista(Lista *L, char tmp_word) {
if (!L) return;
Lista *temp = L;
printf("\n");
while (temp != NULL)
{
if(temp->palavra == tmp_word){
printf("%s Está repetida. ", temp->palavra);
}
printf("TESTE %s", temp->palavra);
temp = temp->prox;
}
}
Lista *criarLista(char *word){
Lista *result = malloc(sizeof(Lista));
AuxLista(result, word);
result->palavra = word;
result->prox = NULL;
if (result->NOCORRENCIAS == NULL) result->NOCORRENCIAS = 1;
else result->NOCORRENCIAS = result->NOCORRENCIAS + 1;
return result;
}
List item
Your AuxLista function is void -- it doesn't return anything.
Also, be aware that the result of malloc() is uninitialized memory. If you don't set each byte to a value, then you must assume that the byte has the worst possible value it could have. You might consider using calloc or passing the memory to memset to initialize everything to 0.
And you should check that malloc didn't return NULL, which it will do if you aren't looking.
Finally, don't use NULL to mean 0. Your NOCORRENCIAS appears to be an integer (because you set it to 1) which means you would normally be checking it against 0 rather than NULL. But that whole if/else appears to be wasted -- you could probably just do result->NOCORRENCIAS += 1 and handle the zero/non-zero cases the same.

How can I improve running time in C

I repeat this function 2500 times in a loop for different parameters and it takes 85 seconds. What is wrong with this function? How can I improve running time? Thanks for your help.
Function:
int findBaconNumber(v * actors[], int actorCount, int index, v * visited[]){
// Bacon number is 0:
if(strcmp(actors[index]->name, "Bacon,Kevin") == 0)
return 0;
// Bacon number is infinite:
else if(actors[index]->next == NULL)
return -1;
// Otherwise, calculate:
memset(visited, NULL, sizeof(visited));
q * queue = createQueue();
v * tmp = actors[index];
v * found;
n * tmp2;
int baconNumber = 0;
int visitCount = 0;
int empty = 0;
int full = 0;
// Add first item to queue:
enqueue(queue, tmp);
visited[visitCount] = tmp;
visitCount++;
tmp->parent = NULL;
// Until queue is empty, queue is full or kevin bacon found:
while(strcmp(tmp->name, "Bacon, Kevin") != 0 && isEmpty(queue) != 1 && isFull(queue) != 1){
// Get neighbors:
if(tmp->next != NULL)
tmp2 = tmp->next;
else
tmp2 = NULL;
// Add neighbors to queue:
while(tmp2 != NULL){
if(contains(visited,visitCount+1,actors[tmp2->actorNo]) != 1){
enqueue(queue, actors[tmp2->actorNo]);
visited[visitCount] = actors[tmp2->actorNo];
visitCount++;
actors[tmp2->actorNo]->parent = tmp;
}
tmp2 = tmp2->next;
}
// Dequeue current item and skip to next item:
dequeue(queue);
// Get next item in queue:
if(getFront(queue) != NULL)
tmp = getFront(queue);
}
if(strcmp(tmp->name, "Bacon, Kevin") != 0)
return -1;
while(tmp->parent != NULL && tmp != NULL){
baconNumber++;
tmp = tmp->parent;
}
return baconNumber;
}
For Loop:
for(i=0; i<actorCount; i++){
baconNumbers[i] = findBaconNumber(actors, actorCount, i, visited);
}
EDIT:
Thanks all of you for answers. This is my school project and it must be solved by breadth first search algorithm so I can not use other solutions. I profiled the code it looks like the problem is with the contains function. I use it to check if node is visited or not. Now, I am working on finding another solution for this.
img1
img2
Some things that gets to messy to write in comments:
1) Use a profiler to find out what parts of the code that is actually taking time. It can be wise to break out all loops into functions to get more useful information from the profiler.
2) Use the information from the profiler to optimize the code
You're not showing the implementation of queue. That could be the bottleneck. If it is, there are basically two ways to attack it. Either try to optimize the queue code, or change the overall algorithm to use the queue less or in a smarter way.
Solving the Bacon number is basically a shortest path problem, so you might want to tinker with different algorithms. Dijkstras and A* are common shortest path algorithms.
Another thing you might look into is preprocessing the data. Right now, you have a struct array, and the structs have a field called name. Maybe you could change this to an int where 1 indicates that the name is "Bacon,Kevin" and 0 that it's not. That would save you potentially costly calls to strcmp and might also make it more cache friendly.
You might want to change the representation completely. Right now, I get the impression that you have something like this:
struct actor {
struct actor *parent, *next;
char *name;
}
I already mentioned that you could change name to a simple integer, but perhaps you could also change everything to:
int *names;
int *parents;
int *next;
That could make the code a lot more cache friendly. Or not. But it is worth trying. Linked lists are usually not very cache friendly. And this can be good to think about when it comes to the queue too.
Also, you might have a look at this: Calculating "Kevin Bacon" Numbers
I solved this problem with your helpful advices.
The way I used before (Too slow => takes 85 seconds for my input):
-When a node is visited, append this node to the end of visited[] array.
-Checking if node is visited or not by looping over all elements of visited[] array (via contains() function)
Faster way which I am using now (1.75 seconds for the same input):
-When a node is visited, assign this node to related index => (visited[actorNumber] = node;)
-Checking if node is visited or not without looping over all elements, only checking the related index.
I also removed strcmp and I am checking by index now.
Updated code is here:
int findBaconNumber(v * actors[], int actorCount, int index, int baconsIndex){
// Bacon number is 0:
if(actors[index]->index == baconsIndex)
return 0;
// Bacon number is infinite:
else if(actors[index]->next == NULL)
return -1;
// Otherwise, calculate:
q * queue = createQueue();
v * visited[ACTORS_SIZE] = {};
v * tmp = actors[index];
v * found;
n * tmp2;
int baconNumber = 0;
int empty = 0;
int full = 0;
// Add first item to queue:
enqueue(queue, tmp);
visited[tmp->index] = tmp;
tmp->parent = NULL;
// Until queue is empty, queue is full or kevin bacon found:
while(tmp->index != baconsIndex && isEmpty(queue) != 1 && isFull(queue) != 1){
// Get neighbors:
if(tmp->next != NULL)
tmp2 = tmp->next;
else
tmp2 = NULL;
// Add neighbors to queue:
while(tmp2 != NULL){
// If not visited:
if(visited[tmp2->actorNo] == NULL){
enqueue(queue, actors[tmp2->actorNo]);
visited[tmp2->actorNo] = actors[tmp2->actorNo];
actors[tmp2->actorNo]->parent = tmp;
}
tmp2 = tmp2->next;
}
// Dequeue current item and skip to next item:
dequeue(queue);
// Get next item in queue:
if(getFront(queue) != NULL)
tmp = getFront(queue);
}
if(tmp->index != baconsIndex)
return -1;
while(tmp->parent != NULL && tmp != NULL){
baconNumber++;
tmp = tmp->parent;
}
return baconNumber;
}

Can someone help me find the segfault here?

EDIT: So, it turns out that 'index' was not being returned to 0. Well then. That fixed one segfault. But still getting a different segfault. Working on it.
node* new_node(void){
node* ptr = malloc(sizeof(node));
for (int i = 0; i<27; i++) {
ptr->next[i] = NULL;
}
return ptr;
}
bool load(const char* dictionary)
{
FILE* dict = fopen(dictionary, "r");
node* ptr = new_node;
char word[LENGTH+1];
int index = 0;
for (int c = fgetc(dict); c!=EOF; c = fgetc(dict)){
if(c!='\n'){
word[index]=c;
index++;
}
else {
for(int x=0; x<=index; x++){
int ch = (word[x] == '\'') ? 26 : tolower(word[x])-'a';
if (ptr->next[ch] == NULL){
ptr->next[ch] = new_node;
}
ptr = ptr->next[ch];
}
ptr->end=true;
}
}
return true;
}
I'm trying to implement a trie data structure for a dictionary but my program seems to segfault somewhere in this function. I can't seem to pin it down even with the help of GDB, so can someone give me a hand?
Node is defined as such:
typedef struct node{
bool end;
struct node* next[27];
} node;
Dictionary file:
a
aaa
aaas
aachen
aalborg
aalesund
aardvark
aardvark's
aardvarks
aardwolf
(...)
You have many issues in your code:
When you allocate memory with malloc, it is uninitialised. initialise it directly after allocating it, so that NULL pointers really are null. (calloc, a cousin of ´malloc´, initialises all memory to zero.)
When you loop over the word, you should nor include index:
for (int x = 0; x < index; x++) ...
When you have found the end of a word, you must reset the index to 0. Otherwise, you will append to the old word and overflow the buffer. (You should probably also enforce the upper bound of ´index´.)
Likewise, when you insert a word into the trie, you must reset your pointer for trie traversal to the trie's root. You need two pointers here: A root node pointer and an auxiliary pointer for traversing the trie.
As is, your trie is local to your function. Return the root node, so that other functions can use the trie, or NULL on failure.
Fix these, and you will have a non-crashing function. (It still leaks memory and may not construct the trie properly.)
node *load(const char *dictionary)
{
FILE *dict = fopen(dictionary, "r");
node *head = calloc(1, sizeof(node));
char word[LENGTH + 1];
int index = 0;
for (int c = fgetc(dict); c != EOF; c = fgetc(dict)) {
if (c != '\n') {
word[index] = c;
index++;
} else {
node *ptr = head;
for (int x = 0; x < index; x++) {
int ch = (word[x] == '\'') ? 26 : tolower(word[x]) - 'a';
if (ptr->next[ch] == NULL) {
ptr->next[ch] = calloc(1, sizeof(node));
}
ptr = ptr->next[ch];
}
ptr->end = true;
index = 0;
}
}
return head;
}
The line:
node* ptr = new_node;
and
ptr->next[ch] = new_node;
are not calling the function, but assigning the address of the function to ptr. Call the function instead.
This problem could have been prevented if compiler warnings: -Wall and -Wextra were enabled.
There is no bounds checking done on the array word. Use the value LENGTH to check if the index is in bounds before using it.
It isn't clear what the if statement inside the for loop is doing. It appears that every time a newline is found the whole array word is added to the tree, but the index isn't reset so the same array is added multiple times. At some point index will point out of bounds causing undefined behavior. You should reset index after you use the array word.
You forgot to reset index to 0 at the beginning of the loop.
You should also use calloc(1, sizeof(node)) instead of malloc(sizeof(node)) to avoid leaving memory uninitialized. I suggest you use valgrind to help you track problems of this kind in your code.
You should filter punctuation\unsupported characters a bit more. Any character outside of [a-z|A-Z|\n|\\] will crash your program because of
int ch = (word[x] == '\'') ? 26 : tolower(word[x])-'a';
if (ptr->next[ch] == NULL){
Given that you open a file, there might be a space somewhere or some unexpected character. You need something like
if(c!='\n'){
int num = (c == '\'') ? 26 : tolower(c)-'a');
if(num >=0 && num < 27)
{
word[index]=c;
index++;
}
}

printing array of linked lists

I am trying to print an array of linked lists. I am having trouble getting it to print. Here is my struct.
typedef struct VERTEXTAG
{
char c;
bool isvisited;
struct EDGETAG* p;
}VERTEX;
typedef struct EDGETAG
{
VERTEX* v;
struct EDGETAG* q;
//cookies rock
//I like cookies
}EDGE;
Here are my variable declarations
VERTEX v[100];
EDGE *e;
EDGE* temp;
int counter = 0;
int addcounter = 0;
int i = 0;
Here is where I try to create the linked lists. I have an even case and an odd case.
//even case
if(counter - i == 1 && flag == 0)
{
vertices[addcounter] = (char)c;
//printf("The vertice is %c :\n", vertices[addcounter]);
e = (EDGE*) malloc(sizeof(EDGE));
v[addcounter].p=e;
v[addcounter].c= (char)c;
v[addcounter].isvisited=false;
v[addcounter].p=NULL;
addcounter++;
}
//odd case
if(counter - i == 1 && flag == 0)
{
vertices[addcounter] = (char)c;
//printf("The vertice is %c :\n", vertices[addcounter]);
e = (EDGE*) malloc(sizeof(EDGE));
v[addcounter].p=e;
v[addcounter].c= (char)c;
v[addcounter].isvisited=false;
v[addcounter].p=NULL;
(*e).v= &v[addcounter];
e->q = NULL;
addcounter++;
}
Here is where I try to print my linked list. For some reason temp is equal to NULL so it is not printing. I know I am correctly passing my variables to each case with vertices array. It prints out correctly. I am not sure if I am correctly creating the linked list of arrays since it will not print out. Also the second print statement in the while loop creates a segmentation when I take it out of the while loop so the program can reach it.
temp = v[0].p;
if(temp == NULL)
{
printf("Temp is Null\n");
}
while(temp != NULL)
{
printf("While loop");
printf("%c", (*(*temp).v).c);
temp = temp->q;
}
printf("The vertice is %s :\n", vertices);
Your problem is likely here:
v[addcounter].p=e;
v[addcounter].c= (char)c;
v[addcounter].isvisited=false;
v[addcounter].p=NULL;
Why are you setting v[x].p to e, then setting it to NULL a few lines after? Later on when you try to access v[0].p, of course it's going to still be NULL.

graph implementation with adjacency lists in C

I just started learning C and as a self-learning excercise, I am implementing data structures and algos in C. Right now I am working on a graph and this is the data structure representation of it.
typedef int graphElementT;
typedef struct graphCDT *graphADT;
typedef struct vertexTag
{
graphElementT element;
int visited;
struct edgeTag *edges;
struct vertexTag *next;
} vertexT;
typedef struct edgeTag
{
int weight;
vertexT *connectsTo;
struct edgeTag *next;
} edgeT;
typedef struct graphCDT
{
vertexT *vertices;
} graphCDT;
To this graph I added a addVertex function.
int addVertex(graphADT graph, graphElementT value)
{
vertexT *new = malloc(sizeof(*new));
vertexT *vert;
new->element = value;
new->visited = 0;
new->edges = NULL;
new->next = NULL;
int i = 0;
for(vert=graph->vertices; vert->next != NULL; vert=vert->next)
{
if(vert->element == value)
{
printf("already exists\n");
return 0;
}
}
vert->next = new;
//free(new);
printf("\ninserted %d\n", vert->element);
return 1;
}
This works fine except for three things.
if the newly added vertex is the same as the last vertex in the list, it fails to see it. To prevent this i changed the for loop limiting condition to vert != NULL, but that gives a seg fault.
if i try to free the temporarily allocated pointer, it resets the memory pointer by the pointer and this adds an infinite loop at the end of the vertex list. Is there no way to free the pointer without writing over the memory it points to? Or is it not really needed to free the pointer?
Also would destroying the graph mean destroying every edge and vertices? or is there a better approach?
Also if this data structure for graph is not a good one and there are better implementations, i would appreciate that being pointed out.
1
If you change the limiting condition to vert!=NULL , and if the loop ends with vert==NULL ,i.e. ,the vertex to be added isn't present , then you will be reading next statement :
vert->next = new;
That means you are accesing the NULL ,vert pointer , hence the seg fault .
Now to allow checking if the last element isn't the vertex to be added ,and also to prevent seg fault ,do this :
for(vert=graph->vertices; vert->next != NULL; vert=vert->next)
{
if(vert->element == value)
{
printf("already exists\n");
return 0;
}
}
if(vert->element == value)
{
printf("already exists\n");
return 0;
}
vert->next = new;
2
The temporary "new" pointer is the memory location allocated to the Vertex you added .IT IS NOT to be freed ,as freeing it will mean that you deleted the vertex you just added :O .
3
Yes , detroying the graph essentialy means the same .
It is always a good practice to implement linked list as a adjacency list implementation of graph .Although you can always use a c++ "2 D Vector" to implement the same .
Here's a working addVertex function that you can use.
I am keeping the original declarations as it is.
I have added a main () to which you can give command line arguments to test.
int addVertex(graphADT graph, graphElementT value)
{
vertexT *tmpvert , *vert ;
vert=graph->vertices ;
/*check to see whether we really need to create a new vertex*/
tmpvert = vert;
while(tmpvert != NULL)
{
/* U can put a debug printf here to check what's there in graph:
* printf("tmpvert->elem=%d ", tmpvert->element);
*/
vert = tmpvert;
if(tmpvert->element == value)
return 0;
tmpvert=tmpvert->next ;
}
/*If we are here , then we HAVE to allocate memory and add to our graph.*/
tmpvert = (vertexT*)malloc(sizeof(vertexT));
if ( NULL == tmpvert )
return 0; /* malloc failure */
tmpvert->element = value;
tmpvert->visited = 0;
tmpvert->edges = NULL;
tmpvert->next = NULL;
if ( NULL == vert )
graph->vertices = tmpvert; /*Notice that I dont use virt=tmpvert */
else
vert->next = tmpvert; /*putting stuff in next is fine */
return 1;
/* Dont try printing vert->element here ..vert will be NULL first time */
/*return code for success is normally 0 others are error.
*That way you can have your printfs and error code
*handling outside this function.But its ok for a test code here */
}
Now for the main () snippet for testing :
int main (int argc , char* argv[]) {
graphADT graph ;
graph =(graphADT) malloc ( sizeof(struct graphCDT) );
graph->vertices = NULL;
while ( --argc >0)
{
int value = atoi(argv[argc]);
addVertex(graph,value);
}
}

Resources