I searched previous asked questions but could not find exactly what I was looking for. I was curious if anyone had any idea on how to delete every other node of a linked list. I have a function called duplicate which take 1 2 3 and turns it into 1 1 2 2 3 3. Deleting every other node would work just fine, no need to compare them or anything. If anyone has any insights. Please don't just post source code.
Heres what I was trying to do but wasn't working.
Node *current;
Node *undo;
for (current = front, undo = current->next->next;
undo != NULL; current = current->next, undo = current->next->next){
current->next = undo;
}
This will output 1 1 2 2 3 3 3
Thank you for any help. I can comment back later to clarify any misconceptions.
Original code:
for (current = front, undo = current->next->next;
undo != NULL;
current = current->next, // this moves current on before you use it's next pointer
undo = current->next->next){
current->next = undo;
}
To fix, without deallocating the unwanted nodes (assumes an even number of nodes):
for (current = front; current != NULL; current = current->next){
current->next = current->next->next
}
To handle odd length lists, and delete memory of removed nodes:
for (current = front; current && current->next ; current = current->next){
undo = current->next;
current->next = current->next->next
delete undo;
}
I would use std::list (or perhaps std::forward_list if so needed) and std::copy_if or std::remove_if in C++11.
Otherwise, remember the address of the pointer to be modified, something like:
Node**undoad = &front;
int cnt=0;
for (current=front; current != NULL; current=current->next) {
if (cnt%2 == 1) {
Node*undo = *undoad;
undoad = ¤t->next;
if (undo) undo->next = current->next;
}
cnt++;
}
However, the above probably is a memory leak: you may need to use delete somewhere.
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;
}
}
I'm attempting to delete some nodes from a singly linked list. However, visual studio tells me that there is a read access violation in my function. I've found that it is one certain line. I've checked to make sure that I'm not deleting any of the nodes before attempting to write to them (I'm confident, though I may be wrong.) Here's the function:
template <class dataType>
bool UnOrderedList<dataType>::remove(int remove) {
Node<dataType>* current = head;
Node<dataType>* copy = head;
Node<dataType>* trail = nullptr;
if (current == nullptr) {
return false;
}
if (head->data == remove) {
Node<dataType>* temp = head;
head = head->next;
delete temp;
return true;
}
while (current != nullptr) {
trail = current;
if (current->data == remove) { // error occurs on this line
Node<dataType>* temp2 = current;
current = trail->next;
delete temp2;
return true;
}
current = current->next;
}
return false;
}
I've commented on the line in which the error occurs.
Visual Studio says: "Exception thrown: read access violation.
current was 0xDDDDDDDD."
Is there something I'm doing wrong? I would love to know how I can avoid doing this in the future. I'm interested in learning more about pointers and how they work.
Thank you for any help!
When you delete a node, you don’t update the previous node’s next pointer. The next time thru the loop you’ll access this dangling pointer and read memory that has been freed (which Visual C++ has set to 0xDDDDDDDD).
You need to update the previous node’s next pointer to the current node’s next pointer before deleting it, so that you skip over the node you’re deleting.
As JaMiT mentioned, it is hard to debug without being able to reproduce. Please note a few suggestions
You never use copy.
The behaviour in the following part
while (current != nullptr) {
trail = current;
if (current->data == remove) { // error occurs on this line
Node<dataType>* temp2 = current;
current = trail->next;
delete temp2;
return true;
}
current = current->next;
}
does look like something you'd want to do. When you enter the loop for the first time, trail and current are the same thing (because you assign current to trail). If current node was not the node to be deleted, you do current = current->next;. Assuming this was not nullptr, you loop back over and set trail to current again. You may want to set trail to the old current (so that trail follows the current as the name suggests).
while (current != nullptr) {
if (current->data == remove) {
Node<dataType>* temp2 = current;//save pointer to the node to delete later
trail->next = current->next;//skip this node - deleting it from the chain
delete temp2;//delete it
return true;
}
trail = current; //let trail follow current
current = current->next;
}
Why does remove for an UnOrderedList<dataType> take an int instead of dataType ?
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'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.
I have the following array structure (linked list):
struct str_pair
{
char ip [50] ;
char uri [50] ;
str_pair *next ;
} ;
str_pair *item;
I know to create a new item, I need to use
item = new str_pair;
However, I need to be able to loop through the array and delete a particular item. I have the looping part sorted. But how do I delete an item from an array of structures?
What you've shown is not an array of struct, but a linked list of struct containing arrays (of type char).
An array of struct would look like this:
str_pair array_of_structs[10];
// or:
str_pair* dynamically_allocated_array_of_structs = new str_pair[10];
If you actually have something like this, you don't need to delete single items from an array. Let's say you've initialized your array as follows:
str_pair* array_of_structs = new str_pair[10];
Then you delete the whole array (including all of its items) using:
delete[] array_of_structs;
Again, you can't delete single items in an array allocated with new[]; you perform a delete[] on the whole array.
If, on the other hand, you intended to say "linked list of struct", then you'd generally delete an item similarly to the following:
str_pair* previous_item = ...;
str_pair* item_to_delete = previous_item->next;
if (item_to_delete != 0)
{
previous_item->next = item_to_delete->next; // make the list "skip" one item
delete item_to_delete; // and delete the skipped item
}
Or, in English: Find the item (A) preceding the item which you want to delete (B), then adjust A's "next" pointer so that B will be skipped in the list, then delete B.
You need to be careful with special cases, i.e. when the item to be removed from the list is the first item or the last one. The above code is not sufficient when you want to delete the first item in the list, because there will be no previous_item. In this case, you'd need to change the pointer to the list's first element to the second element.
Your code:
void deleteitem(char *uri)
{
str_pair *itemtodelete;
curr = head;
while (curr->next != NULL) {
if ((strcmp(curr->uri, uri)) == 0) {
itemtodelete = curr;
curr = itemtodelete->next;
delete itemtodelete;
curr = head;
return;
}
curr = curr->next;
}
}
Some things are wrong here:
If head is null, the test curr->next != NULL will cause a segfault. (You must never dereference a null pointer!)
Your code for removing an item from the list is completely incorrect. Worst of all, you delete a node without changing the previous item's next pointer. The previous item will thus reference an item that's no longer there.
A detail: curr = head; before the return statement doesn't do anything useful at all.
Suggested code:
Do it in two steps: One function to find the node to be deleted via its attached uri, and one function to remove the node. You could separate it even better than the code below does, but it should be a starting point:
str_pair* finditemwithuri(char* uri)
{
str_pair* current = head;
while (current)
{
if (strcmp(current->uri, uri) == 0) return current;
current = current->next;
}
return 0;
}
void deleteitem(char* uri)
{
// find linked list node with that uri; abort if uri not in list
str_pair* itemtodelete = finditemwithuri(uri);
if (!itemtodelete) return;
// special case: node to be deleted is the list's head
if (itemtodelete == head)
{
head = itemtodelete->next;
delete itemtodelete;
return;
}
// else, iterate over list nodes
// up to the one preceding the node to be deleted
str_pair* current = head;
while (current)
{
if (itemtodelete == current->next)
{
current->next = itemtodelete->next;
delete itemtodelete;
return;
}
current = current->next;
}
}
Just use std::list. There's no reason to manually write such a construct.
http://msdn.microsoft.com/en-us/library/802d66bt(VS.80).aspx
std::list offers remove.
You can delete it using the ordinary delete keyword, but that will not shift all other members of the array. If you want this kind of behaviour take a look at std::vector or something like that.
It is maybe off topic, but why dont you use the std::list library ?
As others have pointed out, this is a Linked List, not an array. To answer your question for linked lists:
To insert an item:
str_pair* p = // iterate over the linked list to your insertion point
str_pair* item = new str_pair;
item->next = p->next;
p->next = item;
That inserts a new str_pair after p.
To remove an item:
str_pair* p = // iterate to just before your deletion point
str_pair* item = p->next;
p->next = p->next->next;
delete item;
This will remove the element after p
To do this with your code:
void deleteitem(char *uri)
{
str_pair *previous = NULL;
curr = head;
while (curr != NULL) {
if ((strcmp(curr->uri, uri)) == 0) {
// modify the previous element to skip over our deleted one
if (previous)
previous->next = curr->next;
else
head = curr->next;
// safely delete the element, now that no one points to it
delete curr;
curr = head;
return;
}
// always remember our previous element, so we can fix its 'next' pointer
previous = curr;
curr = curr->next;
}
}
You need a better add method too:
void additem(char *uri, char *ip)
{
curr = head;
// traverse the list until we're at the last item
while (curr->next != NULL) {
curr = curr->next;
}
// attach a new element to the list
curr->next = new str_pair;
// go to that new element
curr = curr->next;
// set the values of the new element
strcpy(curr->ip, ip);
strcpy(curr->uri, uri);
curr->next = NULL;
curr = head;
}