Issues with linked list and pointers (C) - c

I am writing a C program to sort a linked list according to the largest values. I met an issue whereby the program just hangs when the program reached "prevPtr->next = headPtr".
I want the prevPtr->next to equate to headPtr, if the sum of prevPtr is larger than the sum of headPtr, however the program just hangs there.
compareNodes() function is used to compare the nodes to see if newNode has the same name as any other structs in the linked list, then it will add in the sum.
sortSimilarNodes() function is used to sort the nodes according to the sum of each struct.
The struct is here below:
struct purchase {
char name[30];
double sum;
struct purchase * next;
} ;
LOG * compareNodes(LOG * headPtr, char * name, char * price){
.
.
.
while (curPtr != NULL) {
if (strcmp(newNode->name, curPtr->name)==0) {
curPtr->sum += newNode->sum;
free(newNode);
similar = 1;
break;
}
//advance to next target
prevPtr = curPtr;
curPtr = curPtr->next;
}
/*if (curPtr == NULL){
if(strcmp(newNode->name, prevPtr->name)==0){
prevPtr->sum += newNode->sum;
free(newNode);
similar = 1;
}
}*/
if (similar == 1){
headPtr = sortSimilarNodes(curPtr, headPtr);
}
else{
headPtr = sortNodes(newNode, headPtr);
}
return headPtr;
}
LOG * sortSimilarNodes(LOG * newPtr, LOG * headPtr){
LOG * curPtr;
LOG * prevPtr;
if(headPtr->sum < newPtr->sum){
newPtr->next = headPtr;
return newPtr;
}
prevPtr = headPtr;
curPtr = headPtr->next;
while (curPtr == NULL){
}
while (curPtr != NULL){
if(strcmp(curPtr->name, newPtr->name)==0){
break;
}
prevPtr = curPtr;
curPtr = curPtr->next;
}
return headPtr;
}
This is the output of the program.
Thank you!

It's hard to tell from your code, because you haven't posted all of it, but you seem to have some misconceptions about linked lists. In particular:
There is no need for new nodes unless you really add new nodes to the list. That also means that you don't call malloc except when adding nodes. (There's no malloc in your code, but a suspicious free in your comparison function. Comparing does not involve creating or destroying anything; it just means to look what is already there.)
A corollary to the first point is that there should be no nodes in an empty list, not even dummy nodes. An empty list is a list whose head is NULL. Make sure that you initialise all head pointers before creating a new list:
LOG *head = NULL; // empty list
When you sort the list, the order of the list has changed and the old head is invalid. You cater for that by returning the new head:
head = sort(head);
But that seems redundant and it also seems to imply that the two pointers can be different. That's not the case, because the old pointer will point somehwre in the sorted list, not necessarily at its head. It's probably better to pass the head pointer's address in order to avoid confusion:
sort(&head);
Sorting linked lists can be tricky. One straightforward way is selection sort: Find the node with the highest value, remove it from the original list and add it at the front of a new list. Repeat until there are no more nodes in the original list.
Adding a new node n at the front of a list given by head is easy:
n->next = head;
head= n;
Adding a new node at the end of a list that is given by head is a bit more involved:
LOG **p = &head;
while (*p) p = &(*p)->next;
*p = n;
n->next = NULL;
Here, p is the address of the pointer that points to the current node, *p. After walking the list, that address is either the address of the head node (when the list is empty) or the address of the next pointer of the precedig node.
You could achieve something similar by keeping a prev pointer, but the pointer-to-pointer solution means that you don't have to treat the cases where there is no previous node specially at the cost of some extra & and * operators.
With that, your sorting routine becomes:
void sortByName(LOG **head)
{
LOG *sorted = NULL;
while (*head) {
LOG **p = head; // auxiliary pointer to walk the list
LOG **max = head; // pointer to current maximum
LOG *n; // maximum node
while (*p) {
if (strcmp((*p)->name, (*max)->name) > 0) max = p;
p = &(*p)->next;
}
n = *max;
*max = (*max)->next;
n->next = sorted;
sorted = n;
}
*head = sorted;
}
If you want to sort by sum, change the comparison to:
if ((*p)->sum > (*max)->sum) max = p;
Call the function like this:
LOG *head = NULL;
insert(&head, "apple", 2.3);
insert(&head, "pear", 1.7);
insert(&head, "strawberry", 2.2);
insert(&head, "orange", 3.2);
insert(&head, "plum", 2.1);
sortByName(&head);
print(head);
destroy(&head);
with the insert, destroy and print functions for completeness:
void insert(LOG **head, const char *name, double sum)
{
LOG *n = malloc(sizeof(*n));
if (n) {
snprintf(n->name, sizeof(n->name), "%s", name);
n->sum = sum;
n->next = *head;
*head = n;
}
}
void destroy(LOG **head)
{
LOG *n = *head;
while (n) {
LOG *p = n;
n = n->next;
free(p);
}
*head = NULL;
}
void print(LOG *l)
{
while (l) {
printf("%s: %g\n", l->name, l->sum);
l = l->next;
}
puts("");
}

Related

Why aren't new elements being added to my linked list?

This is just a snippet of the code, but I checked and know for a fact that all the strings save nicely into the "new" element (in function SortedInsert), but then the "new" doesn't link to the head?
I've tried everything I could think, hopefully I'm just missing something obvious.
typedef struct _Info* Position;
typedef struct _Info{
char name[MAX];
char surname[MAX];
Position next;
} Info;
(declaration inside main function:
Info headInfo = {.name = {0}, .surname {0}, .next = NULL};
Position head = &headInfo;
)
int SortedInsert(Position head, char name[], char surname[]){
Position prev = NULL, temp = NULL, new = NULL;
prev = head;
temp = head->next;
new = (Position)malloc(sizeof(Info));
if(!new){
return EXIT_FAILURE;
}
strcpy(new->name, name);
strcpy(new->surname, surname);
new->next = NULL;
if(head->next==NULL){
temp = new;
}
else{
// first sort, by surname
while(strcmp(temp->surname, new->surname) < 0){
prev = temp;
temp = temp->next;
}
// second sort, by name
while(strcmp(temp->name, new->name) < 0){
prev = temp;
temp = temp->next;
}
new->next = prev->next;
prev->next = new;
}
return EXIT_SUCCESS;
}
int PrintList(Position head){
Position temp = NULL;
temp = head->next;
while(temp){
printf("%s ", temp->name);
printf("%s\n", temp->surname);
printf("---\n");
temp = temp->next;
}
return EXIT_SUCCESS;
}
Some issues:
temp = new does not insert anything into the list. It merely copies a reference to the new node into a local variable. The assignment should be to head->next. Moreover, there is no need to create a separate case for this. It can be handled with the code you have in the else part.
The retrieval of the insert point is not correct. If in the first loop the strcmp call returns 1 (not 0), then the second while loop should not iterate at all: it doesn't matter in that case what the first name is like. The last name of temp is already greater, so the insertion point has been found. Similarly, if the strcmp call returns 0, the second loop should keep verifying that the last name is still the same in its second iteration,...etc. Moreover, this logic can be combined in one loop.
Not a problem for the correct execution, but still:
Many consider it bad practice to typedef a pointer to a struct where you dereference the pointer regularly in your code. See the answers to Is it a good idea to typedef pointers? for some background. So I'd keep using Info *.
Create a separate function for creating and initialising a node.
The comments that say "first sort", "second sort" are misleading. There is no sorting happening in the loop that follows the comment. The list is already sorted. The process that follows just intends to find the insertion spot according to the sort order. So the comment could be improved.
Many consider it better not to cast the value returned by malloc.
Here is the correction of the SortedInsert function, together with the separated function for node creation:
Info *createNode(char name[], char surname[]) {
Info *new = malloc(sizeof(*new));
if (new != NULL) {
strcpy(new->name, name);
strcpy(new->surname, surname);
new->next = NULL;
}
return new;
}
int SortedInsert(Info *head, char name[], char surname[]){
Info *new = createNode(name, surname);
if (new == NULL) {
return EXIT_FAILURE;
}
Info *prev = head;
Info *temp = head->next;
// Find insertion spot according to sort order
while (temp != NULL) {
int cmp = strcmp(temp->surname, new->surname);
if (cmp == 0) { // It's a tie. Then use name as discriminator
cmp = strcmp(temp->name, new->name);
}
if (cmp >= 0) { // Found insertion spot
break;
}
prev = temp;
temp = temp->next;
}
new->next = prev->next;
prev->next = new;
return EXIT_SUCCESS;
}

Updating linked-list by pointer inside a function

I'm missing with linked-list and trying to make a function which gonna take of all the odd numbers out of the link and make a new linked-list with them.
The point is that I dont understand how to update the original list by pointer to the function, actually what I made so far is making a new list with the odd numbers but I dont really understand how to "delete" them from the original list and link all the rest togther, then send it back to the main.
Node *build_odd_list(Node *oldlst, Node *newlst) {
Node *temp, *curheadNew;
temp = (Node*)malloc(sizeof(Node));
if (oldlst->value % 2 != 0) {
temp->next = NULL;
temp->value = oldlst->value;
newlst = temp;
curheadNew = newlst;
oldlst = oldlst->next;
printf("Passed %d\n", curheadNew->value);
}
else {
oldlst = oldlst->next;
}
while (oldlst) {
if (oldlst->value % 2 != 0) {
temp = (Node*)malloc(sizeof(Node));
temp->value = oldlst->value;
temp->next = NULL;
curheadNew->next = temp;
curheadNew = curheadNew->next;
oldlst = oldlst->next;
printf("Passed %d\n", curheadNew->value);
}
else {
oldlst = oldlst->next;
}
}
return newlst;
}
Thanks a lot!
Since you need to return a new list containing the odd numbers, and modify the original list due to removal of the odd numbers, you need to pass two values back to the caller: a pointer to the first element of the updated original list, and a pointer to the first element of the "odd numbers" list.
Since you need to pass the original list to the function anyway, the simplest option for the function is to:
pass a pointer to a pointer to the first element of the original list;
modify the original list via the pointer;
return a pointer to the first element of the "odd numbers" list extracted from the original list.
There is no need to allocate any new elements for the "odd numbers" list as the odd number elements can be moved from one list to the other.
It is worth learning the "pointer to a pointer" trick as it is a common way of manipulating list pointers.
Here is an example program to illustrate the above method. Pay particular attention to the extract_odd_list() function and the call to that function from main().
#include <stdio.h>
#include <stdlib.h>
typedef struct _Node {
int value;
struct _Node *next;
} Node;
/* Move odd numbers in *list to returned list. */
Node *extract_odd_list(Node **list) {
Node *oddstart = NULL; /* start of returned list */
Node **oddend = &oddstart; /* pointer to final link of returned list */
while (*list) {
if ((*list)->value % 2 != 0) {
/* Current element of original *list is odd. */
/* Move original *list element to end of returned list. */
*oddend = *list;
/* Bypass moved element in original list. */
*list = (*list)->next;
/* Update pointer to final link of returned list. */
oddend = &(*oddend)->next;
}
else {
/* Current element of original *list is even. */
/* Skip to next element of original *list. */
list = &(*list)->next;
}
}
/* Terminate the returned list. */
*oddend = NULL;
/* And return it. */
return oddstart;
}
void *printlist(Node *list) {
while (list) {
printf(" %d", list->value);
list = list->next;
}
}
int main(void) {
int i;
Node *list = NULL;
Node *end = NULL;
Node *oddlist;
Node *temp;
/* Construct a list containing odd and even numbers. */
for (i = 1; i <= 10; i++) {
temp = malloc(sizeof(*temp));
temp->value = i;
if (end == NULL) {
list = temp;
}
else {
end->next = temp;
}
end = temp;
}
end->next = NULL;
printf("Original list:");
printlist(list);
printf("\n");
/* Move the "odd number" elements from the original list to a new list. */
oddlist = extract_odd_list(&list);
printf("Updated list:");
printlist(list);
printf("\n");
printf("Odd list:");
printlist(oddlist);
printf("\n");
return 0;
}

Pointers to pointers - linked list mess

I'm writing a simple C program to manage a linked list defined as follow:
typedef struct node {
int value;
struct node *next;
} *List;
I reviewed the code and it seems okay but when printing results something is not working well.
My main, with problems on comments:
int main(void) {
List n = list_create(1);
insert(n, 2);
insert(n, 3);
insert(n, 5);
insert(n, 4);
//something here does not work properly. It produces the following output:
//Value: 1
//Value: 2
//Value: 3
//Value: 4
//where is value 5?
print_list(n);
delete(n, 3);
print_list(n);
return 0;
}
I don't know where am I destroying list structure. These are my functions, to debug, if you are too kind.
List list_create(int value) {
List new = malloc(sizeof(struct node));
new->value = value;
new->next = NULL;
return new;
}
List new_node(int value, List next_node) {
List new = malloc(sizeof(struct node));
new->value = value;
new->next = next_node;
return new;
}
void print_list(List l) {
List *aux;
for (aux = &l; (*aux) != NULL; aux = &((*aux)->next))
printf("Valor: %d\n", (*aux)->value);
}
void insert(List l, int value) {
List *p;
for (p = &l; (*p) != NULL; p = &((*p)->next))
if ((*p)->value > value) {
List tmp = *p;
List new = new_node(value, tmp);
*p = new;
break;
}
*p = new_node(value, NULL);
}
void delete(List l, int value) {
List *p;
for (p = &l; (*p) != NULL; p = &((*p)->next))
if ((*p)->value == value) {
List del = (*p);
(*p) = ((*p)->next);
free(del);
break;
}
}
This code has (at least) two bugs:
The line
if ((*p)->value > value){
means that if you start the list with 1 as the first value and then try to insert 2,3,4..., the body of the 'if' statement never runs, so nothing ever gets inserted.
If you insert a value below the starting value, you have to modify the list pointer itself. However, as #EOF alluded, you are trying to modify a value passed to a function by taking its address. This won't work. &l does not give you the address of the List you passed, it gives you the address of the local copy on insert()'s stack. You are better off modifying the values of first element of the list 'in place'. If you really want to make the List parameter mutable, you'll need to pass it as a List *, and call the function with the address of the list (e.g. insert(&n,2); ) Your delete() function suffers from the same problem - try deleting the first element of the list.
Try this for your insert function:
void insert(List l, int value)
{
List p;
// Find end of list or highest item less than value
for(p = l; p->next != NULL && p->next->value < value; p = p->next);
if (p->value >= value) {
// Over-write p with new value, and insert p as a new one after.
// This saves having to modify l itself.
int tmpval = p->value;
p->value = value;
p->next = new_node(tmpval, p->next);
} else {
// Insert new item after p
p->next = new_node(value, p->next);
}
}
A comment: it is possible the way you are using pointers is not helping the debugging process.
For example, your print_list() could be re-written like this:
void print_list(List l){
List aux;
for(aux = l; aux != NULL; aux = aux->next)
printf("Valor: %d\n", aux->value);
}
and still behave the same. It is generally good practice not to 'hide' the pointer-like nature of a pointer by including a '*' in the typedef.
For example, if you define your list like this:
typedef struct node{
int value;
struct node *next;
} List
And pass it to functions like this:
my_func(List *l, ...)
then it'll make some of these issues more apparent. Hope this helps.
There are many problems in your code:
Hiding pointers behind typedefs is a bad idea, it leads to confusion for both the programmer and the reader.
You must decide whether the initial node is a dummy node or if the empty list is simply a NULL pointer. The latter is much simpler to handle but you must pass the address of the head node to insert and delete so they can change the head node.
printlist does not need an indirect pointer, especially starting from the address of the pointer passed as an argument. Simplify by using the Node pointer directly.
in insert you correctly insert the new node before the next higher node but you should then return from the function. Instead, you break out of the switch and the code for appending is executed, replacing the inserted node with a new node with the same value and a NULL next pointer. This is the reason 5 gets removed and lost when you insert 4. Furthermore, you should pass the address of the head node so a node can be inserted before the first.
delete starts from the address of the argument. It cannot delete the head node because the pointer in the caller space does not get updated. You should pass the address of the head node.
You should avoid using C++ keywords such as new and delete in C code: while not illegal, it confuses readers used to C++, confuses the syntax highlighter and prevents compilation by C++ compilers.
Here is a simplified and corrected version:
#include <stdio.h>
#include <stdlib.h>
typedef struct Node {
int value;
struct Node *next;
} Node;
Node *new_node(int value, Node *next_node) {
Node *node = malloc(sizeof(*node));
if (node != NULL) {
node->value = value;
node->next = next_node;
}
return node;
}
void print_list(Node *list) {
for (; list != NULL; list = list->next)
printf("Valor: %d\n", list->value);
}
void insert_node(Node **p, int value) {
while ((*p) != NULL && (*p)->value < value)
p = &(*p)->next;
*p = new_node(value, *p);
}
void delete_node(Node **p, int value) {
while (*p != NULL) {
if ((*p)->value == value) {
Node *found = *p;
*p = (*p)->next;
free(found);
// return unless delete() is supposed to remove all occurrences
return;
} else {
p = &(*p)->next;
}
}
}
int main(void) {
Node *n = NULL;
insert_node(&n, 2);
insert_node(&n, 3);
insert_node(&n, 5);
insert_node(&n, 4);
insert_node(&n, 1);
print_list(n);
delete_node(&n, 3);
print_list(n);
delete_node(&n, 1);
print_list(n);
return 0;
}

Adding node in Linked List in a specific position in C

I'm trying to add a node to a linked list. The idea is to pass in the pointer, see where new node will go to through a ranked order, in this case G, then D, then M, then S.
Yet, when I compile and run, I'm not actually generating a linked list (this has already been done in the main). I'm more than certain that there's something wrong with my addp() function. Is it that I should pass in double pointers instead?
Sorry for being rather unprofessional and clueless. I'm not the strongest of coders.
Any help would be helpful.
I have attached my method which I have gone through so many times.
typedef struct node {
char fname[1024];
char lname[1024];
char pos;
int val;
int rank;
struct node * next;
} player;
struct node* addp (player* newnode, struct node* list){
player* templist = list;
player* templist1;
// if the list is non empty.
if (list!=NULL){
if(newnode->pos == GOALKEEPER){ //insert if G.
newnode->next = list;
}
if(newnode->pos == DEFENDER){// after G bef M.
// iterate through templist.
while (templist->next != NULL && (templist->next)->rank < 1) { // go to end of G.
// when the list isn't empty next node rank is less than one, keep going
templist = templist -> next;
}
// when finally rank == or > 1, then add newnode.
templist1 = templist->next;
templist->next = newnode;
newnode->next = templist1;
}
if(newnode->pos == MIDFIELDER){ //after G and M but before S
while (templist->next != NULL && (templist->next)->rank <2 && (templist->next)->rank> 2){
templist = templist -> next;
}
// when stopped, then add newnode.
templist1 = templist->next;
templist->next = newnode;
newnode->next = templist1;
}
if(newnode->pos == STRIKER){ // at the end.
while (templist->next != NULL && (templist->next)->rank <3){
templist = templist -> next;
}
templist1 = templist->next;
templist->next = newnode;
newnode->next = templist1;
}
return list;
printf("player added");
}
// if list is empty
else{
newnode->next = list;
return 0;
}
}
The following is the list function I've come up with. It keeps saying that my linked list is empty. Maybe it's something wrong with this function.
int print(struct player* list){
// create temp list so non modify origin.
struct player* temp = list;
if (list == NULL && temp == NULL)
printf("linked list is empty");
while (temp != NULL){
printf("%s \n", temp->lname);
printf("%s \n", temp->fname);
printf("%c \n", temp->pos);
printf("d \n", temp->val);
temp = temp->next;
}
return 0;
}
Without knowing the player typedef this is hard to analyze, but I think you can make it much easier on yourself by simplifying the signature of the function to only use player.
void addp(player* newnode, player* firstnode)
The return is unnecessary since you're just returning the 2nd argument, which the caller already has. The 2nd argument should be a pointer to a player node, which is the first element in your linked list. If you can call the function without the compiler complaining about implicitly casting pointers then I don't see anything wrong with your algorithm, although it could certainly be simplified.
Ok so what I understood is that your player structure contains a variable pos that will indicate in which place insert the player in the list. Am I right ?
In that case the best thing you can do is to sorted the list by the rank variable. Then modify your pos variable (in the player structure) to match with the rank variable of your list.
Then you will just have to add it with a classic "add in sorted list" function : C++ Add to linked list in sorted orderenter link description here

Splitting a linked list

Why are the split lists always empty in this program? (It is derived from the code on the Wikipedia page on Linked Lists.)
/*
Example program from wikipedia linked list article
Modified to find nth node and to split the list
*/
#include <stdio.h>
#include <stdlib.h>
typedef struct ns
{
int data;
struct ns *next; /* pointer to next element in list */
} node;
node *list_add(node **p, int i)
{
node *n = (node *)malloc(sizeof(node));
if (n == NULL)
return NULL;
n->next = *p; //* the previous element (*p) now becomes the "next" element */
*p = n; //* add new empty element to the front (head) of the list */
n->data = i;
return *p;
}
void list_print(node *n)
{
int i=0;
if (n == NULL)
{
printf("list is empty\n");
}
while (n != NULL)
{
printf("Value at node #%d = %d\n", i, n->data);
n = n->next;
i++;
}
}
node *list_nth(node *head, int index) {
node *current = head;
node *temp=NULL;
int count = 0; // the index of the node we're currently looking at
while (current != NULL) {
if (count == index)
temp = current;
count++;
current = current->next;
}
return temp;
}
/*
This function is to split a linked list:
Return a list with nodes starting from index 'int ind' and
step the index by 'int step' until the end of list.
*/
node *list_split(node *head, int ind, int step) {
node *current = head;
node *temp=NULL;
int count = ind; // the index of the node we're currently looking at
temp = list_nth(current, ind);
while (current != NULL) {
count = count+step;
temp->next = list_nth(head, count);
current = current->next;
}
return temp; /* return the final stepped list */
}
int main(void)
{
node *n = NULL, *list1=NULL, *list2=NULL, *list3=NULL, *list4=NULL;
int i;
/* List with 30 nodes */
for(i=0;i<=30;i++){
list_add(&n, i);
}
list_print(n);
/* Get 1th, 5th, 9th, 13th, 18th ... nodes of n etc */
list1 = list_split(n, 1, 4);
list_print(list1);
list2 = list_split(n, 2, 4); /* 2, 6, 10, 14 etc */
list_print(list2);
list3 = list_split(n, 3, 4); /* 3, 7, 11, 15 etc */
list_print(list3);
list3 = list_split(n, 4, 4); /* 4, 8, 12, 16 etc */
list_print(list4);
getch();
return 0;
}
temp = list_nth(current, ind);
while (current != NULL) {
count = count+step;
temp->next = list_nth(head, count);
current = current->next;
}
You are finding the correct item to begin the split at, but look at what happens to temp from then on ... you only ever assign to temp->next.
You need to keep track of both the head of your split list and the tail where you are inserting new items.
The program, actually, has more than one problem.
Indexes are not a native way to address linked list content. Normally, pointers to nodes or iterators (which are disguised pointers to nodes) are used. With indexes, accessing a node has linear complexity (O(n)) instead of constant O(1).
Note that list_nth returns a pointer to a "live" node within a list, not a copy. By assigning to temp->next in list_split, you are rewiring the original list instead of creating a new one (but maybe it's intentional?)
Within list_split, temp is never advanced, so the loop just keeps attaching nodes to the head instead of to the tail.
Due to use of list_nth for finding nodes by iterating through the whole list from the beginning, list_split has quadratic time (O(n**2)) instead of linear time. It's better to rewrite the function to iterate through the list once and copy (or re-attach) required nodes as it passes them, instead of calling list_nth. Or, you can write current = list_nth(current, step).
[EDIT] Forgot to mention. Since you are rewiring the original list, writing list_nth(head, count) is incorrect: it will be travelling the "short-cirquited" list, not the unmodified one.
I also notice that it looks like you are skipping the first record in the list when you are calculating list_nth. Remember is C we normally start counting at zero.
Draw out a Linked List diagram and follow your logic:
[0]->[1]->[2]->[3]->[4]->[5]->[6]->[7]->[8]->[9]->...->[10]->[NULL]
Your description of what list_split is supposed to return is pretty clear, but it's not clear what is supposed to happen, if anything, to the original list. Assuming it's not supposed to change:
node *list_split(node *head, int ind, int step) {
node *current = head;
node *newlist=NULL;
node **end = &newlist;
node *temp = list_nth(current, ind);
while (temp != NULL) {
*end = (node *)malloc(sizeof(node));
if (*end == NULL) return NULL;
(*end)->data = temp->data;
end = &((*end)->next);
temp = list_nth(temp, step);
}
return newlist; /* return the final stepped list */
}
(You probably want to factor a list_insert routine out of that that inserts a new
node at a given location. list_add isn't very useful since it always adds to the
beginning of the list.)

Resources