What is wrong with my sorting function - c

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;.

Related

Change address of pointer to linked list when sorting

This is my program to sort a linked list by Bubble sort. When I use while it is OK, but when I use for loop it have an infinite loop so my program is stop unexpectedly. On the other hand, for the first swap of first and second element, I change pointer head point to the second but maybe it doesn't work for the whole program.
For example,
Ex1:
Input : 3,2,4,1,5
Output: 2,3,4,5
Ex2:
Input: 4,3,2,1,5
Output:3,4,5
Ex3:
Input: 3,2,1
Output: 2,3
I think that it just change the address the head pointer point to for the first time, so head is point to 2 in Ex1, 3 in Ex2 and 2 in Ex3.
void Swap(Node *p1, Node *p2)
{
Node *t;
t=p2->next;
p2->next=p1;
p1->next=t;
}
Node *BubbleSort(Node *head,int n) //pass the address of the first element in linked list and the linked list size
{
Node *tmp=head;
int swap=1;
for(int i=0;i<n-1;i++)
{
tmp=a; swap=0;
for(int j=0;j<n-1;j++)
{
if((tmp->data)>(tmp->next->data))
{
if(j==0) //if this is the first and second element I will change the address of the pointer
a=tmp->next;
Swap(tmp,tmp->next);
swap=1;
}
else tmp=tmp->next; //If the element I focus on is not greater than its folowing I will move the tmp pointer to the next.
}
if(swap==0)
return head;
}
return head;
}
int main()
{
Node*head;
//Assume I have a linked list with n elements
head=BubbleSort(head,n);
}
I searched for an other way in GeekforGeek but I still want to know why my code doesn't work. I have think for almost a long day. Please help me !
The Swap function is off by one. Therefore, in the innermost loop body you need to Swap(tmp->prev, tmp) instead of Swap(tmp, tmp->next).
You need to compare and swap the same elements, not compare the current and next element but swap the next and 2nd next element.
if( (tmp->data) > (tmp->next->data) ), then tmp->prev->next should point to tmp->next and tmp->next->next should point to tmp.
However, there is one thing to take care of: As you Swap(tmp->prev, tmp) you need to start one element further, too. This is also clear from the fact that the head points to the first element but does not contain any data. first->prev should point to the head, the head should be rw.
It's possible to let the top level code maintain the head but it's better if the ll code maintains it's own head.

Interpreting the loop condition of custom circular doubly linked list

I have a list inside a structure,
struct A{
list B;
};
I also have a pointer to this structure like
struct A *a;
Now, assume the list is already implemented, and its elements are of type elem.
Then, I do the following -
(i) I get the head of the list in nodeL (a pointer of elem type)
elem * nodeL = list_get_head(&(a->B));
(ii) I now loop through the list in this way:
while(nodeL != (elem *)(&a->B)){ // did not get this part ----- I
; //some code here
nodeL = list_get_next(&(a->B), nodeL);
}
Assume that list_get_head gets the pointer to head of list, and list_get_next gets the pointer to next element of passed second argument elem, of list.
Now my question is:
What is my loop condition here? Till what of list do I hope to loop? (see I) In other words, if &(a->B) is the address of the start of the list, what is &a->B here?
I thought this should loop till end of list, but it doesn't seem like that's what the while loop condition is doing. Also, this is a circular doubly linked list.
elem* x = list_get_head(&a->B);
elem* y = (elem *)(&a->B);
First at all, how far do x and y differ in your case?
To be valid at all, first member of list must be of type elem* anyway. I personally would assume that this is the head of the list, but then your while loop won't ever be entered, so it must rather be the tail??? But then your first element considered in the loop is the tail...
How is an empty list represented? Null pointer? If so, this is not covered by your code.
while(nodeL != (elem *)(&a->B))
did not get this part
Idea is simple: we start iterating at the head, and as long as the head is not reached again, we are still in the loop... Problem is, though, you have to distinguish two situations:
current node is head at start of the loop
current node is head after all elements have been iterated
I recommend different handling for iterating now:
elem* nodeL = list_get_head(&a->B);
if(nodeL) // based on assumption(!): otherwise, empty list
{
do
{
//some code here
nodeL = list_get_next(&a->B, nodeL);
}
while(nodeL != list_get_head(&a->B));
}
One element is guaranteed to be in the list, so we can use it unconditionally (thus a do-while loop). Then we iterate to the next element, until we reach the beginning again. I replaced the suspicious cast by another call to list_get_head, making the whole matter safer (not relying on assumptions any more either).
It is not clear if you are talking about std::list. But since you are mentioning unfamiliar list_get_head and list_get_next, I assume this is a non standard implementation.
So the guess is: In a list head is also an element. The condition while(nodeL != (elem *)(&A->B)) looks like it is checking when loop traverses back to the head of the circular list.
However, there are two observations:
Elements of a list are generally allocated from heap or a memory pool. Hence, if B is head, it should have been a pointer. Unless it is typedef elem * list.
The loop may not run because nodeL is already at head.
Suppose you have a variable and a pointer of the structure:
A var;
A* pntr;
Now to access a data member of the structure we do the following :
var.B // For a variable use dot
(*pntr).B or pntr->B //For a pointer we can use * . or ->
In your code while(nodeL != (elem *)(&a->B)) iterates over the list till the head of the circular list is encountered again.
We can get head by :
A* a;
//SOME INITIAL CODE
A* pntr;
pntr = a;
head = (elem*)&(pntr->B)
So look at the look like:
while( nodeL != (elem *) ( & ( a->B ) ) )

Linked List Function?

So i'm writing this function that makes the smallest node at the end of the linked list. So Far it works except for the last element. if the linked list is created as nodes containing items from 0~9, the list after this function is {1,2,3,4,5,6,7,8,0,9} and the zero does not become at the end.
Any ideas why?
my function;
int connecting(linkk A)
{
linkk L = A;
for (int i = 0; i<sizeof(L);i++)
{
if (L->item < L->next->item)
{
int a = L->item;
L->item = L->next->item;
L->next->item = a;
L = L->next;
}
else{L=L->next;}
}
return 0;
}
Let's start with what I think you should be doing differently:
The name of your function is connecting. Given the description of what you'd like the function to do, this is not a good name.
I can infer from the usage that linkk is a typedef'ed pointer. Hiding pointers this way is not a good idea, most of the time.
Your function returns an int. It's always 0. Why? This does not make any sense.
Because linkk is probably a pointer to a node, and you pass the head pointer by value (i.e. a copy of it) to the function, you're unable to handle the case where the head of your list is the minimum. Either return the "new" head pointer, or pass a pointer to the head pointer to be able to modify the head pointer.
Your use of sizeof is completely wrong, as already suggested. sizeof(L) will give you the size (in chars) of the pointer, thus probably 8 on a 64 bit system or 4 on a 32 bit system.
You're not changing the nodes, but rather moving the values in between the nodes. This is OK if it's what you want to do, but the description of your algorithm suggest that you want to move the node instead.
Your function is doing too much, IMO. This can be hard to split, but a better approach would be to separate finding / extracting the minimum and putting it at the end of the list.
You're modifying a lot more than you originally wanted to. Consider a list like 1, 2, 3, 0, 4. Using your algorithm, the list will be changed to 2, 3, 1, 4, 0. Doing this is not only bad for the performance, but also very surprising for the caller. Surprises aren't good when it comes to programming!
So, let's get to a hopefully good implementation, step by step:
struct node {
int item;
struct node * next;
};
I assume that you want to move the node containing the minimum value to the end of the list, as in your description. I'm also going to keep this into a single function receiving a struct node * head pointer, despite my point above, in order to keep closer to the original code. Let's get out the special / base cases first: Moving the minimum element of an empty list as well as of a single element list is trivial: Do nothing.
if (head == NULL || head->next == NULL) {
return head;
}
I'm returning the "new" head of the list to allow the caller to update it's own head pointer. (As already said, head is just a copy of the caller's head pointer, modifying it will not have any effect at the call site).
Because we're dealing with a singly linked list here, and the implementation should not unnecessarily iterate over the list, we should remember the node we've previously visited. Otherwise we couldn't easily extract a node from the list:
struct node * follow, * point;
follow follows directly behind the point.
Initially, we place the point to the second node of the list (we already checked that there are at least 2 nodes in the list). follow will thus point to the head:
point = head->next;
follow = head;
Since we want to find the minimum item, we need to keep track of the minimum of the already searched part of the list. We initialize it with the head node's value:
int currentMinimum = head->item;
Now we're ready to iterate over the list, in order to find the node containing the minimum. But we need not only find the node containing the minimum, but also the one before it and the one after it, in order to be able to extract it easily. So, 3 pointers:
struct node * predecessor, * minimum, * successor;
As we set the currentMinimum to heads item, we should also set the pointers accordingly:
predecessor = NULL; // Nothing preceding the head
minimum = head;
successor = head->next;
Now let's iterate, moving the point completely over the list, until it falls off at the end:
while (point != NULL) {
// to be continued
follow = point;
point = point->next;
}
// when we're here, follow will point to the last node
In each iteration, we need to check if we found a smaller value than the current minimum, and eventually remember the node containing it:
if (point->item < currentMinimum) {
predecessor = follow;
minimum = point;
successor = point->next;
currentMinimum = point->item;
}
Now, when we get out of the loop, the following state should be reached:
minimum points to the node containing the minimum.
follow points to the last node of the list.
The two above could be the same, which is a special case!
predecessor could still be NULL, which is another special case!
Considering first the special case of minimum = follow: In that case, the minimum is already at the end of the list, so profit! Otherwise, we need to "cut" the node at minimum out of the list and append it to the last node, pointed to by follow:
if (follow != minimum) {
if (predecessor != NULL) {
predecessor->next = successor; // Cut out
minimum->next = NULL; // will be the last node
follow->next = minimum; // append at end
} else {
// to be continued
}
}
As you can see, there's the second special case to consider: If predecessor is still NULL, then no item was smaller than heads item. (Therefore, we could also test for minimum == head) Thus, the first node of the list will be moved to the end. We need to inform the caller about this!
head = head->next; // Second node is now the first one, though this is not all we need to do, see further down!
minimum->next = NULL; // Will be the last node
follow->next = minimum; // Append at end
Since the assignment to head only changed the function parameter (which is a copy of the pointer with which the function has been called), we need to return the (possibly modified!) head pointer, giving the caller the ability to update its own head pointer:
return head;
A caller would thus use this function like so:
struct node * head = get_a_fancy_list();
head = move_minimum_to_end(head); // This is our function being called!
Finally, a thing to consider: As you can see, moving the node (instead of the item) is more complicated. We need to modify at least 2 pointers in order to achieve what we want. In contrast: Moving the item value requires two modifications of item values (and iterating is easier). Moving the node instead of the item thus makes only sense when pointer assignments are faster than item assignments. Since the items are of type int this is not the case here.
Moving the item instead of the node containing the item is considerably easier. First of all, we need to keep track of the minimum (value as well as node):
struct node * minimum;
int currentMinimum;
To iterate, we're again going to use two pointers. It can be done with a single one, but the code is going to be more readable this way:
struct node * point, * follow;
We start off with the same initial state:
minimum = head;
currentMinimum = head->item;
follow = head;
point = head->next;
Iterating is similar to the other implementation, as is the iteration step:
while (point != NULL) {
if (point->item < currentMinimum) {
minimum = point;
currentMinimum = point->item;
}
follow = point;
point = point->next;
}
// follow points to the last node now
Now, doing the same as the previous implementation, we can swap the items of the last node and the node with the minimum:
minimum->item = follow->item;
follow->item = currentMinimum; // contains minimum->item
There's no point in checking for follow != minimum like in the previous approach: You could do it, but swapping the item of a node with its own item won't do any harm. OTOH adding an if will add a branch, and thus a possible performance penalty.
Since we didn't change the list structure (the linking between nodes), there's not much more to consider. We don't even need to inform the caller about a new head, as there will never be any change to it. For style purposes, I'd add it either way:
return head;
Ok, this got a bit long now, but hopefully it's easy to understand!
Try this function
int connecting(linkk A)
{
linkk L = A;
while(L->next!=NULL)
{
if (L->item < L->next->item)
{
int a = L->item;
L->item = L->next->item;
L->next->item = a;
}
L=L->next;
}
return 0;
}

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)!

Using loop to traverse through linked list

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.

Resources