Swap adjacent element in a linked list - c

While seeing a programming interview site, I came across code which swap adjacent elements in a linked list, but I found it to be a bit wrong. Below is the code.
void swap (struct list **list1)
{
struct list *cur, *tmp, *next;
cur = *list1;
if (cur && cur->next)
*list1 = cur->next;
//To make sure that we have at least two more elements to be swapped.
while (cur && cur->next)
{
next = cur->next;
tmp = next->next;
next->next = cur;
//We have to make 1->next as 4 in above example (figure).
if (tmp)
cur->next = tmp->next;
cur = tmp;
}
return;
}
Now for me, the condition if (temp) is not right here. Is that assessment correct?
Suppose we do have a linked list like:
1->2->3->4->NULL
Now our objective is to make a linked list like:
2->1->4->3->NULL
My worry is if the if (temp) is there in our code, we can't assign null at end of the linked list.

You are right. This doesn't work. It creates a loop at the end of the list, and if you run swap twice on the same list, the second run will get into an endless loop.
To fix this awkward code, replace the if (tmp) with the following code:
if(tmp)
if (tmp->next)
cur->next = tmp->next;
else
cur->next = tmp; // take care of an add number of nodes
else
cur->next = NULL; // take care of an even number of nodes
It will take care of the last nodes:
If there's an even number of nodes, it makes sure the last points to NULL instead of the node before it.
If there's an odd number of nodes, checking cur->next will prevent the following iteration, so the last node must be pointed by the one before it before the loop is exited.

It tests it to make sure it's not NULL (the last element). Not testing it will make your program follow the NULL pointer for the last element of the list.
tmp = next->next; /* If `next` is the last, `next->next` is NULL. */

Yes, you are right that there's a bug in the function - cur->next isn't updated correctly in all cases.
I personally find the local variable names tmp and next not particularly helpful and actively confusing in the case of next. Those names make it hard for me to keep track in my head of what's going on as I read through the function.
I find that the names node1, node2, and node3 work better to for me to keep a mental picture of which node is being manipulated. I wouldn't be surprised if other people disagree, though.
Here's a reworked version of the function that I find more readable, and more importantly that I believe is correct.
void swap (struct list **head)
{
struct list *node1 = *head;
struct list *node2 = node1 ? node1->next : NULL;
// handle degenerate cases
if (!node2) {
// no elements or only one element on the list
// nothing to do...
return;
}
// fix-up list head to what will be the new first node on list
*head = node2;
// while there are at least 2 more elements to be swapped
while (node1 && node2) {
struct list* node3 = node2->next;
// get a pointer to the node that will be the remainder of the
// list after the remainder of the list is processed.
//
// This will be NULL, node3, or 'node4' depending on whether
// there are 0 , 1 or 2+ nodes following node1 and node2
struct list* remainder = (node3 && node3->next) ? node3->next : node3;
node1->next = remainder;
node2->next = node1;
// prepare pointers to the next two nodes to be swapped
node1 = node3;
node2 = node3 ? node3->next : NULL;
}
return;
}

Java Implementation
Given: 1->2->3->4->5->6
Logic
1. First - 1, Second - 2, Third 3
2. Second.next = first
3. First.next = Third.next depending on even or odd numbers update accordingly
public ListNode evenOddMerge(ListNode head) {
if (head == null || head.next == null) {
return head;
}
ListNode first = head;
ListNode second = first.next;
ListNode third = null;
head = second;
while (true) {
third = second.next;
second.next = first;
if (third == null || third.next == null) {
first.next = third;
break;
}
first.next = third.next;
first = third;
second = first.next;
}
return head;
}
Credits: Geeks for Geeks

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.

Inserting node into sorted linked list in C

I need to insert a node into a sorted linked list. The list has a dummy node.
void add(Node **nodeArray, int setNumber) {
char userString[5];
Node *head = nodeArray[setNumber]; /* head pointer to first element of array (dummy) */
Node *newNode = malloc(sizeof(Node)); /* new node to be added to array */
Node *tmp = head;
printf("Please enter some data: ");
scanf("%s", userString);
strncpy(newNode->data, userString, DATA_SIZE); /* copies string entered by the user to data field of new node */
newNode->next = NULL; /* initializes next field of new node to NULL */
while (tmp->next)
tmp = tmp->next; /* points head to next element in list */
tmp->next = newNode; /* adds element to list */
}
This inserts a node at the end of the list. I understand the logic of the added sort. You look ahead to the next node and if the new node is less than the next, you point the new node to the next node and the previous node to the new node. Can someone help me implement this in code. This is what I have so far, but does not work.
if (!tmp->next)
tmp->next = newNode;
else {
while (tmp->next) {
if (strcmpa((tmp->next)->data, newNode->data) < 0) {
newNode->next = tmp->next;
tmp->next = newNode;
} //else if (strcmpa((tmp->next)->data, newNode->data) > 0) {
//tmp->next = newNode;
//}
tmp = tmp->next;
}
}
Here is the compare function:
int strcmpa(char *s1, char *s2) {
while (*s1 && tolower(*s1) == tolower(*s2))
{
s1++;
s2++;
}
return tolower(*s1) - tolower(*s2);
}
You're almost there but tmp->next should be redirected to newNode itself, not to newNode->next. Actually, that's a no-operation, because at the time of that command, newNode->next is what tmp->next was and you're reassigning a value that's already there.
What you want is that tmp, having pointed to X, points to next instead, which then only points to X. So, formally,
X = tmp->next;
tmp->next = newNode;
newNode->next = X;
which can be condensed to
newNode->next = tmp->next; /* using the X where originally stored */
tmp->next = newNode; /* rewriting that memory has to wait till now */
Update: The above code links newNode after tmp (as I thought that was what you needed). But in the sorted list, you find the first tmp that needs to appear after your node, so you need to go before that. This brings two problems:
You need to modify what the item before tmp points to, because the new node will go there, so you need to keep one more variable,
sometimes you'll need to put the new node in the very beginning, which has no previous node.
On the plus side, the above also handles !tmp->next (insertion at the end) gracefully so you don't really need another branch for that case.
It will look something like
Node* head = (whatever);
Node* prev = NULL;
Node* tmp;
for(tmp = head; tmp && strcmpa(tmp->data, newNode->data) < 0; tmp = tmp->next) {
prev = tmp;
}
/* now tmp points to the first larger node or to NULL and prev is its preceding node.
* If there's none (if the head is already larger) then prev = NULL. */
if(prev == NULL) {
newNode->next = head;
head = newNode; /* Don't forget to update this in the array where you took head from! */
} else {
newNode->next = prev->next;
prev->next = newNode;
}
This works also if head was NULL and creates a linked list with a single entry.
This focused on the insertion. Regarding the rest of your code (originally posted as comments):
The program will fail if you overrun the userString buffer on the unrestricted %s scanf. Set a limit.
With a limit, you don't need the unsafe strncpy. Use a normal strcpy that guarantees it will null-terminate your string.
Your strcmpa looks fine. But do make sure tolower() does what you expect it to with an input of '\0'. This compares the strings correctly:
char toLower(char in) {
if(in >= 'A' && in <= 'Z')
return in + 'a' - 'A';
else
return in;
}

Assigning nodes (reversing linked list)

I want to write both an iterative and recursive way to reverse a linked list.
Unfortunately, in both cases, I'm running into a similar problem: I am unable to change one node's pointer to a different node, and I'm struggling in some cases with iterating down the list. For example, here's my recursive reverse function:
node *reverse(node *initial){
node *prev = initial;
node *nextNode;
nextNode = (node *)malloc(sizeof(struct node));
nextNode = initial->next;
if(nextNode->next == NULL){
return prev;
}
else{
nextNode = reverse(nextNode);
nextNode->next = prev;
}
}
The line nextNode = initial->next; crashes the program. I'm sure there's plenty of other issues with this code, and while I'm open to suggestions if its fatally flawed, I mostly just want to resolve this error so that I can debug the rest on my own. In the iterative version, some of the similar lines that crash the program are:
startA = startA->next; // startA is a node pointer
backNode = startB; // backNode and startB are both node pointers
backNode->data = frontNode->data; //both ints
frontNode->data = temp; //again both ints
By request, the rest of the code:
main(){
node * start = buildList();
int i;
int nodeSize = sizeof(struct node);
reverse(start);
}
And buildList:
node *buildList(){
node *head = NULL;
node *second = NULL;
node *third = NULL;
node *fourth = NULL;
node *fifth = NULL;
head = (node *)malloc(sizeof(struct node));
second = (node *)malloc(sizeof(struct node));
third = (node *)malloc(sizeof(struct node));
fourth = (node *)malloc(sizeof(struct node));
fifth = (node *)malloc(sizeof(struct node));
head->data = 1;
head->next = second;
second->data =2;
second->next = third;
third->data = 3;
third->next = fourth;
fourth->data =4;
fourth->next = fifth;
fifth->data = 5;
fifth->next = NULL;
return head;
}
Note that when you dereference nextNode->next in your if statement, you haven't checked for nextNode == NULL.
Essentially you're doing:
if (initial->next->next == NULL)
What happens here if initial->next == NULL? This is also an issue with your recursion base-case.
Furthermore, your malloc is wasted and will cause a memory leak: you assign to nextNode a new memory block, then lose the reference to that block when you assign something else to nextNode in the very next line: nextNode = initial->next; A malloc is unnecessary here: you're not adding new nodes to your list, only rearranging the nodes that you have.
When implementing recursion, carefully consider your base-case. With your code you want to recurse to traverse your list to its last node, then use return to build the list again, backwards. How do you know when you're at the last node in the list? This is your base-case, and your recursion function should start there. Can you determine this using only your function argument?
This isn't so different from your current code, but the code you've posted contains a number of mistakes.
Here is a quick walkthrough for you:
node *reverse(node *initial){
if (initial is NULL)
/* this is an empty list so return */
return a null pointer;
if (initial->next is NULL)
/* this is the recursion base case under normal operation - one elem left */
return initial;
node *prev = initial;
node *nextNode = initial->next;
/* reverse the rest of the list starting at the next node */
nextNode = reverse(nextNode);
/* now just reverse the pointers */
initial->next->next = prev;
/*
* but remember that prev->next still points to the wrong node,
* we need to clear that
*/
prev->next = NULL;
/* you were also missing the return case here */
/* we want to keep track of the last element (the new head element) */
/* keep passing this back up through the recursive steps */
return nextNode;
}

Printing contents of linked list only prints first node

This is the updated version of the code. i am trying to add some info in a link list every time a client sends a message to the server (it can be multiple clients). Every time a new message arrives, the function checks its time so it could add it to the list before or after the previous node depending on its current time. The server must print the messages in order of arrival. If there are equal time stamps then it should move to sort the equal ones in using the ids of the servers.
Here is my struct for the list:
'typedef struct trade_list {
char* trader_msg;
u_int32_t id_of_sender;
int sender_timer;
int local_time;
struct trade_list *next;
}trade_list;
trade_list *head = NULL;
the following is the function that inserts to list and sorts according to time:
void add_transaction (char* received_msg_IN, int curr_time_IN, u_int32_t my_id_IN, int elapsedIn)
{
/* Find the node with the smallest time >= curr_time_IN. 'found' starts as NULL, then
is always the node before 'cur'. 'cur' moves through the list until its time is
less than 'curr_time_IN'. So at the end, 'cur' is the first node that's too
far in, and 'found' is either NULL or the node we insert after. */
trade_list *newnode, *cur, *found;
found = NULL;
for (cur = head; cur && cur->sender_timer <= curr_time_IN; cur = cur->next)
found = cur;
if (found) {
/* If 'found' isn't NULL, we're inserting after it*/
/* Times match: Sort out in another way*/
} else {
newnode = malloc(sizeof(*newnode));
newnode->trader_msg = malloc(strlen(received_msg_IN)*sizeof(received_msg_IN));
strcpy(newnode->trader_msg,received_msg_IN);
newnode->sender_timer = curr_time_IN;
newnode->id_of_sender = my_id_IN;
newnode->local_time = elapsedIn;
newnode->next = found->next;
found->next = newnode;
}
} else {
/* No node with more recent time found -- inserting at 'head' */
newnode = malloc(sizeof(*newnode));
newnode->trader_msg = malloc(strlen(received_msg_IN)*sizeof(received_msg_IN));
strcpy(newnode->trader_msg,received_msg_IN);
newnode->sender_timer = curr_time_IN;
newnode->id_of_sender = my_id_IN;
newnode->local_time = elapsedIn;
newnode->next = head;
head = newnode;
}
EDITED AFTER NEW PROBLEM
I managed to sort the list using a sorting method instead later on. So now my list gets sorted just fine. It prints just fine swell, the new problem that arose now is that I want to delete the current node after I print it. So after it gets print it gets deleted. I used the following function but my app crashes.
void deletefirst (struct trade_list *head) {
struct trade_list *tmp = *head;
if (tmp == NULL) return;
*head = tmp->next;
free (tmp);
}
i call this function from my print function:
void print_trades()
{
trade_list * newnode = head;
while (newnode) {
if ((elapsed - newnode->local_time >= 8))
{
printf ("%s\n", newnode->trader_msg);
newnode = newnode->next;
deletefirst(newnode);
}
}
}
How would I delete the current node and move on?
The way you print the list should work fine, as long as there is at least one node. I'd recommend changing the do { ... } while() to just a while() { ... } though, so that it still works when the list is empty and head is NULL:
trade_list *currentnode = head;
while (currentnode) {
printf ("Trade: %s Time: %d ID: %d\n",
currentnode->trader_msg,
currentnode->sender_timer,
currentnode->id_of_sender);
currentnode = currentnode->next;
}
There are some problems with the way you add to the list, though. When head is not null, you're removing its link to the rest of your list:
if (head == NULL)
{
... /* skipped for brevity */
}
else
{
currentnode->next = NULL;
head = currentnode;
}
Since at this point currentnode == head, you're pointing head->next to NULL (if there were another node, you'd be throwing it away) and then assigning head to itself.
Your insertion code in general doesn't look right. If you want to insert only at the beginning of the list, you just need something like:
trade_list *newnode = malloc(sizeof(trade_list));
/* *** Fill out newnode's fields here *** */
newnode->next = head;
head = newnode;
If you want to insert after an arbitrary node, you have to find it first by walking the list, then do something like this (keeping later times at the head of the list):
trade_list *newnode, *cur, *found;
/* Find the node with the smallest time >= curr_time_IN. 'found' starts as NULL, then
is always the node before 'cur'. 'cur' moves through the list until its time is
less than 'curr_time_IN'. So at the end, 'cur' is the first node that's too far in,
and 'found' is either NULL or the node we insert after. */
found = NULL;
for (cur = head; cur && cur->sender_timer >= curr_time_IN; cur = cur->next)
found = cur;
if (found) {
/* If 'found' isn't NULL, we're inserting after it (or skipping if the times match,
since that seems to be what the original code was trying to do) */
if (found->sender_timer == curr_time_IN) {
/* Times match: skip it */
printf("SKIPPED\n");
} else {
/* inserting after 'found' */
newnode = malloc(sizeof(*newnode));
/* *** Fill out newnode's fields here *** */
newnode->next = found->next;
found->next = newnode;
}
} else {
/* No node with more recent time found -- inserting at 'head' */
newnode = malloc(sizeof(*newnode));
/* *** Fill out newnode's fields here *** */
newnode->next = head;
head = newnode;
}
Edited after comment:
To change the list to sort descending by time, then ascending by ID when the times match, just a couple of changes are needed.
Edit
Since the original for loop to find the node continues to the last node that meets the criteria, we just have to add a test into the loop to break when we're at the right one... that is, to break if the next node has equal time and a higher ID, since in that case we want to insert before it. (the earlier edit was flawed... sorry about that).
Additionally, the if (found->sender_timer == curr_time_IN) check afterwards is no longer needed, since there's no skipping and the sort by ID was handled by the new for loop.
So that section of code becomes:
/* original search */
for (cur = head; cur && cur->sender_timer >= curr_time_IN; cur = cur->next) {
/* *** added condition for ID sort */
if (cur->sender_timer == curr_time_IN && cur->id_of_sender >= my_id_IN) break;
found = cur;
}
if (found) {
/* If 'found' isn't NULL, we're inserting after it */
/* CHANGED: no need to skip when times are equal */
newnode = malloc(sizeof(*newnode));
/* *** Fill out newnode's fields here *** */
newnode->next = found->next;
found->next = newnode;
} else {
/* No node with more recent time found -- inserting at 'head' */
newnode = malloc(sizeof(*newnode));
/* *** Fill out newnode's fields here *** */
newnode->next = head;
head = newnode;
}
The problem is not with the printing code it is in the addition.
It looks like when adding a new node you allocate the node but does not connect it to the head or any of the other nodes.
You must have a code like this to add to the beginning of the list:
new_node->next = head;
head= new_node;
Also, the logic you used is obscured and you have many code duplications.
Adding to a linked list has only two options: beginning of the list (head) or on any of the other nodes, you do not need all this logic.

swap elements in a linked list without access to it?

if i have a reference to an element in a linked list, how do i swap it with the next element, in c
here's a try,
Node* nRoot, *temp=pNode->next;
nRoot=pNode;
do{
nRoot->next = temp->next;
if(nRoot==pNode) pNode=temp;
temp->next = nRoot;
nRoot=nRoot->next;
}while(nRoot!=NULL)||temp!=NULL);
but it does not work
You can only do this if it double linked list. You need the previous pointer so that you can point it's next to the current's next.
However if you have these then you an do something like this:
Node* next = curr->next;
Nide* prev = curr->prev;
curr->prev = next;
curr->next = next->next;
curr->next->prev = curr;
next->prev = prev;
next->prev->next = next;
next->next = curr;
And the 2 are swapped.
Edit: Of course you can do this with a singly linked list but you do need to know the previous node so that you fix up its next pointer to point to the current's next.
If you have a reference to A and A->next is B, you can do this. I'm assuming they hold a Data* pointer, replace with whatever the data is. Don't actually swap the nodes, just swap the data in the nodes.
void push_forward(Node* curr)
{
Data* currData = curr->data;
curr->data = curr->next->data;
curr->next->data = currData;
}
For the record, I am not a C guy so this might be correct only in algorithm, but not in implementation. I welcome edits, fixes, suggestions, and constructive comments!
If there are no external pointers to the elements that you wish to swap, then you can just swap the data within the list nodes, rather than the nodes themselves, as pointed out in other answers.
If you have external pointers to the list nodes, then you should probably not mess with the node content, unless the rest of your program is find with node contents changing from under its feet.
You will have to swap the nodes, which means that you need to have a pointer to the node that precedes the ones that you need to swap. If you only have the head of the list, then the swap function could be something along these lines:
void swap(Node **list, Node *first) {
Node *i = *list;
Node *p = NULL;
while (i != NULL) {
if (i == first) {
Node *n = i->next;
/* No next node to swap with */
if (n == NULL)
break;
if (p != NULL) {
p->next = n;
} else {
*list = n;
}
i->next = n->next;
n->next = i;
break;
}
p = i;
i = i->next;
}
}

Resources