Linked list traversing with and without the next field - c

I have written two functions while learning Linked list. First counts and returns number of nodes in the list. Second adds new node towards the end of the list. Can you please help me understand why I need to use "current->next" to check for NULL in my code below in the addatend function? I did not have to use it for first function. Without that my exe crashes with bad pointer...
Thank you very much for your time and help.
int length(node * head) {
node * current = head;
int count = 0;
while(current != NULL){ // This line works as expected....
count ++;
current = current->next;
}
return count;
}
void addatend (node * head, int value){
node * newnode = (struct node *) malloc(sizeof(struct node));
node * current = head;
while (current != NULL){ // This line would not work?? If I use current->next != NULL it works.....
current = current->next;
}
current->next = newnode;
newnode->data = value;
newnode->next = NULL;
}

In the second function your are accessing current->MEMBER after you changed it to current->next, while in the first one you are not. That is, in the first one you just run to end, and end up with a NULL pointer, but you don't use it, so it's ok. In the second one, you do use it.

In order not to mess up with your original linked-list you copy the head to the current, and then you can do what you want with that current node.
Linked lists are generated like:
typedef struct e
{
int data;
struct e *next;
} node;
which means each node (in my example) has a data of type integer and a pointer to the next node. Let's suppose that our list has 4 objects:
1->2->3->4->NULL
the first node (data=1) point to its next node (data=2) and so on till last node (data=4) which the next node does not exist so will point to NULL.
in the function "addatend" in order to add new node to the end of the linked-list you should travel through the last element of the list (data=4 in my example), which the node next to it will point to NULL, so you should go through the list till the end and when you arrive there, you can add the "newnode".

Related

Reverse fuction for singly linked list isn't working as it should

we've given a task to reverse a singly linked list and for some reason i struggling with it
its reversing as it should ,but the head that should be the tail just disappears and i can't figure why, even after debugging
'''
void Reverse(struct node *head) {
struct node *last = NULL;
struct node *current = NULL;
struct node *temp = NULL;
current = head;
while (current->next != NULL) { //getting ptr to last item of the list
current = current->next;
last = current;
};
current = head; //resseting the current ptr back to the head of the list
while (current->next->next != NULL) { //getting the current ptr to one before the tail item
current = current->next;
};
temp = last;
while (last != head) {
if (current->next == last) {
last->next = current;
last = current;
current = head;
if (last == head) {
head->next = NULL;;
head->data = temp->data;
head->next = temp->next;
break;
};
};
};
};
'''
I can't completely follow the logic in your code. You find the last and the second last element, and I will assume that head is some dummy element that you always have (because otherwise, not checking if it is NULL is a problem). After that, I am not sure what happens. You "flip" the last two links if you are looking at the head you update it? (You don't need to set head->next to NULL before you update it two lines later; that doesn't do anything).
Did you want to move current from the head and down to the one before last somewhere in the loop? I think that would work, but you would get a quadratic time algorithm.
If we assume that head is a dummy, so it doesn't have any elements to worry about, and we just want its next pointer to point to the reversed links, you should be able to do something like this:
void reverse(struct node *head)
{
struct node *next = head->next;
head->next = 0;
while (next) {
struct node *next_next = next->next;
next->next = head->next;
head->next = next;
next = next_next;
}
}
In the while-loop you can think of the code as pushing next to the front of the list pointed to by head->next and popping it off the current list (by setting next to next_next. When you reach the end of the list, head->next points to all the links, in reversed order. (I haven't tested the code, but I belive it should work).
If head is not a dummy node, you have to handle the special case that it is NULL, and you have to move its value. It makes things trickier if you have to do the latter, especially if you want to use the list generically, where the user is allowed to allocate links, and you might not know what data is in them (beyond that they have a link embedded in them). I would go for having a dummy link if you can, it makes everything simpler, not just reversal.
Of course, with doubly-linked lists it gets simpler still:
#define swap_p(x,y) \
do { struct node *tmp = x; x = y; y = tmp; } while(0)
void reverse(node *dummy)
{
struct link *p = dummy;
do {
swap_p(p->prev, p->next);
p = p->prev;
} while (p != dummy);
}
but that might not be what you want.
Don't know if that helps, but I hope it does a little at least.

Why does my code not insert a new node into this linked list?

I am new to programming. I just want to know why this doesn't work.
My understanding of pointers isn't clear, especially when using pointers across functions.
void append(struct Node** head_ref, float new_data)
{
struct Node* new_node = (struct Node*) malloc(sizeof(struct Node));
struct Node *last = *head_ref; /* used in step 5*/
new_node->data = new_data;
new_node->next = NULL;
while (last != NULL)
last = last->next;
last = new_node;
return;
}
void append(struct Node** head_ref, float new_data)
{
while (last->next != NULL)
last = last->next;
last->next = new_node;
return;
}
In the first function the new data doesn't get included, I get only the original linked list.
But the second function works just fine. How does a double pointer work when inserting a new node in the beginning of the linked list? (I have seen answers regarding this question, but I am still confused)
In the first example, you move the pointer last until it points at a NULL location. Then, you set the pointer to new_node. However, at this point, last has no real association to your linked list. It is just a pointer to some memory. In the second example, the correct one, you iterate until you reach the tail of the linked list, where next of that node is NULL. Then, you set that next to new_node. There is now a new tail to the list, that is new_node.
Changing the local variable last does not change the value of the data member next of the previous (last) node.
To be more clear let's assume that the list is empty. Then you have to change the pointer referenced by this double pointer head_ref.
You declared a new pointer
struct Node *last = *head_ref;
The loop
while (last != NULL)
last = last->next;
is skipped because now already last is equal to NULL die to the initialization in the previous declaration. And then you changed this local variable last
last = new_node;
The original value of the pointer pointed by head_ref was not changed because last and *head_ref occupy different extents of memory. You changed the memory occupied by last but not changed the memory occupied by head_ref.
Also you should check whether a memory was successfully allocated.
The function can look the following way
int append( struct Node **head_ref, float new_data )
{
struct Node *new_node = malloc( sizeof( struct Node ) );
int success = new_node != NULL;
if ( success )
{
new_node->data = new_data;
new_node->next = NULL;
while ( *head_ref != NULL ) head_ref = &( *head_ref )->next;
*head_ref = new_node;
}
return success;
}
As for this loop (I think you wanted just to show the loop not a whole function)
while (last->next != NULL)
last = last->next;
last->next = new_node;
then you are changing the data member next of the previous (last ) node.
Though this loop will not work if initially head_ref is equal to NULL.

Linked Lists C Deleting Node

I'm just starting to learn linked lists and i was practicing by writing some basic functions such as add or delete. The program was working for some time but then i guess i did something and it starting giving me segmentation fault at my delete function which is this. The segmentation fault is in the while loop in if. Any idea why? Thanks in advance and sorry for my bad english :).
void deleteNode(struct node **first, int age)
{
struct node *tempNode;
if((*(*first)).age == age)
{
tempNode = *first;
*first = (*first)->next;
free(tempNode);
}
struct node *currentNode = (*first)->next;
struct node *lastNode = *first;
while(currentNode!=NULL)
{
//Segmentation Fault
if(currentNode->age == age)
{
tempNode = currentNode;
lastNode->next = currentNode->next;
free(tempNode);
}
lastNode = currentNode;
currentNode = currentNode->next;
}
}
The problem here is that you are referencing lastNode after it has been freed. If we look at the trace of the loop, you free tempNode, which you have assigned to currentNode, and then you set lastNode to currentNode. You want lastNode to point to the last valid node, so the update lastNode = currentNode is only valid if currentNode has not been freed.
In short, the fix is to only assign lastNode = currentNode if you don't remove the current node.
The trick with freeing nodes from a list is to get the information out of the node before you free it. Attempting to access memory that has been freed results in undefined behavior.
In this case you are only dealing with next, and you are tracking lastNode. So, lastNode->next is the logical currentNode.
In the beginning of your function, you account for the case that the first node matches age. However, if that is the case, the next node may also match age. So, your first matches age case also needs to be a loop.
Instead of writing two loops, use the concept of address of previous node's next to represent lastNode. This works well since first is already a pointer to a pointer to node.
struct node **lastNode = first;
while (*lastNode != NULL) {
struct node *currentNode = *lastNode;
if (currentNode->age != age) {
lastNode = &currentNode->next;
continue;
}
/* remove currentNode */
*lastNode = currentNode->next;
free(currentNode);
}

A method to delete the elements in a linkedlist using C

So I'm asked to make a method that empty out the entire linked list.
This is what I have now, and I have no idea why it doesn't want to work:
void deleteList(){
}
Your function needs to be passed the head of the list, i.e. struct node *head that it can operate on. Then you can use that instead of current to keep track of the current head.
void deleteList(struct node *head){
struct node* next;
while (head!= NULL) {
next = head->next;
free(head);
head = next;
}
}
Edit:
Since the list head is a global variable, then you would do this:
struct node *head; // Initialized elsewhere
void deleteList(){
struct node* current = head;
struct node* next;
while (current != NULL) {
next = current->next;
free(current);
current = next;
}
}
Your function never assigns "current" to any node to begin with. Because of this your while loop never runs because "current" effectively always equals NULL (hopefully, actually it is undefined and could cause very strange behavior). The loop is always skipped. Try passing the first node of the list into your function as a parameter:
void deleteList(struct node * start) {
//"start" points to the first node in the list, point "current" at it
struct node * current = start;
struct node * next;
while (current != NULL) {
next = current->next;
//what's this pop doing? It seems unnecessary to the logic here
pop(next);
//free "current" and then point "current" at "next"
free(current);
current = next;
}
}
This would also allow you to release an arbitrary segment of the end of the list by providing "start" the node you want to begin releasing at. It need not be the very first node.

Adding nodes in C Linked List and listing them

I'm practicing with linked lists in c, I want to make a program that reads entries from keyboard and lists the contents of the linked list after adding each node, but the output is always the first node, i can't figure out why, could someone please point out the flaw in this code:
#include <stdio.h>
#include <stdlib.h>
typedef int ID;
typedef struct node {
ID id;
struct node * next;
} node_t;
int main(){
node_t * head;
node_t * conductor;
head = NULL;
ID id;
int i=0;
while(i<10){
printf("Introduce an id:\n");
scanf("%d", &id);
if(head == NULL){
head = malloc(sizeof(node_t));
head->id = id;
head->next = NULL;
}
else{
conductor = head;
while(conductor != NULL)
conductor = conductor->next;
conductor = malloc(sizeof(node_t));
conductor->id = id;
conductor->next = NULL;
}
conductor = head;
printf("the list contains these elements:\n");
while(conductor != NULL){
printf("%d\n", conductor->id);
conductor = conductor->next;
}
++i;
}
}
He problem is in your while loop at
while (conductor != NULL)
To add a new node, you need to set conductor->next, after finding the last conductor.
Something like this could work:
While (conductor->next != NULL)
You are running off the end of the list before inserting. You should go till conductor->next becomes NULL , insert new node and set conductor->next to point to the new node.
Some other pointers:
Indent properly. This will make it easier to spot errors
Split code into functions such as addNode, removeNode, printList etc,
Adding elements one by one to end of linked list is O(n) time for each insertion. Either maintain tail pointer or insert at the head for O(1) insertion. This is very important and results in lot of speedup with trivial effort. With head & tail being tracked, you can insert both sides and remove from head in O(1). Removal from tail will still take O(n) though AFAIK
You need something like the following
conductor = head;
// Find the last node in the list
while(conductor->next != NULL)
conductor = conductor->next;
// Setup the new node
node_t* newNode = malloc(sizeof(node_t));
newNode->id = id;
newNode->next = NULL;
// Have the last node's next pointer point to the new node
constructor->next = newNode;
The key is that you move constructor to the last node in the list, the new node will be wired into the list following the last node.
Typically with a linked list you should keep track of the current head and tail, this will improve performance when adding new nodes, you will note need to traverse the existing list everytime.

Resources