Trying to understand how to properly setup a function that will delete from a linked list based on a user specified parameter. So I have a global variable entitled list that holds all the dog structs within them. I can get the list to delete the node the user wants removed if there are multiple structs in the linked list, for some reason though if there is only one node or the node I want deleted is the head of the list the function does not remove it. Any direction for this issue would be greatly appreciated.
void remove_one(char* name)
{
struct dog *tempList = list;
struct dog *previous = NULL;
if (tempList == NULL) {
return;
}
while (tempList != NULL) {
if (strcmpi(tempList->name, name) == 0) {
if (previous == NULL) {
tempList = tempList->next;
}
else {
previous->next = tempList->next;
free(tempList);
tempList = previous->next;
}
return;
}
else {
previous = tempList;
tempList = tempList->next;
}
}
return;
}
2 problems here:
if (previous == NULL) {
tempList = tempList->next;
}
First, tempList is a local variable, so you don't really delete the head. Next time you enter in the routine, list is still pointing to the same (valid) value.
Second, you don't free the memory.
correct code:
if (previous == NULL) {
list = tempList->next; // change global variable so head is "tempList->next"
free(tempList); // free templist memory else you get memory leak
}
Related
I'm trying to figure out how to manipulate linked lists. I want to remove the end node of a given linked list and add this to the end of another given linked list.
For some reason I can not get my pointers right, well- at least that is where I think the problem lays.
(This method sits in a while loop, so one list keeps getting smaller while the other one grows.)
void movenode(struct Node **cards,struct Node **column)
{
struct Node *head = NULL;
struct Node *tmp,*head1 = *cards;
struct Node *tmp2,*head2 = *column;
if (*cards == NULL || (*cards)->next == NULL){
return;
}
while (tmp->next != NULL) {
head->next = tmp;
tmp = tmp->next;
}
while (tmp2->next != NULL) {
tmp2 = tmp2->next;
}
head->next = NULL;
tmp2->data = tmp;
tmp2->next = NULL;
*cards = head1;
*column = head2;
}
Hope someone is able to help me better understand this.
For some reason I can not get my pointers right, well.. atleast that is where I think the problem lays.
You're right, but it's a little more than that. For example, after struct Node *head = NULL; nothing modifies the value in head, so every time you do head->next you're accessing memory at "NULL + a small offset" (and will probably crash).
To remove the last entry from a singly linked list, you have to find the entry before the last entry (so that you can modify its next); and to add an entry to a linked list you have to find the last entry (so that you can modify its next). With this in mind we can break it into 5 parts:
Do sanity checks
Find the entry before the last entry in the *cards list
Remove the last entry in the *cards list
Find the last entry in the *column list
Add the (removed) entry to the end of the *columns list
You can implement each of these 5 pieces one at a time (and test them). This is an important part of programming - breaking more complex stuff into simpler easier things.
The resulting code might look something like (untested):
void movenode(struct Node **cards,struct Node **column) {
struct Node *temp = *cards;
struct Node *removed;
// Do sanity checks
if (temp == NULL) {
return;
}
// Find the entry before the last entry in the `*cards` list
if(temp->next != NULL) {
while(temp->next->next != NULL) {
temp = temp->next;
}
}
// Remove the last entry in the `*cards` list
if(temp == NULL) {
// The last entry was the first entry
removed = temp;
*cards = NULL;
} else {
removed = temp->next;
temp->next = NULL;
}
// Find the last entry in the `*column` list
temp = *column;
if(temp != NULL) {
while(temp->next != NULL) {
temp = temp->next;
}
}
// Add the (removed) entry to the end of the `*columns` list
if(temp == NULL) {
// There was no last entry (list was empty)
*column = removed;
} else {
temp->next = removed;
}
}
I'm not entirely sure about the mechanics of your solution so I'll offer a separate implementation and solution.
typedef struct Node {
void *data;
struct Node *next;
}
void poppush(Node *popHead, Node *pushHead) {
Node *pushLastNode = pushHead;
while (pushLastNode->next != NULL) {
pushLastNode = pushLastNode->next;
}
Node *popLastNode = popHead;
while (popLastNode->next != NULL) {
popLastNode = popLastNode->next;
}
Node *popSecondLastNode = popHead;
while (popSecondLastNode->next != popLastNode) {
popSecondLastNode = popSecondLastNode->next;
}
popSecondLastNode->next = NULL;
pushLastNode->next = popLastNode;
}
However, for operations such as these, I would recommend using a doubly-linked list and/or create some functions dedicated to managing the lists.
I am trying to insert a string userId if it doesn't already exist in the linked list. This is my code:
Node *insertLL(Node **ppHead, User user){
Node *pPrecedes;
Node *pNew;
Node *pNext;
Node *pFind;
int pHead;
Fillup fillup; //file with user information
//searches for the user
pFind= searchLL(*ppHead, fillup.szUserId, &pPrecedes);
if(pFind == NULL)
{
return allocateNode(user);
if (strcmp(pPrecedes->user.szUserId, pFind)!= 0) //pFind seems wrong
pNew->pNext = pPrecedes->pNext;
else
pNew->pNext = pPrecedes->pNext;
pPrecedes->pNext = pNew;
}
return pNew;
}
things wrong with the posted code that are certainly not helping
if(pFind == NULL)
{
return allocateNode(user); <<< == nothing will be executed after this line
if (strcmp(pPrecedes->user.szUserId, pFind)!= 0) //pFind seems wrong
pNew->pNext = pPrecedes->pNext;
else
pNew->pNext = pPrecedes->pNext;
pPrecedes->pNext = pNew; <<<<< == should this line be dependant on the else above. The indentation says yes but there are no braces
}
and yes I know, I will get down voted cos this is 'not an answer' - whatever, I will take the hit just to see if I can help out
I have figured out the correct code for this!!!I should have mentioned this but I had another function searchLL that already compared and searched for the user so strcmp was unnecessary. I had to make 2 new if-statements. One where if the string already exists it just returns that string. The other in case the string has to be inserted at the beginning where it doesn't have a preceding string. I also had to open up some space for my new string.
Node *insertLL(Node **ppHead, User user)
{
Node *pPrecedes;
Node *pNew;
Node *pNext;
Node *pFind;
//searches the user //searchLL is a differen't function
pFind= searchLL(*ppHead, user.szUserId, &pPrecedes);
if (pFind!= NULL) //if pFind already exists
return pFind;
pNew=allocateNode(user);//get space for pNew
if(pFind == NULL) //if pFind doesn't exist insert pFind
{
//insert at the beginning
if(pPrecedes == NULL) //if there is no pPrecedes
{
pNew->pNext = *ppHead; //pNew's next will be the head
*ppHead = pNew; //pNew is now the new head
}
//insert in the middle
else
{
pNew->pNext = pPrecedes->pNext;
pPrecedes->pNext = pNew;
}
}
return pNew;
}
So I have a function that adds a node to a linked list but instead of just adding the element at the end of the linked list I am trying to get user input for the spot the user wants to add it in. I then need to add it and shift everything else over without deleting anything else. I am having a lot of trouble with this.
Below I will show my code, its a bit messy but I will explain as best I can.
void InsertGraphicElement(struct RasterGraphic *pA) {
int counter = 1;
int response = 0;
char tempString[256];
struct GraphicElement *newNode = malloc(sizeof(*newNode));
if (newNode == NULL)
return;
newNode->fileName = malloc(256 * sizeof(char));
if (newNode->fileName == NULL)
return;
newNode->pNext = NULL;
printf("Insert a GraphicElement in the RasterGraphic\nPlease enter the GraphicElement filename: ");
scanf("%s", newNode->fileName);
if (pA->GraphicElements == NULL) {
pA->GraphicElements = newNode;
printf("This is the first GraphicElement in the list\n");
} else {
struct GraphicElement *tempHead = pA->GraphicElements;
struct GraphicElement *tempTail = pA->GraphicElements;
while (tempHead->pNext != NULL) {
tempHead = tempHead->pNext;
counter++;
}
tempHead->pNext = newNode;
printf("There are %d GraphicElement(s) in the list. Please specify the position (<= %d) to insert at :", counter, counter);
scanf("%d", &response);
if (response == counter) {
return;
} else {
while (response < counter) {
tempTail = tempTail->pNext;
response++;
}
}
}
return;
}
It is incomplete Ive been messing with the code trying to figure it out but as you can see I am able to add without a problem at the end of the list. What I am having trouble with is if there are 5 elements for example in the list 1,2,3,4,,5 and I add a sixth the list would obviously look like this 1,2,3,4,5,6. What I want to do is take user input such as they want to add the sixth element to maybe spot 3 so the list would look like this 1,2,6,3,4,5. Ive tried a bunch of things a point in the right direction or some help would be greatly appreciated. Thank you,
Below are my struct definitions
struct GraphicElement {
char *fileName;
struct GraphicElement *pNext;
};
struct RasterGraphic {
//int numNodes;
struct GraphicElement *GraphicElements;
};
You currently have the following:
if (response == counter) {
return;
}
else {
while(response < counter){
tempTail = tempTail->pNext;
response++;
}
}
I would revise this to be something like:
if (response > counter+1 || response < 1) { // These are all invalid values
return;
}
else if (response == 1) { // We are replacing the head node
newNode->pNext = tempTail->pNext;
pA->GraphicElements = newNode;
}
else {
while(response-- > 2) { // Responses are 1-indexed
tempTail = tempTail->pNext;
}
newNode->pNext = tempTail->pNext; // IMPORTANT: Update newNode reference FIRST
tempTail->pNext = newNode; // Update tempTail to point to newNode
}
Disclaimer - Not tested in any way
I tried to comment things I thought were important here and there. The key thing to remember here though is that in order to insert something into a singly-linked list, you must update your new node to point to the rest of the linked list BEFORE you update the reference of the node previous to it, or else the last bit of your list will be lost forever and you will have a circular reference to the last node.
As a side note, it looks like your are traversing your entire linked-list twice each time you want to add a new element to it. I might recommend keeping a rolling counter of how many items are in the linked list that you update each time you add something to it to avoid the overhead of recalculating this value if efficiency is something you value.
Hope this helps!
EDIT: You'd have to remove the line that adds the new node to the end of the list higher up in the function for this to work, however. "tempHead->pNext = newNode;"
I've been working on this code for my shell that I'm creating and for some reason it isn't working. I'm implementing a watchuser function that watch's a user when an argument is given (args[1]). However, when a second argument (args[2]) of "off" is given, the user should be deleted from the linked list and should no longer be watched.
struct userList * goList;
goList = userInventory;
do{
if (strcmp(userInventory->username, args[1]) == 0){
printf("%s\n", args[1]);
printf("%s\n",userInventory->username);
struct userList * temp2;
temp2 = userInventory->next;
if (userInventory->next != NULL){
userInventory->next = temp2->next;
userInventory->next->prev = userInventory;
}
free(temp2);
}
goList = goList->next;
}while (goList != userInventory);
My global struct is also as follows:
struct userList{
char * username;
struct userList * prev;
struct userList * next;
}
For reason, this code won't delete the user node from my linked list. The adding works, but this remove function won't and I'm not sure why. The print statements are there just to make sure it's executing the condition, which it is.
If anyone could help me find the reasoning behind my error, I'd greatly appreciate it. Till then, I'll be trying to debug this.
Thanks.
If I understand anything about your code,
Problem 1 (plausible):
goList = userInventory;
do {
...
goList = goList->next;
} while (goList != userInventory);
Is this a circular list? If it's not, the condition in while () isn't going to become true.
Problem 2:
goList = userInventory;
do {
if (strcmp(userInventory->username, args[1]) == 0) {
...
}
goList = goList->next;
} while (goList != userInventory);
Here you keep comparing the string in the head (or tail) of the list instead of comparing the string in the current node, goList. Finding a match can only succeed in the above code, if the match is in the very first node (head/tail) to which userInventory points initially.
Problem 3:
temp2 = userInventory->next;
if (userInventory->next != NULL) {
userInventory->next = temp2->next;
userInventory->next->prev = userInventory;
}
free(temp2);
Let's assume userInventory is already corrected to be goList:
temp2 = goList->next;
if (goList->next != NULL) {
goList->next = temp2->next;
goList->next->prev = goList;
}
free(temp2);
First of all, it's going to free() not the matching node, but the one after it (or maybe even NULL), which is wrong.
Secondly, this piece of code isn't doing proper unlinking and relinking of nodes. What it should be (assuming the list isn't circular):
temp2 = goList;
if (goList->next != NULL) {
goList->next->prev = goList->prev; // now goList->next node points to goList->prev node
}
if (goList->prev != NULL) {
goList->prev->next = goList->next; // now goList->prev node points to goList->next node
}
free(temp2);
Problem 4:
do {
if (strcmp(goList->username, args[1]) == 0) {
temp2 = goList;
if (goList->next != NULL) {
goList->next->prev = goList->prev; // now goList->next node points to goList->prev node
}
if (goList->prev != NULL) {
goList->prev->next = goList->next; // now goList->prev node points to goList->next node
}
free(temp2);
}
goList = goList->next;
} while (...);
If the deletion succeeds, this line is going to access the just freed node and likely crash your program:
goList = goList->next;
So, you need to change the code to something like:
do {
if (strcmp(goList->username, args[1]) == 0) {
temp2 = goList;
if (goList->next != NULL) {
goList->next->prev = goList->prev; // now goList->next node points to goList->prev node
}
if (goList->prev != NULL) {
goList->prev->next = goList->next; // now goList->prev node points to goList->next node
}
goList = goList->next;
free(temp2);
}
else
{
goList = goList->next;
}
} while (...);
Problem 5:
goList = userInventory;
If you delete the list head (or is it tail?) node, you need to update userInventory to point to the next node after it. If you don't, you will lose all access to the list because userInventory will point to freed memory and not to the remaining nodes, if any.
Problem 6 (plausible):
free(temp2);
The above line does not free() the memory behind temp2->username. You want to free() it if it was malloc()ed.
You should really approach problems one step at a time (e.g. first, iterating over a list, then unlinking/relinking nodes, then deleting nodes).
When things aren't clear or aren't working, use paper and a pencil (or a drawing board and a pen or a piece of chalk) to visualize the problem for yourself. Draw the objects, the arrows depicting pointers or some other connections between them, etc etc, scribble variable names next to the objects so you can clearly see how to progress from the diagram to code.
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;
}