I have a pointer to a node, I want to delete that particular node from linked list.
The below logic works fine, But it fails if node to be deleted is Last node.
How to delete the last node?
void deleteNodWhosePointerIsGivene(node *pointerNode)
{
node *temp=pointerNode->next;
pointerNode->id=temp->id;
pointerNode->next=temp->next;
free(temp);
}
First of all you are not removing pointerNode from the list, you are removing the next node in the list. Secondly you are not checking if there is a next node or not.
Think about what you are trying to do.
You are trying to delete the next object in the list.
You are at the end of the list, therefore the temp pointer will be null.
You are then trying to get the next from null and also trying to free null.
Normally I would do something along the lines of.
DeleteObject(*obj)
Get Next Object
Get Prev Object
Set Prev->next = Next
Set Next->prev = Prev
Delete obj;
If I am understanding you comments above you are wanting to do a copy then your code should look like.
DeleteObject
Get Next Object
if (Next Object is not null)
This.Id == Next.Id
Free Next
else
Throw exception "Cannot delete last object in list"
There is no way to set the pointer in the second to last object in the list if you want to delete the last object in the list. The only way to do this is to either use a double-linked list, or iterate down the list looking for the pointer you wish to delete and keep track of the last object in the list.
You aren't deleting pointerNode in your code, You are deleting pointerNode->next.
Looking at your example of a single linked list. Let's say we have:
1->2->3->4->5
You pass in a pointerNode of "3". Your code then does the following:
1) Assigns pointerNode->Next to temp i.e. "4"
2) pointerNode->Next will be assigned temp->Next i.e. "5"
3) temp is freed i.e. "4"
so your list after this would be 1->2->3->5.
When you get to node "5" then you will get an access violation
1) pointerNode->next is assigned to temp i.e. NULL
2) pointerNode->Next will be assigned temp->Next i.e. access violation as you reference a NULL pointer.
A doubly linked list would be a better solution as to delete pointerNode you need to change the Next pointer of the previous node. Otherwise you'd have to scan the list first to find the node prior to pointerNode.
Here's the code which should work for a singularly linked list. Bear in mind this will be quite slow for large lists and I would second Andrew Norman's suggestion of using a doubly linked list.
Anyway.. here goes. In order for this to work you need to pass the root node of the list and beware that the address of this node may get changed if you try to delete it, hence I pass it as a pointer to a pointer.
void DeleteNode (Node **rootNode,Node *pointerNode)
{
Node *prevNode;
if (pointerNode == *rootNode) {
// Head node is being removed
*rootNode = pointerNode->Next;
} else {
// Find the previous node
for (prevNode = *rootNode; prevNode->Next != pointerNode;
prevNode = prevNode->Next) ;
prevNode->Next = pointerNode->Next;
}
// Free the node
free (pointerNode);
}
If you really need to delete the item associated with the node whose pointer you've got, then you have two options when it comes to the last node:
Iterate from the beginning of the list to find its predecessor (the next-to-last node), delete the last node and set it's predecessor's next to NULL.
Take the lazy approach - do not actually delete the node and only mark it as dead (e.g by setting its data to an impossible value.) Delete it later when you reach it from the predecessor (then also NULL-ing predecessor's next).
Both approaches have obvious drawbacks. This is why it is best to always have the predecessor when deleting a node from a linked list.
If you are able to have a reference to the previous node.
void deleteNodWhosePointerIsGivene(structNode* pCurrentNode, structNode* pPreviousNode)
{
// Not the Last Node ?
if(pCurrentNode->pNext)
pPreviousNode->pNext = pCurrentNode->pNext);
else
pPreviousNode->pNext = NULL;
delete pCurrentNode;
pCurrentNode = NULL;
}
Otherwise you will need to have a reference to the First Node of the List, and search for the previous.
Related
In function given below, I simply delete the head-pointer of the list and set head pointer to nullptr (im setting it nullptr because in my print function,I check for nullptr for head node, and ask user to create list first if head-node is nullptr).
void del_list(stud* &orig_head)
{
cout << "Deleting entire list..." << endl;
delete orig_head;
orig_head = nullptr;
}
I have a question regarding the way I choose to delete the list, since im not clearing each node of list, im simply clear the head pointer, what will happen to all the other nodes? Will this approach create a memory leak ?
Edit:
Im not using OOP to implement linked list,im implementing linked list using struct and couple of functions.
I like to handle this problem recursively:
void deleteNode(Node * head)
{
if(head->pNext != NULL)
{
deleteNode(head->pNext)
}
delete head;
}
If we have a list of 5 items:
head->pNext->pNext->pNext->pNext->NULL;
Then, the function will first get called for head, then for each pNext until the last one. When we reach the last one, it will skip deleting the next one (since it's null) and just delete the last pNext. Then return and delete the list from back to front.
This is assuming that each node's pNext is initialized to NULL. Otherwise, you'll never know when you've reached the end of the linked list.
Your code will cause memory leak. To delete it correctly, traverse the list and while traversing delete each node separately. And finally make head pointer to point to NULL value. You can have a look at the following code.
void deleteList(struct Node** head_ref)
{
struct Node* current = *head_ref;
struct Node* next;
while (current != NULL)
{
next = current->next;
free(current);
current = next;
}
//Now make head_ref point to null
*head_ref = NULL;
}
this coding was to delete the first node, but I guess if I learned to remove the second I will know how to remove the first so here is what I did to remove the first node which now replaces the value with 0.
void removeFirst ( node * head)
{
node * temp = head;
head = head -> next;
delete temp;
You should look up linked lists in your favorite data structures book.
Let's assume that head points to the start of the list and each node has a next field.
// Get the pointer to the second node.
Node * p_second_node = head->next;
// Copy the second node's link to the head node's link:
if (p_second_node)
{
head->next = p_second_node->next;
}
// Finally, delete the second node:
delete p_second_node;
You should always check if the pointer to the second node is nullptr. Deferencing a NULL pointer is undefined behavior.
Edit 1 -- Deleting the first node
Here are the steps for deleting the first, head node:
Node * p_first_node = head;
if (head)
{
head = p_first_node->next;
delete p_first_node;
}
Note: in both cases above, a temporary pointer is created to point to the node to be deleted. This is required because when the links are altered, the node would not be accessible and a memory leak generated. The temporary pointer allows for deleting the node after the links are changed.
NODE* InsertAtHead(NODE* &head, int val){
NODE *tmp = new NODE;
//create a new node
if (tmp == NULL)
exit(1);
//Initialize the new node
tmp->data = val;
tmp->next = NULL;
//Insert by changing links
tmp->next = head;
head = tmp; //update head
return head;
}
Why in the end we need to put return head?
The linked list you have is a singularly linked list. It only has references for next so you can only iterate to the end. If you have a pointer to the beginning you can get to everything, but if you have a pointer to the middle, you have no knowledge of the beginning.
In this function you are prepending, which creates a new beginning. If you don't return the new beginning then the calling function will have no knowledge of this element.
This is of critical impotance as you are using new to allocate memory and if it is not freed there will be a memory leak. It can't be freed unless the calling function has this pointer.
Linked list (when people say linked list they usually mean single linked list) has a link to the next node and no link to the previous one. So imagine if you return a node that is in the middle of list, you could get to next one and next one all the way to the end, this is good. Where you have a problem is that you will not be able to go to the previous node unless you have link to it, which would make it double linked list, or have pointer to the head first node in list which will serve as a start.
In your case you are pushing nodes onto the front of the list thus your code is pushing previous head to next and making new node a head node.
EDIT:
The head should be pass by reference. val should be passed by value. Why? For head we need an address of it so we could make a link to it. For val if we would pass it by reference, the address might be reused in caller function and we do not want our data to change.
I have a sorted linked list and im trying to create a function to delete whatever the user passes to nameToSearch. but i keep geting an error. Below is what i have so far
void deleteProduct(NodePtr head, char* nameToSearch)
{
NodePtr nodeUnderEdit = findNodeByName(head, nameToSearch);
if (nodeUnderEdit == NULL)
{
cout<<"\n ERROR: Product not found \n";
}
else
{
delete nodeUnderEdit;
nodeUnderEdit = nodeUnderEdit->next;
}
}
delete nodeUnderEdit;
nodeUnderEdit = nodeUnderEdit->next;
If you delete nodeUnderEdit first, then nodeUnderEdit->next will be lost.
You need to first make sure that the node that before nodeUnderEdit's next is connected to nodeUnderEdit->next, then you can do the remove.
This is always a problem with a singly-linked list.
The problem arises because deleting the current node from a linked list requires modifying the pointer in the previous node -- to which you don't have direct access.
One way to handle this is to use a list with a sentinel (a final node containing a value you recognize as the end of the list). In this case, you can copy the value from the next node into the current node, then delete the next node from the list.
To remove an item in a singly linked list you must change the pointer from the Previous record to point to the Next record. It is not sufficient for your "findNodeByName" to just find the node with the matching name. It must find the node Previous to it in the sort order, then set the Next pointer of that record of the Next point of the record to be deleted. Only after you have updated the Next pointer of the Previous record can you delete the record you searched for.
This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
LinkedList “node jump”
I just need to have a linked list in order by name. I can only get it as far as 1st, 3rd, 5th, .. nodes. I just can't think this far. I want to be a C++ programmer but if I can't understand this is their any hope? STL containers std::lists are not an option for me at this point as a student. What you see in the list function is what I am TRYING to understanding.
list::node::node(const winery &winery) : item( winery.getName(), winery.getLocation(),
winery.getAcres(), winery.getRating() ), nextByName( NULL ), nextByRating( NULL )
{
}
void list::insert(const winery& winery)
{
node *current_node = new node( winery ); // but came here and did this so it has new info!
node *next_node = NULL;
node *tail_node = current_node;
if ( headByName == NULL ) // then we are here for the first item
{
headByName = current_node; // the list ptrs will have the first node's address.
headByRating = current_node;
}
while ( headByName->nextByName != NULL )
{
headByName->nextByName = tail_node;
tail_node = next_node;
//next_node = current_node;
}
tail_node = new node( winery );
headByName->nextByName = tail_node;
}
And the pointers that are available to me:
struct node
{
winery item;
node * nextByName;
node * nextByRating;
};
class list
{
...
private:
node * headByName;
node * headByRating;
};
Of course there's hope, we all have to start somewhere! :)
First of all, I must point out a dangerous practice in your implementation that my own students used, and it usually resulted in lots of head scratching to find the problem:
while ( headByName->nextByName != NULL )
{
headByName->nextByName = tail_node;
tail_node = next_node;
//next_node = current_node;
}
Don't use a list member like headByName as your iterating variable within your loop unless it is absolutely necessary. You should find the appropriate node first using a local variable, and then modify that node.
That said, Rob Kennedy is right that you should handle the name and rating separately (in other words a 'pure' implementation would have two instances of a generic list class that is unaware of what 'name' and 'rating' mean), however I assume the interfaces for list, node and function above were given to you in the assignment (if not, disregard the rest of my answer :) ).
Given the above interfaces, my approach would be as follows:
void list::insert(const winery& winery)
{
node* wineryNode = new node(winery);
assert (wineryNode);
// Find a node in the list with a name greater than wineryNode's
node* prevNode = NULL;
node* searchNode = headByName; // Note: not using the list member itself to iterate but a local variable
while (NULL != searchNode &&
searchNode.winery.getName() < wineryNode.winery.getName())
{
// Keep iterating through the list until there are no more items, or the right node is found
prevNode = searchNode;
searchNode = searchNode->nextByName;
}
/* At this point searchNode is either NULL
or it's name is greater than or equal to wineryNode's */
// Check for the case where the list was empty, or the first item was what we wanted
if (NULL == prevNode)
{
headByName = wineryNode;
}
else
{
// prevNode is not NULL, and it's Name is less wineryNode's
prevNode-> nextByName = wineryNode;
}
wineryNode->nextByName = searchNode;
/* Now you just need to insert sorted by rating using the same approach as above, except initialize searchNode to headByRating,
and compare and iterate by ratings in the while loop. Don't forget to reset prevNode to NULL */
}
You called this a doubly linked list, and although it's true that your nodes each have two links, this isn't really what most people think of when they hear about doubly linked lists. Since your two links are apparently unrelated to each other — nobody rates wineries alphabetically — it will be a little easier if you don't think of this as a doubly linked list.
The usual place to insert into a linked list, when a more specific order isn't required, is at the end of the list. The steps to do that are as follows:
Create a new node.
Find the place to insert the new node (i.e., the last node, at end of the list)
Update the last node's "next" pointer to point to the new node.
When you're inserting into what may be the middle of the list, as is the case for you, then there is another step:
2.5. Update the new node's "next" pointer.
The reason that step isn't usually there is that when inserting at the end of a list, the new node's "next" pointer is already null from when you constructed the new node object.
You've figured out the first step already. In fact, you've done it too well because your code actually creates two new nodes. One you store in current_node and the other you store in tail_node. Get rid of the second one.
Step 2 is about figuring out which node should be the one that immediately precedes the new node. When ordering by name, that would be the node that comes before the first node you find that has a name after the current name. You're going to have to walk along the list, possibly looking at every node, until you find one that belongs after the new node's name. As you move along the list, you're going to have to keep track of which node came before that node, too, because once you find the node you're looking for, you're going to have to backtrack.
Worry about the name and the rating separately. Don't try to solve both parts at once. Once you get the winery inserted correctly by name, then duplicate that code and replace "name" with "rating." But don't create another node object. Keep using the same one you created before.
As you've worked on this assignment, have you drawn any pictures of nodes with arrows pointing to other nodes? Try it. You've surely seen it done in your textbook or on your teacher's chalkboard. If a problem is too big for you to reason about entirely in your head, then use something to help you keep track of things outside your head. The professionals do it, too. Since each node has multiple pointers, you should either label the pointers or use different colors for name and rating.
To be a doubly-linked list each node must have a pointer to the node before and after it. You can also store a head and tail node if you like, but that would be more for personal convenience than to meet the requirements of a doubly-linked list.
I'm not sure I understand your question exactly. Going just from the topic "How do you insert into a sorted linked list?" I would do something like this (pseudocode):
list::insert(Winery winery) {
Winery node = getHeadNode();
// winery comes before the head node
if (winery.name < node.name) {
// the old head node now goes after this new node
// and this new node becomes the new head node
winery.next = node;
setHeadNode(winery);
return;
}
// let's iterate over the rest of the nodes in the list
Winery previous_node = node;
node = node.next;
while (node != NULL) {
// we found our insertion point
if (winery.name < node.name) {
// insert our new node
previous_node.next = winery;
winery.next = node;
return;
}
// insertion point not found, move to the next node and repeat
previous_node = node;
node = node.next;
}
// all elements visited, append our new element to the end of the list
previous_node.next = winery;
winery.next = NULL;
}
The above algorithm will insert your new node in the appropriate place in the list, assuming the list is sorted by name.