Delete elements conditionally in a Singly Linked List in C++ - c++

I'm having a problem in a function that deletes an element in a singly linked list. The element needs to be deleted conditionnaly with the function ShouldDelete().
I haven't found a similar request in C++ in the forums so any help would be appreciated. It seems that the following code does not work 100% of the time.
void SinglyLinkedList::DeleteObjects(Node *pStartPtr)
{
Node *pPtr = pStartPtr;
Node *temp;
while(pPtr != nullptr)
{
temp = pPtr->next;
if (pPtr->ShouldDelete())
{
delete pPtr;
pPtr = temp;
if(temp != nullptr)
{
pPtr->next = temp->next;
}
}
pPtr = temp;
}
}
Any thoughts ?
Thanks!

As a general C++ advice, I would use std::list instead of a DIY list implementation, but since I wrote quite some list logic myself in the old C-days, I'll give it a go.
There are 2 essential problems with your code.
First of all, whenever removing an element from a single-linked list, you should set the next pointer of the previous element to the next of the deleted one. You can do something like this:
Node *previousNode = …;
while(pPtr != nullptr)
{
Node *nextNode = pPtr->next;
if (pPtr->ShouldDelete())
{
delete pPtr;
previousNode->next = nextNode;
}
else
{
previousNode = pPtr;
}
pPtr = nextNode;
}
Notice that no specific if-tests within the for-loop on nullptr are needed. You should tell the previous node that it now points to the next of the deleted one. If there is no next one, the previous->next will just point to a nullptr, indicating the end of the list.
The second problem is that you don't have any clear 'start of the list'. In my example code above, it becomes quite hard to indicate what the previousNode is. Is it a nullptr? But what if we remove the first element?
You could easily add logic here to indicate keep track of the first element, initialize previousNode to nullptr, and if the first is deleted, just don't set previousNode->next' and keep previousNode equal tonullptr` in that case, but what about the caller? How can the caller know that the first element has been deleted and the list now starts later.
My preferred solution is to have a separate List struct/class, and keep the start of the list (the first Node) as a member in the List struct/class. Instead of passing the startNode to the function, you should pass the List instance.
The code in the while-loop now becomes a bit longer, since you need to adapt previousNode->next=nextNode to this:
if (previousNode)
previousNode->next = nextNode;
else
list->firstNode = nextNode;
If you don't want to introduce a new List struct/class, you could also return the new firstNode as return value from your function, like this:
if (previousNode)
previousNode->next = nextNode;
else
startNode = nextNode;
…
return startNode;
Notice that this second approach only works if the caller if the function is also the one maintaining the list, and there are no other places pointing to the startNode.
In any case, if multithreading is involved, you probably want a decent List class with a kind of mutex (ideally std::list decorated with a mutex).

Related

Is this a effective way to delete entire linked list?

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;
}

C++ Circular Linked List : remove element

I am done with insertion, search in circular linked list but for removal I am getting compiler errors...
Following is my structure for nodes.
struct node
{
int p_data;
struct node* p_next;
node(node* head, int data)
{
p_next = head;
p_data = data;
}
explicit node(int data)
{
p_next = nullptr;
p_data = data;
}
};
node* remove_circular(node* head, node* target)
{
if (head == target->p_next)
{
delete head;
return nullptr;
}
auto next_pointer = target->p_next;
target->p_data = next_pointer->p_data;
target->p_next = next_pointer->p_next;
delete target->p_next;
return target;
}
and in main function I call
head = remove_circular(head, head);
head = remove_circular(head, temp);
this is to remove head element and another element that temp points to.
But I am getting errors
Anybody has any idea to remove one element from circular list??
I changed it to delete target->p_next;
but now it deletes everything in the list.
Any idea???
This is how a circular linked list works:
Each node points to the next in line, and the tail of the list points to the header node. That's the difference from a circular linked list to a regular linked list (which, in the case above, would make 37 point to a terminator null).
In the case of your list having only one object, then it should look something like this:
So, as you can see, there is no object pointing to null anywhere, yet it happens on your code with your explicit constructor (which will run if I write node n = node(12)).
I suggest you take a look at this link to have a better understanding of how your algorithm should look like.
Once you resolve your compiler error, you are still going to have algorithmic issues. I suggest you draw a circular list on paper and think about the steps required to remove an element. Consider all the cases, for example: empty list, list of 1 item, element not in the list, etc.
You need to consider several things.
1.) the case of an empty list
if(head == nullptr){//Empty list case
return nullptr;
}
2.) The target to be removed is the head node and this is the only node in the list.
if (head == target && target->p_next == head){
create a temp node with the data value of target
target = nullptr;//Since nothing points to target now it is for all intents and purposes deleted from the list but the data is still there so you can do something with it. I assume this is necessary because you return a node *.
return the temp node
}
3.) Create a loop that iterates through the entire list. You have something that would only delete the next node which works if you have a two item list and target was the second item.
auto next_pointer = head->p_next;//could not be target->p_next as this assumed
while (next_pointer->p_next != target){//This while loop traverses the list rather than just deleting the next entry.
4.)Inside you loop add a check to see if the list has been traversed and target was never found.
if (next_pointer->p_next == head){
return nullptr;
}//end IF
5.) Inside the loop add the else case which means target was in an arbitrary location in the list. Since I gave you the rest I'll leave you to get this part. It's not hard just a few lines longer than the statements above.

Deleting a node in circular linked list

I have a basic linked list problem that I have attempted to solve below. I would appreciate any inputs on my approach, correctness of the algorithm (and even coding style). The problem calls for a function that deletes all occurrences of an int in a circular linked list and returns any node from the list or NULL (when the list is null).
Here's some C++ code that I have so far:
struct Node{
Node* next;
int data;
};
Node* deleteNode(Node* &node, int num){
if(!node){
return NULL;
}
Node* given = node;
Node* del;
while(node->next != given){
if(node->next->data == num){
del = node->next;
node->next = node->next->next;
delete del;
}
node = node->next;
}
//Check if the first node needs to be deleted, with variable node pointing to last element
if(given->data == num){
node->next = given->next;
delete given;
}
return node;
}
The delete node; should be delete del;.
Also, use Node* node as parameter, instead of Node* &node which will prevent non-lvalues from passing in.
p.s. Forgot a semicolon after struct definition? :)
Without following all your logic I can see at a glance this code cannot work.
You are checking for the input list being empty and that's the only case in which your code returns NULL. But what happens if you are passed a list in which all elements must be deleted?
This problem also has a subtlety in it. To check if you completed a circular list you need to compare to the first address to see if you got linked back to the start. However if this element has been deleted then by C++ standard you're not even allowed to use its address in a comparison.
To avoid making two passes over the elements to be deleted one possible trick is to "break the loop" when starting iteration so you can check for NULL instead of checking for the address of the starting node.

Delete node from linked list (recursively)

!=I am currently working on the following erase recursive bool function that thakes list and int as arguments and return true if the int was found and deleted and false if it was not found in the list. It seems to work, but the problem is that it deletes the next int number in the list, and not the current:
typedef struct E_Type * List;
struct E_Type
{
int data;
List next = 0;
};
bool erase(const List & l, int data){
List current = l;
if (current == 0)
{
return false;
}
else if (current->data == data)
{
List deleteNode = new E_Type;
deleteNode = current->next;//probably this causes the error, but how can I point it to the current without crashing the program
current->next = deleteNode->next;
delete deleteNode;
return true;
}
else if (current->data != data)
{
return erase(current->next, data);
}
}
There are two basic type of lists:
single-linked lists (each node knows its next node) and
double-linked lists (each node knows its next as well as its previous node).
If, like in your case, one has a single-linked list, you must not check the CURRENT node for equality to 'data', because at that point it is too late to change the next pointer of the last node. So you always have to check the NEXT pointer for equality, like this:
bool erase(const List & l, int data)
{
List current = l;
if (current == 0)
return false;
// special case: node to be deleted is the first one
if (current->data == data)
{
delete current;
return true;
}
if (current->next && current->next->data == data) // next exists and must be erased
{
List deleteNode = current->next; // Step 1: save ptr to next
current->next = deleteNode->next; // Step 2: reassign current->next ptr
delete deleteNode; // Step 3: delete the node
return true;
}
return erase(current->next, data);
}
Note: I spared your last 'else if' condition. The 'else' because the previous if had a return in it, and the 'if' since its condition was just the negation of the previous 'if', which - if the program comes this far - would always hold.
Regards
The only node you're considering is the current one, so you must have a provision for modifying l:
if (current->data == data)
{
l = current->next;
delete current;
return true;
}
Here are some pointers.
An iterative approach
When you're iterating over your list, maintaining a pointer to the current element is not enough. You also need to maintain a pointer to the previous element, since you will need to fix up previous->next if you delete the current element.
On top of that, deleting the first element of the list will require special handling.
A recursive approach
Write a recursive function that will take a pointer to the head of the list, find & delete the required element, and return a pointer to the new head of the list. To do this, you need to:
Define and implement the base case. Handling one-element lists seems like a natural candidate.
Define the recursion. There are two cases: either the head of the list is the element you're looking for, or it isn't. Figure out what you need to do in both cases, and take it from there.
If you have a list:
A --> B --> C --> D
And you want to delete C, you have to:
Store C in a temp variable
Change B->next=C->next
delete C.
So you need to find B to be able to modify it.
You should certainly not create any new instance of E_type.
Your condition
else if (current->data == data)
will stop on the node which has the value data. You then go on to delete the node after this node in your code.
If you want to keep the rest of the code same, then that line should be :
else if ((current->next)->data == data)
with an extra check, in case the first element is the only element in the list.
A simpler way would be to keep a pointer that points to the element before the current element, and then deleting the node which is referred by that pointer.
You will need to change the next pointer of the preceding entry. So everything is find, but you have to check current->next->data against data, not current->data.
Be sure to check for NULL-pointers in case current is the last entry in the list!
When you delete a node from a list, you need to point the previous node to the next one. Since you have a singly linked list, there are 2 options:
Maintain a pointer to previous node in your erase function. When encountering desired node, link previous node to current->next and delete current node. Needs special treatment for the first node in the list.
When you encounter desired node, copy the content of the current->next into current, then delete current->next. This way you don't need an extra parameter in your function. Needs special treatment for the last node in the list.

Deleting Element from linked list, interviews exposed book bug?

Pg. 29 of the Programming interviews exposed book has the following sample code to delete an element from a linked list:
bool deleteElement(IntElement **head, IntElement *deleteMe)
{
IntElement *elem = *head;
if(deleteMe == *head){ /*special case for head*/
*head = elem->next;
delete deleteMe;
return true;
}
while (elem){
if(elem->next == deleteMe){
/*elem is element preceding deleteMe */
elem->next = deleteMe->next;
delete deleteMe;
return true;
}
elem = elem->next;
}
/*deleteMe not found */
return false;
}
My question is about the statement "delete deleteMe", is this achieving the effect we want i.e. actually deleting the element at that position, or is it just deleting a copy of a pointer to the deleteMe element?
delete deleteMe; calls the destructor on the element and frees its associated memory. This code is C++, btw.
The rest of the code alters the data structure, the list, to unlink the element from its neighbors.
Your question has already been answered, but I feel obliged to point out that if I were interviewing somebody, and they wrote the code this way, I wouldn't be terribly impressed.
For starters, the use of a pointer to a pointer here, while reasonable in C, is entirely unnecessary in C++. Instead, I'd prefer to see a reference to a pointer. Second, code that's const correct is generally preferable.
bool deleteElement(IntElement const *&head, IntElement const *deleteMe)
{
IntElement *elem = head;
if(deleteMe == head){ /*special case for head*/
head = elem->next;
delete deleteMe;
return true;
}
while (elem){
if(elem->next == deleteMe){
/*elem is element preceding deleteMe */
elem->next = deleteMe->next;
delete deleteMe;
return true;
}
elem = elem->next;
}
/*deleteMe not found */
return false;
}
Finally, I'd add one more special case in an attempt at avoiding an unnecessary traversal of the list. Unless the item to delete happens to be at the very end of the list, you can avoid the traversal. Let's assume your IntElement is something like:
struct IntElement {
int data;
IntElement *next;
};
In this case:
bool simple_delete(IntElement *deleteMe) {
IntElement *temp = deleteMe->next;
deleteMe->data = temp->data;
deleteMe->next = temp->next;
delete temp;
return true;
}
They're searching the whole list to find the previous element, so they can delete the element after it. Instead, we simply copy the next node into the current node, then delete the next node (note: in some cases it'll be better/faster to swap the data instead of copying). Also note that this can/will break things (quite thoroughly) if something else might be holding a pointer to the next node.
[For what it's worth, I originally learned this technique from Algorithms + Data Structures = Programs, by Niklaus Wirth, though I believe it originated with Knuth.]
Anyway, once we have that, we just add a bit of code to deleteElement to use that unless the node to be deleted happens to be the last in the list:
bool deleteElement(IntElement const *&head, IntElement *deleteMe) {
if (deleteMe->next != NULL)
return simple_delete(deleteMe);
// previous body of deleteElement
}
Where the original always had linear complexity, this has constant complexity in most cases. By using a list with a sentinel value instead of a NULL pointer at the end, you can ensure constant complexity in all cases (and simple_delete handles all cases -- you can eliminate the rest of the code entirely).
It's actually deleting the node itself and not the copy of it.