Editing a node in a Linked list Part2 - c

Related to my previous post Editing a node in a Linked list. I've done the following steps in editing the node:
Edit target node data
Remove target node
Re-insert target node
THE PROBLEM IS THAT I CANNOT RE-INSERT IT AT THE TOP OF THE NODE as follows....
std1 90 -> std 2 50 -> std3 20 -> NULL
I edited std3 to 100. The result will be like this
std2 50 -> std3 20 -> NULL
In short, i cannot put it back on the top node. Re-inserting anywhere other than the top node works fine.

You'll have an issue if the head node is a 97%, and you pass a node with a 97%. You need to say
while (curr_std != NULL && to_add->grade <= curr_std->grade){
You'll also have an issue if the student you're editing is the first node, because this:
while((cur != NULL) && (strcmp(cur->name,temp) != 0)){
will evaluate to true, and
prev = cur;
will never get called, leaving prev = null. Then when you get to
prev->next = cur->next;
you have a null reference. You need to explicitly test for adding to the head node; it's its own special case.
scanf("%d", &(cur->grade));
if (prev == null) { // you matched the head
head = cur->next;
}
else {
prev->next = cur->next;
}
EDIT
When you add to the head, in your code, you haven't set the head to point to your new node. You're returning the old head, which now points to the second node in the list. Try:
while (curr_std != NULL && to_add->grade < curr_std->grade){
prev_std = curr_std;
curr_std = curr_std->next;
}
// if you're adding to the head, you didn't go into the above loop
// curr_std is still pointing to head in this case
if (curr_std == head) {
head = to_add
}
else {
prev_std->next = to_add;
}
to_add->next = curr_std;
return head;

Related

Recursively removing the duplicate elements in a linked list

I was trying to remove the duplicate element in a sorted linked list using recursion concept.
I wanna see how to remove the elements in a sorted linked list. I made a code in which if head->data == head->next->data than head->next should be freed until we get the different element.
Now I have made so many changes I am confused how I am supposed to do it. It is deleting every value that is duplicate and only leaving the one that was only appeared in the entire code only once.
Please also tell me why this code is doing this and also what can wrong with code and if any optimal way possible to do the same thing.
(I am only providing the deleteduplicate function if there is a need to provide the whole code like print the list or insert in the list I will edit it if told).
Thanks.
Node *deleteDuplicates(Node *head) {
if (head == NULL || head->next == NULL) {
return head;
}
if (head->data == head->next->data) {
struct Node *x = head->next;
head->next = head->next->next;
free(x);
return deleteDuplicates(head);
} else
return deleteDuplicates(head->next);
}
Input: 11 11 11 13 13 20
Output: 20
Expected output: 11 13 20
It is deleting every value that is duplicate and only leaving the one value that was only appeared in the entire code only once.
No. It is deleting only duplicate values but you always return pointer to the last node.
if(head==NULL ||head->next==NULL){
return head;
}
You don't need to return the new head, since only duplicates are going to be removed, there is no way head is going to change.
There is no need for recursion in this function. Just iterate in a loop either removing the next element or skipping to the next element:
Node *deleteDuplicates(Node *head) {
if (head != NULL) {
Node *p = head;
while (p->next) {
if (p->next->data == p->data) {
Node *x = p->next;
p->next = x->next;
free(x);
} else {
p = p->next;
}
}
}
return head;
}
You could fix your recursive function, but it should be modified to not return the head node as this prevents tail recursion, therefore requiring a potentially huge amount of stack space. A sufficiently long list would cause a Stack overflow.
Here is a modified recursive function:
void deleteDuplicates(Node *head) {
if (head != NULL && head->next != NULL) {
if (head->data == head->next->data) {
struct Node *x = head->next;
head->next = x->next;
free(x);
deleteDuplicates(head);
} else {
deleteDuplicates(head->next);
}
}
}
The problem in your code is you store the return value of deleteDuplicates into your head pointer, but the function returns the pointer to the last node in the list, not the head node.

Linked List Alphabetical Insertion

I'm trying to insert nodes into a linked list so the strings contained in the data of each node are sorted alphabetically. If a duplicate word is inputted, the "count" integer of a node is incremented instead. I'm given a makeLnode() function that creates a node, a isGreater() function that compares two strings, and a getLnodeValue() function that returns the string with each node.
lnode insertWord(char * word, lnode head) {
/* Your code to insert a word in the linked list goes here */
lnode temp = makeLnode(word);
if(head == NULL) // if head is null, make head=temp
{
head = temp;
}
else if(isGreater(getLnodeValue(temp),getLnodeValue(head)) == -1) // if temp < head, place temp before head
{
temp->next = head;
head = temp;
}
else
{
lnode curr;
for(curr = head; curr; curr = curr->next) // loop through linked list
{
if(isGreater(getLnodeValue(temp),getLnodeValue(curr)) == 0) // if curr = temp, increment curr
{
curr->count++;
break;
}
else if(isGreater(getLnodeValue(temp),getLnodeValue(curr)) == -1) // if temp < curr, place temp before curr
{
temp->next = curr->next;
curr->next = temp;
break;
}
else if(curr->next == NULL) // if we reach the end of the list and temp > all other nodes, place temp at end of list
{
curr->next = temp;
break;
}
}
}
return head;
}
Only some words are incremented and there are multiples of some words. My output is as follows:
1. - 2 - a
2. - 2 - is
3. - 1 - broadcasting
4. - 1 - emergency
5. - 1 - be
6. - 1 - for
7. - 2 - this
8. - 1 - system
9. - 1 - system
10. - 1 - the
11. - 1 - testing
12. - 1 - seconds
13. - 1 - sixty
14. - 1 - next
15. - 1 - the
16. - 1 - test
17. - 1 - only
18. - 1 - test
19. - 1 - well
You say // if temp < curr, place temp before curr but actually is putting it after:
temp->next = curr->next;
curr->next = temp;
As you see your output is not ordered because of that.
There could be an issue with isGreater too, and there are memory leaks as well, but this should be the first thing to fix.
I don't want to refactor the entire code here since it wasn't the question, feel free to ask if there still a problem.
First, you create a node before even checking if you need to create one : in case the word is present in the list, you don't need that new node.
Then, you should browse the list until you find a greater value or you reach the end. Then you insert your node. No need to test the three cases.
For example :
// check for the following base cases : no node, one node, then :
lnode node = head;
while (node->next && (isGreater(word,getLnodeValue(node->next)) == 1))
{
node = node->next;
}
// check if the next node value is the same as your word and if it is, increment its count.
if (isGreater(word,getLnodeValue(node->next)) == 0)
{
node->next->count++;
}
// Else, create a node with the word and insert the new node after the current node :
else
{
lnode new_node = makeLnode(word);
new_node->next = node->next;
node->next = new_node;
}
This code is not complete and is not really good but you can start with that.

Bubble sorting a linked list in C

I have created a linked list of 5 nodes of type:
typedef struct node
{
int i;
struct node* link;
}node;
node* head = NULL;
When printing out, this gives:
4 3 2 1 0
The head pointer is set to point at 4. I have then written a function to bubble sort the linked list as follows:
void sort(void)
{
node* cur = head;
node* next = cur->link;
node* prev = NULL;
while(cur->i > next->i)
{
printf("cur is greater than next\n");
while(prev != head)
{
cur->link = next->link;
next->link = cur;
head = next;
next = cur->link;
prev = head;
}
while(next != NULL)
{
prev->link = next;
cur->link = next->link;
next->link = cur;
prev = next;
next = cur->link;
}
printf("second while loop exited\n");
for (node* ptr = head; ptr != NULL; ptr = ptr->link)
{
printf("%d", ptr->i);
}
cur = head;
next = cur->link;
}
}
There are various printf statements to check that the program is working. What I find is that after the first run-through, 4 is successfully bubbled up as follows:
3 2 1 0 4
However, after re-setting the cur pointer to 3 and next to 2, the next run-through provides the following:
2 1 0 4 3
Ultimately, we finish with
0 4 3 2 1
So as can be seen "3", "2" and "1" are being bubbled up too far. I have tried various conditonals in place of the third while loop to correct this but in the majority of cases this leads to seg faults. Of course, the other thing here is that my logic could be completely wrong and there may be a better way to implement this. Could you get away with just swapping the contents of nodes and not pointers themselves? Any help would be much appreciated. Thanks in advance
Ordinary bubble-sort implementations for sorting arrays make use of the direct addressing and a known size of the array: they naturally use indices, that is ordinal numbers of items, so they can easily shrink the area sorted as the work progresses, because they know how many items are already on their final places.
A linked list is processed purely sequentially, so it doesn't allow such simple optimization without adding artificial 'index', incremented along the list iteration. That's why it's easiest to iterate always through the whole list and terminate when no more items were swapped, hence the list is sorted:
void sort(void)
{
int swapped = 1;
while(swapped)
{
node **prev = &head, *curr, *next;
swapped = 0;
for(curr = head; curr; prev = & curr->link, curr = curr->link)
{
next = curr->link;
if(next && curr->i > next->i)
{
curr->link = next->link;
next->link = curr;
*prev = next;
swapped = 1;
}
}
}
}
EDIT – some explanations in reply to questions in Matthew2015 comments.
Logical conditions in C expect a numeric or pointer expression which are considered 'true' if they are different from zero or different from NULL, respectively. That means while(swapped) is essentially equivalent to while(swapped != 0) and next && ... is equivalent to next != NULL && .... The condition in while(swapped != 0) means the loop will terminate when some execution of internal for does not set swapped to 1, which happens when no item in the list is greater than its successor – that is, when the list is sorted.
The for loop condition expression is curr alone, equivalent to curr != NULL. That makes the for loop iterate along the list until there is no 'current' node.
The node **prev variable points to a pointer, which points to the current node. When the 'current' and the 'next' node need to be swapped, then the 'previous' link should no longer point to the 'current' node but to the 'next' node instead. Of course one might keep the pointer to the 'previous node' and assign a new value to the (previous node)->link — but that would not work in case of the first node in a list, which has no 'previous node' but is pointed to by the head variable. One must use additional condition to verify if the current node is the first node to resolve this inconsistency. Having a pointer to pointer, which originally points to head and then to 'previous node'.link makes the whole code much simpler, shorter and also a bit faster.
I would look at the third while
while(next != NULL)
{
prev->link = next;
cur->link = next->link;
next->link = cur;
prev = next;
next = cur->link;
}
Here you're always moving elements without testing whether they have to be moved - i.e. cur->i > next->i.
By the way, if it's guard is true, the second while gets executed only once and so it's the same as an if so I would use an if, at least for clarity reasons.

Segmentation fault deleting nodes from singly linked list

this is the case i am working on
[11] -> [12] -> [13] -> NULL
I am trying to delete the elements from the liked list above(example) but I keep getting segfault and on running GDB doesnot help much. I am not looking for an answer but and explanation on where I am going wrong logically.
here is the code
int
List:: remove( int val )
{
ListNode *headNode = _head;
ListNode *tempNode = NULL;
if(headNode->_value == val){
tempNode = headNode->_next;
delete headNode;
_head = tempNode;
}
else
{
while(headNode->_value != val){
tempNode = headNode;
headNode = headNode->_next;
}
tempNode->_next = headNode->_next;
delete headNode;
}
}
You're not accounting for the following conditions:
The list may be empty; i.e. _head is NULL;
The value may not be in the list at all.
Your function is declared to return int, but makes no such return
Assuming the rest of your code is correct (and that is a big assumption), I'm all-but-certain this is what you're trying to do:
void List::remove( int val )
{
ListNode *headNode = _head;
ListNode *tempNode = NULL;
while (headNode && headNode->_value != val)
{
tempNode = headNode;
headNode = headNode->next;
}
if (headNode)
{
if (tempNode)
tempNode->next = headNode->next;
else
_head = headNode->next;
delete headNode;
}
}
Alternatively, if so inclined this can get (arguably) simpler utilizing a pointer-to-pointer to traverse the pointers in the list, not just their values. It is worth investigating how the following works, which still covers all the bases described previously, but does so using the actual pointers in the list nodes themselves, including _head, by-address rather than by-value, thereby eliminating the need for a walk-behind temporary pointer:
void List::remove( int val )
{
ListNode **pp = &_head;
while (*pp && (*pp)->_value != val)
pp = &(*pp)->next;
if (*pp)
{
ListNode *p = *pp;
*pp = p->next;
delete p;
}
}
In your remove method you are assuming there are always elements in your list. - What if it is empty?
What if the value isn't in the list? You need to handle this case as well.
You're headed in the right direction - there are just a few cases that you haven't considered that can lead you to seg fault.
Example of forward traversal with deletion (forward-only linked list):
// Start from the beginning (head), then while the current isn't null,
// move to the next node.
for (ListNode* current = head; current != null; current = current->next) {
// Check the next item if there is one, and remove it if it matches the value.
// We check the next one because you can't delete the current node in a
// forward only linked list (can in a doubly-linked list however)
if (current->_next != nullptr && current->_value == value) {
// Make this item point to the next next item
// (Since we're gonna delete the next item)
current->_next = current->_next->next;
// Delete the next item.
delete current->_next;
}
}

How to free only a single node in a linked list in C?

How can I free only a single node in a linked list? The following frees the whole linked list but I wanted to free only one node in the linked list.
//Here's my code for delete
while(headPtr!=NULL)
{
temp = headPtr;
headPtr = headPtr->next;
if(strcmp(temp->fname, stdfname) ==0 &&
strcmp(temp->sname, stdsname) ==0 )
{
free(temp);
}
}
You first need to know the previous node. Because of that, you need to iterate until you hit the node you want to delete. In that process you need to remember the previous node. Then you need to connection the previous and next nodes, thus "delinking" the node you want to delete.
currentNode = headNode;
previousNode = NULL;
while (currentNode != NULL) {
if (currentNode != nodeToDelete) {
// Not the node we want to delete yet,
// go on to next node.
previousNode = currentNode;
currentNode = currentNode->next;
continue;
}
// We've now hit the node to delete and know the
// previous node. Fix the structure.
if (previousNode) {
previousNode->next = nodeToDelete->next;
} else {
// No previous node means it's the head node.
headNode = nodeToDelete->next;
}
// The node is now delinked from list. Delete it.
free(nodeToDelete);
// Stop the loop.
break;
}
This is pretty bad performance-wise, which is why there are double-linked lists. There, the whole operations looks like this:
if (nodeToDelete->previous) {
nodeToDelete->previous->next = nodeToDelete->next;
}
if (nodeToDelete->next) {
nodeToDelete->next->previous = nodeToDelete->previous;
}
if (nodeToDelete == headNode) {
headNode = nodeToDelete->next;
}
free(nodeToDelete);
As you can see, no iteration is necessary here as each node knows its previous and next nodes.
BTW, to work these things out (they are pretty basic) it helps to draw a short linked list on a piece of paper. Draw boxes, in each box write the member names (like previous and next) and draw lines from these members to the corresponding other boxes. Then think about what is necessary to do in order to delete the node. It really helps you understand how this works.
The head of the list should never change unless the node you're deleting is the head. You should be moving the temp pointer down the list. You also need to fix up the links when you delete a node, note that there are three cases you need to be aware of and the case where it's the first node requires special handling. I'll give you the skeleton for the code, but since I don't know whether your list is singly- or doubly-linked, I'll leave the pointer updates to you.
temp = headPtr;
prev = NULL;
while (temp != NULL)
{
if (strcmp(temp->fname, stdfname) == 0)
&& strcmp(temp->sname, stdsname) == 0)
{
if (prev == NULL) { // head node
...
}
else if (temp->next == NULL) { // tail node
...
}
else { // interior node
...
}
break; // stop when done
}
prev = temp;
temp = temp->next;
}

Resources