I'm having an issue with deleting pointers. I don't think I'm doing anything compiler illegal or anything, but perhaps I am, so I would appreciate it if someone could explain the flaw in my logic. I'm hoping the below function should be enough to help, as the whole thing would be a lot to transcribe, but if any more of the code is required, please let me know and I'll add it!
The below is a function to remove lockers from a linked list I've created. I've done my best to cover every conceivable case. The problem arises when I try to actually deallocate the memory of a locker I want to delete. The lines where I've tried to delete the temp variable that references that locked are commented out, because the code breaks with them included. Obviously, though, without them, I can't delete the lockers like I want.
int SelfStorageList::removeLockersOverdue() {
int lockersDeleted = 0;
if (isEmpty()) {
return 0;
}
if (head->objLocker.isRentOverdue && head==tail) { //If that was the only locker, the tail needs to be updated to = head = 0
delete head;
head = tail = 0;
return ++lockersDeleted;
}
LockerNode *prev = head;
LockerNode *curr = head->next;
while (curr != 0) {
if((curr == tail) && curr->objLocker.isRentOverdue) { //If the current locker is tail and needs deleting
LockerNode *temp = curr;
curr = prev;
//delete temp;
lockersDeleted++;
}
if(prev->objLocker.isRentOverdue) { //General case: Previous locker needs deleting
LockerNode *temp = prev;
prev = prev->next;
curr = curr->next;
//delete temp;
lockersDeleted++;
}
else { //Update the pointers if not updated anywhere else
prev = prev->next;
curr = curr->next;
}
}
return lockersDeleted;
}
Any "pointers"? (Terrible pun. :p )
Because you're maintaining a singly-linked list, I see you're keeping track of a prev pointer as you're iterating through your list. That's as it should be, of course, since you can't get the previous node of a given node in a linked list if it's singly-linked without remembering what its previous node was. Your problem is simply that your logic is busted: If a deletion is needed, you need to be deleting the curr node, and patching up the prev node to point its next pointer to curr->next before you delete curr.
Think about it: What you're doing is deleting the prev node, but there's likely a "more previous than that" node that's still pointing to the prev node that you just deleted. The next time you iterate through the list, you'll be iterating into formerly allocated nodes, which may be allocated for some entirely different purpose by that point. Your memory allocator is failing some internal assertion because likely on the next time you call removeLockersOverdue(), the memory has NOT yet been allocated to something else, and you're still finding the same node there that you already deleted, and finding again that it's overdue, and deleting it again, and your memory allocator is complaining that you're deleting memory that isn't allocated. (It would be really nice if it gave you that clear of a message, wouldn't it!)
Also, your special case stuff for handling the very first node & last node can be simplified & commonized; I'll avoid rewriting it for you so you can see if you can simplify it yourself.
while (curr != 0) {
if((curr == tail) && curr->objLocker.isRentOverdue) {
LockerNode *temp = curr;
curr = prev;
//delete temp;
lockersDeleted++;
}
You are not updating the previous node's next pointer here, so it ends up dangling (pointing to memory that is deleted). You also leave tail pointing at the deleted node.
if(prev->objLocker.isRentOverdue) {
LockerNode *temp = prev;
prev = prev->next;
curr = curr->next;
//delete temp;
lockersDeleted++;
}
Again, you don't update the next pointer in the list that points at the deleted node.
else { //Update the pointers if not updated anywhere else
prev = prev->next;
curr = curr->next;
}
}
I think you are greatly confusing yourself by carrying around two pointers in the loop. Try to write it again with just a single pointer tracking the node you are currently examining, and draw some pictures of the three possible cases (remove first node, remove last node, remove internal node) to ensure you are getting the removal logic right.
Related
I tried to create a code for it. However, it didn't work. Here's my previous attempted code:
void modifyLastNode(node*) {
curr = head;
while (Curr != NULL && curr->data != 99) {
temp = new node;
temp->data = 1000;
temp->next = curr;
}
}
Aside from the syntax errors that prevent your code from even compiling properly (ie, curr, head, Curr, and temp are undefined), the code also contains several logic errors:
You are ignoring the input parameter.
You are not changing what curr points to while looping, so you end up checking the data field of the same node over and over, so you end up in an endless loop.
You are allocating (and leaking) a new node on each loop iteration. Searching should not be creating new nodes at all.
Checking the data field is the wrong way to detect the last node in the list. The last node has its next field set to NULL, that is what you should be looking for. Imagine calling modifyLastNode() twice on the same list. The 1st call would look for 99 (which in your example is the last node in the list, but in a production system it may not be), changes it to 1000, and then the 2nd call cannot find 99 anymore.
You need something more like this instead:
void modifyLastNode(node *head) {
if (head) {
node *curr = head;
while (curr->next != NULL) {
curr = curr->next;
}
curr->data = 1000;
}
}
Below program is used to remove the duplicates from a sorted singly linked list. The code gives garbage values in online IDE. But when I comment the line .
delete curr;
The program works fine in online IDE itself. Here is the function I wrote. The other parts of the code are well-defined(not by me) by the online Judge.
And also the code without commenting the line delete curr; works fine in local IDE(codeblocks).
FULL PROGRAM:http://ideone.com/9bHab0
Why do I get the garbage values?
Node *removeDuplicates(Node *root)
{
// your code goes here
struct Node* curr = root,*prev = NULL;
while(curr)
{
if(prev==NULL)
prev = curr;
else if(curr->data!=prev->data)
prev = curr;
else
{
prev->next = curr->next;
delete curr;
curr = prev->next;
}
}
return root;
}
EDIT: One could see the pointer whose location is deleted, is reassigned immediately. Thus there couldn't be a dangling pointer here!
Lets take a very simple example, with a two-node list, where you have e.g.
node1 -> node2
With the first iteration then prev is NULL so you do prev = curr. Now curr and prev points to the same node.
That means in the second iteration both if conditions are false (prev != NULL and curr->data == prev->data) you go into the else part, where you have
prev->next = curr->next;
delete curr;
curr = prev->next;
Here you delete curr but curr is pointing to the same memory as prev, leading to undefined behavior in the assignment curr = prev->next, as you now dereference the stray pointer prev.
To make matters worse, you then enter a third iteration where prev is still pointing to the deleted first node, and again dereference the invalid prev pointer (in your second if condition) and you once again end up in the else part where you continue the invalid dereferencing. And so on in infinity (or you get a crash).
I've pasted my work so far here:
http://codepad.org/WhJuujRm
The concepts of linked lists boggle my mind, so I thought I'd practice. I know how to add nodes, and edit nodes, but I don't know how to remove nodes in my particular scenario.
My Pseudo Code:
previous == now - 1;
if(stdid == now->getID());
previous->setNext(now->getNext);
delete now;
return;
How could I implement this?
The mind-tease in deleting an element from a linked list is updating the pointer that brought you to the element in the first place. In your list case, that could be top (and/or possibly bottom), it could be some node's next. As you walk through the list hunting with a cur pointer, keep a prev pointer which you advance one step behind as you enumerate. Assuming you find the victim node (if you don't, there's nothing to do, woot!), prev will be in one of two states:
It will be NULL, in which case top is the pointer that refers to your victim node and top must be updated, or...
It will be some pointer to a node, in which case that node's next member needs to be updated to the reflect the victim node's next member value.
In both cases bottom may need updating as well. In the first case bottom will need to change if the list only had one node and you're deleting it. i.e. you will have an empty list when finished. Easy enough to tell, since top will be NULL after to detach cur and set top equal to cur->next. Even easier for you, since you're keeping a size member in your list container; if it was 1, you know both head and bottom
In the second case, the last node may be the victim node. In that case bottom has to be updated to reflect the new end of the list (which is coincidentally in prev, and may be NULL if, once again, the list had only a single element. How do you tell if the victim was the last node in the list? If it's next member is NULL, it has to be the last node, and bottom must be updated.
So something like this, a delete function based on ID search
void deleteStudent(int id)
{
student *cur = top, *prev = nullptr;
while (cur && cur->getID() != id)
{
prev = cur;
cur = cur->getNext();
}
// found a node?
if (cur)
{
student *pNext = cur->getNext();
// set new next pointer for prev, or new top
if (prev)
prev->setNext(pNext);
else
top = pNext;
// update bottom if needed
if (!pNext)
bottom = prev;
delete cur;
--scnt;
}
}
Other delete options and criteria I leave to you.
Best of luck.
This should work, but I have not tested it.
There is a special case, when the first node is deleted. previous is set to NULL for the first iteration, and the top has to be adjusted in this case.
I didn't use bottom, because it's not the way I would do it. If you use bottom, there is a second special case, when you delete the last student. I would mark the end of the list with a next pointer set to NULL, because this eliminates this special case.
bool deleteStudent(int id)
{
student* now = top;
student* prev = NULL;
while(now != NULL) {
student* next = now->getNext();
if(id == now->getID()) {
delete now;
if(prev) prev->setNext(next);
else top = next;
return true;
}
prev = now;
now = next;
}
return false;
}
I did not use your notation but I think you can get the point.
prev = NULL;
current = top;
while (current != NULL && !isfound(current)){
prev = current;
current = current->next;
}
// current point to the element you want to delete (if not NULL)
if(current != NULL) {
if(previous != NULL) {
previous->next = current->next;
}
else {
top = current->next;
}
delete current;
}
I got some problems with the algorithm for "clearing" nodes in a circular list: most of times the program crashes and sometimes not. I'm quite sure that the algorithm is ok and I have no clue about the solution... :-(
Here some piecies of code:
The clear() function (Note: mFreenode is the "head-tail free" node for the circular list):
List* clear() {
if (mFreenode->getNext() != 0) {
Node<T>* current = mFreenode->getNext();
Node<T>* temp = 0;
while (current != mFreenode) {
temp = current->getNext();
delete current;
current = temp;
}
}
mFreenode->setNext(0);
mFreenode->setPrev(0);
mSize = 0;
return this;
}
And this is the Node destructor:
~Node() {
delete &item;
}
Any suggestion?
Your destructor looks very suspicious -- I'm assuming that item is a member variable of Node. If item is a value type (eg int or std::string), then you shouldn't be deleting it at all. If it's a pointer, then perhaps you should be doing delete item; -- but only if the Node has ownership of the item that it has a pointer to.
When you delete item; you do not make the previus Node to point to the Node after the Node you delete. As a result the Node previus to the one you delete points to nowhere.
Please never do the following with a circular linked list:
while (current != mFreenode) {
temp = current->getNext();
delete current;
current = temp;
}
Why do you think it is a circular linked list?? Because the tail node points to the first one, so when you do that the loop will go ahead and delete all nodes except the node before current.
and then you try to access it bad idea:
mFreenode->setNext(0);
mFreenode->setPrev(0);
I have a linked list contains 3 nodes like the image shown:
There is a head pointer and temp1 pointer point to the front of the list, and tail point points at the end of the list.
I want to remove all the nodes, and change it back to its original initial form ( tail = NULL, head = first_node , but the first node doesn't have any value in the data and next field).
Because I want to start putting up some new values in it. To remove all those data, is this code going to remove nodes inside this linked list and left with the first node with no values in data and next field?
This code is in C++:
while(temp1!=tail)
{
temp1 = temp1->next;
if(temp1->next == tail)
{
tail=temp1;
temp1 = temp1->next;
free(temp1);
}
}
But then, does this mean only the last node will be deleted? are there any way to delete all the nodes except the first one?
To delete all nodes except the first node, you can try below code.
temp1 = head->next;
while(temp1!=NULL) // as I am considering tail->next = NULL
{
head->next = temp1->next;
temp1->next = NULL;
free(temp1);
temp1 = head->next;
}
This will delete all nodes except first one. But the data with the first node will remain as it is.
Disclaimer: I assume it's only for learning purposes and in real-world scenario you would use std::list<> or similar container.
For single-linked list, you can just drop all this burden an let the stdlib manage the pointers:
class Node {
std::unique_ptr<Node> next;
};
You can safely use .reset() method to make operations on the list:
Given current_ptr, the pointer that was managed by *this, performs the following actions, in this order:
Saves a copy of the current pointer old_ptr = current_ptr
Overwrites the current pointer with the argument current_ptr = ptr
If the old pointer was non-empty, deletes the previously managed object if(old_ptr != nullptr) get_deleter()(old_ptr).
From http://en.cppreference.com/w/cpp/memory/unique_ptr/reset.
And that's pretty much what you would do when deleting. I believe you can also use unique_ptr::swap(), to easily manipulate your nodes.
Instead of free, C++ uses delete function.
Check the link to have deep knowledge about all kind of operations(including recursive or iterative delete) on linked lists.
temp1 = head->next;
while(temp1!=NULL) // as I am considering tail->next = NULL
{
head->next = temp1->next;
temp1->next = NULL;
free(temp1);
temp1 = head->next;
}
The logic for this would be more correct if it is this way.
After the statement
free(temp1);
Add the condition
if (head -> next != NULL)
temp1 = head->next;
Since after deleting the last node there is no point in reassigning the address of head pointer to temp1.