Using loop to traverse through linked list - c

I was reading about a few basic operations on linked list and I saw two types of loops being used predominantly
struct node {
int data;
struct node *next;
}*start=NULL,*tmp;
The first loop was of the form
for(tmp=start;tmp->next!=NULL;tmp=tmp->next);
Using the above loop, now the tmp pointer points towards the last node in the list
The second loop was of the form
tmp=start;
while(tmp!=NULL)
{
// do something
}
I think that both of them do the same work, but I'm not sure. Is there any difference?

I suppose your while loop is something like this.
temp=start;
while(temp!=NULL)
{
// do something
temp= temp->next;
}
In your code of for loop, When you are out of the for loop, temp is not pointing to NULL. temp is pointing to end of the linked list. But in case of while loop, your temp is pointing to NULL after you exit the while loop and you don't have tail(Unless you assign temp to any other temporary variable to change the logic of program) with you if you want to use it in the further steps. That is the only difference. Except that there isn't much difference.
You could have checked it by writing a small program and printing the results. I recommend you do it.

The loops aren't identical. In fact, your for loop has a problem. Consider what happens when start==NULL before you enter the for loop.
for(tmp=start;tmp->next!=NULL;tmp=tmp->next);
You assign start to tmp and then dereference tmp, a NULL pointer. I think you want the following instead.
for(tmp=start;tmp!=NULL;tmp=tmp->next);
That change makes the for and while loops the same.

Q: Effectively, "no". There isn't any substantive difference; they both do the same work.
You can always code a "for()" loop with an equivalent "while()".

I use while loop when I need to change the linked list. For e.g.
while (root->next)
{
if(0 == strcmp(root->data,root->next->data))
{
temp = root;
root = root->next;
free(temp)
}
else
{
root = root->next;
}
}
I use for loop when I need a read only access to linked list.

Related

Swapping nodes in a linked list?

I wrote this function in order to swap 2 nodes in a linked list, but the result is a Segmentation Fault. Can you check it? Thanks.
(I did typedef for struct student* as punt)
void swap_node(punt node1, punt node2)
{
node1->next=node2->next;
node2->next=node1;
node2->prev=node1->prev;
node1->prev=node2;
(node2->prev)->next=node2;
}
I guess this would be sufficient, basically the only difference from your code is the last statement (did not include null checks for simplicity):
void swap_node(punt node1, punt node2)
{
node1->next=node2->next;
node2->next=node1;
(node1->prev)->next=node2;
node2->prev=node1->prev;
node1->prev=node2;
(node1->next)->prev=node1;
}
When possible, two nodes are more easily swapped by only swapping their contents. The result is identical. This would pose a problem if somewhere else in your code you held a pointer to one of the nodes you swapped and are still expecting it to be the same node.
Anyway, back to your code. There are a few things that need refinement. Firstly, we need to check if node1 and node2 are not null and if they aren't we can proceed, otherwise exit the function as we can't swap this. The first 4 lines of your swapping code are alright, but here's two issues:
Node that was before node1 (prior to swapping) might be null, and thus accessing it's next pointer to point at node2 would result in undefined behaviour and likely crash. Check if it exists and then perform the assignment.
You forgot to check if there exists a node after node2 (prior to swapping) and if so, point it's prev pointer at node1
It usually helps to visualize the links in a doubly linked list, just draw it on a piece of paper. You'll notice 4 links per node:
node->next
node->next->prev
node->prev
node->prev->next
So to swap two unrelated nodes you need to swap the 4 links.
The following will not work:
node1->next=node2->next;
This overwrites the old value node1-> next. You need to use a temporary variable instead. Something like this:
#define SWAP_PTR(a,b) { void* tmp = b; b = a; a = tmp; }
void swap_node(punt node1, punt node2)
{
SWAP_PTR(node1->next, node2->next);
SWAP_PTR(node1->next->prev, node2->next->prev);
SWAP_PTR(node1->prev, node2->prev);
SWAP_PTR(node1->prev->next, node2->prev->next);
}
Note - in a threaded environment you'll need some form of locking.

Does loop condition work even it gets zero while using pointer based condition?

As far my knowledge goes, a for loop has this structure "for(init; condition; increment)" and goes on till the condition is not zero or true. Now, I came across a code like this:
struct node
{
int no;
struct node *next;
};
struct node *first;
void creatlist()
{
char ch='y';
struct node *ptr,*nw;
while(ch!='n')
{
printf("\nEnter item in list");
nw=(struct node*)malloc(sizeof(struct node));
scanf("%d",&nw->no);
nw->next=0;
if(first==0)
{
first=nw;
}
else
{
for(ptr=first ;ptr->next!=0;ptr=ptr->next);
{
ptr->next=nw;
}
}
printf("\nDo you want to countinue y\n");
ch=getch();
}
}
I am having some understanding problem with the for loop with "ptr->next!=0" condition as the loop is going on even though "ptr->next" is zero every time, but according to what I know, it shouldn't be. Can any one help me to find the explanation?
as the loop is going on even though "ptr->next" is zero everytime,
Not really. It's zero only for the last element of the list. That for loop walks the list as long as the current element has a valid next, so when the loop terminates ptr points to the last element.
At this point, we have
ptr->next=nw;
which changes the next pointer of the last node (currently 0) to point to the newly created node.
By the way, notice that this line is confusingly indented - it's into a block as if it was the loop body, but it's not - the for loop has a semicolon after it, i.e. an empty statement, which is the loop body (IOW, you have a for loop with an empty body, as all it has to do is already done in the increment section).
The ptr->next=nw; is in a block for no good reason, and it's completely independent from the loop body. That's probably the cause of your confusion if you printed the next value adding some printf here.

What is wrong with my sorting function

I am having a hard time understanding why this sort function is not working. It does put nodes into sorted order, but the first node is always lost in the process. Here is my code (the head variable is global):
void medianscore() {
int i, j=0;
int counter=0;
struct student *curr=head;
struct student *trail=head;
struct student *temp=NULL;
while (curr !=NULL)
{
curr=curr->next; //couting the number of items I have in my list.
counter++; //this works fine.
}
curr=head->next; // reseting the curr value for the 2nd position.
for (i=0; i<counter; i++)
{
while (curr != NULL)
{
if (trail->grade > curr->grade)
{
temp=curr->next; //bubble sort for the pointers.
curr->next=trail;
trail->next=temp;
temp=curr; //reseting trail and curr. curr gets back to be infront.
curr=trail;
trail=temp;
if (j==0) //i'm using j to determine the start of the loop so i won't loose the head pointer.
{
head=trail;
}
}
j++;
trail=curr;
curr=curr->next; //traversing thru the list. nested loop.
}
trail=head;
curr=trail->next;
curr->next=trail->next->next; //traversing thru the list. outer loop.
j=0;
}
}
What am I doing wrong?
Your node counting and use of variables i and j to control the sort has bad code smell. These should not be necessary.
I'll address j and your head-of-list handling later. With respect to the outer loop over i, you could instead maintain a flag for each iteration of the outer loop, so as to determine at the end of that iteration whether any swaps were made. If not, then there is no need to perform another iteration. This approach will never perform more iterations than your present technique performs, and for most inputs it will perform fewer.
The main problem I see in your code, however, is that it does not perform node swaps correctly. Suppose you start with this situation ...
prev --> trail --> curr --> Z
.... You ask the question whether trail and curr must be swapped, and suppose you determine that they must be. In that case you update trail's next pointer and curr's, but not prev's, ending up with links looking like this:
prev ----V
trail --> Z
curr ----^
At that point, curr has effectively been lost -- the chain of links from the list head to Z no longer passes through it, and nothing else you do fixes that.
I suppose you may have ended up with this problem in part because your head node is a special case, in that nothing links to it. But that does not have to be. Without changing anything about how the linked list is used elsewhere in the code, you can create a dummy node local to medianscore() that points to the head:
struct student dummy;
dummy->next = head;
You can then start each iteration of the outer loop with ...
trail = &dummy;
... and in the inner loop test whether trail->next should be swapped with trail->next->next. Your loop termination condition and swaps would need to be a little different, of course, but you need always to have a pointer to the node preceding the first of the pair to swap, as this provides, so that you can update its next pointer.
And doing it that way has another bonus: the head node no longer needs any special treatment within the sort loop. Instead, at the end you simply set head = dummy->next;.

How to enumerate a circular linked list using a for() expression?

I've frequently used a linear linked list construct in C
typedef struct _node {
...node guts...
struct _node *next
} node;
And the enumeration idiom
for (node *each = headNode; each != NULL; each = each->next)
I'm in a situation right now, where a circular list is attractive to me (e.g. the next of the last node is set to the headNode). Naively, I thought I'd using something similar to the for expression there, and the more I've stared at it, I think I've convinced myself you can't do something like that with a circular linked list.
It seems that no matter what kind of expression I come up with for the end conditional, I'll have the basic problem that I want said conditional to evaluate true the first time and false the second time the same node is encountered. I could do something with a loop side effect:
for (BOOL traversed = FALSE, node *each = headNode;
traversed && each != headNode;
traversed = TRUE, each = each->next)
But that definitely loses the elegance/simplicity of the null terminated list approach. Is there some trick of logic that's eluding me this late in the day?
Obviously I can use a while() construct, and maybe that's the only way to do it.
Assuming the following:
An empty list means NULL for the starting point.
The last node will be the one who's next pointer references your starting point, including a single-node list who's next pointer self-references.
NO next pointers are NULL.
Then the following will do what you want using a tertiary expression as the incremental step.
// node* start comes from "somewhere'
for (node *p=start; p; p = (p->next==start ? NULL : p->next))
{
// do something with p
}
Note: p will be NULL when this exits, one way or another; start will remain unchanged, and a NULL start is acceptable, as is a single-self-referencing node.
That said, I'd do this with a while-loop, but since you specifically asked for a for-loop you gets that =P.
As you've already pointed out, any "current" pointer will simply cycle through the whole list, being no different on its second time around than the first.
Therefore you must use some other piece of information. As the simplest/smallest additional variable, a boolean, is too complex or inelegant by your admission, it can be inferred that this can't be done.
...unless you're already making use of some additional data, say a "prev" pointer that's initially null (in which case something very similar to your traversed example can be used).
For the record, I'd use a do...while:
struct _node *node = head;
do {
...
node = node->next;
} while (node != head);
I suggest not just to loop a chain. It is not beautiful itself. How do you check you don't loop at the second element of the chain and so on?
If we have a notion of a head then we should hold a pointer to a head in each node or have it static. Also, instead of looping to the head, we should have special flag.
p = (pointer to some node)
pfirst = p
while true:
do whatever with node (p)...
p = p->next
if (p==pfirst) break
Note that this works for a one element list.
The following re-arranged loop doesn't look too terrible:
for (node * n = head; ; )
{
// process n
if ((n = n->next) == head) { break; }
}
By the way, this only works for non-empty lists. Whatever your notion of emptiness is should be added as an initial check — unless you avoid that problem by having dummy head node (so that the empty list contains one node n with n == head == n.next, and in that case, you don't actually want to print the dummy at all and can use a genuine for loop (just put the above condition back into the loop statement)!

Why does this piece of C code end up in an infinite loop?

I have been trying to fix the infinite loop in this code. However, I could not understand why an infinite loop occurs. This code is trying to sort jobs from the smallest to highest before being processed.
SortJobs()
{
linked_list ptr, h, temp, pptr;
int i, j;
pptr = ready_queue;
ptr = ready_queue->next;
h= ready_queue;
while(ptr != NULL) {
if ((ready_queue->pcb.job_length - ready_queue->pcb.run_time) > (ptr->pcb.job_length - ptr->pcb.run_time)) {
ready_queue = ptr;
pptr->next = ptr->next;
ptr->next = h->next;
h->next = pptr->next;
pptr->next = h;
ptr=h->next;
h=ready_queue;
pptr=ptr->next;
} else {
pptr = ptr;
ptr=ptr->next;
}
}
}
gdb is your friend for debugging such issues. Please start using debuggers!
OTOH, is this a circular-(linked)-list?!
TIP: before running SortJobs(), can you run through your ready_queue and print all the elements and see whether it goes in an infinite loop?!
The reason for an infinite loop could be because you haven't set the last node in your linked-list to NULL. You can check your addNode() function.
Because ptr always is not null??
The problem that stands out is that this line:
pptr=ptr->next;
can sometimes be followed by this line:
pptr->next = ptr->next;
with no changes to pptr or ptr in between. This will result in a little one-node circular linked list, with ptr->next->next == ptr->next. So you can never get out of the linked list.
Overall, I find your algorithm very confusing. You really need to decide on a single logical meaning for each variable, and come up with appropriate loop invariants. For example, at the end of a loop iteration, you'll sometimes have ptr == pptr->next (which, I think, is correct), but sometimes pptr == ptr->next (which I'm pretty sure is wrong, for the reason given above).
I find your algorithm very confusing and I think it is also wrong. Supposing the loop ends somehow and you did everything right the result you'll get is that the first element is the one with the minimum job_length - run_time, but the rest of the list won't be ordered.
As ruakh pointed out, the problem is that you mess up your list when fiddling with all the next pointer, you're overcomplicating things! I wouldn't touch the structure of the list itself moving entire nodes around, rather use memcpy and move only the data carried by the nodes. Here's a sample funcion:
// I assume your linked list is made of nodes such as this
typedef struct {
struct Node next;
struct Node prev; // optional
struct Somewhat pcb;
} Node;
void swapData(Node *n1, Node *n2)
{
struct pcb temp;
memcpy(&temp, n1->pcb, sizeof(struct Somewhat));
memcpy(n1->pcb, n2->pcb, sizeof(struct Somewhat));
memcpy(n2->pcb, &temp, sizeof(struct Somewhat));
}
Now that we're able to swap correctly nodes, I'd use some well-tested/well-known sorting algorithm, this way you'll find help easier and the next one who will look at your code won't be tempted to kill himself (no offence intended, I'm just kidding ;) ). Let me suggest some simple algorithm such as the Selection sort or the Bubble sort. Not very fast but easy to implement :)

Resources