Deletion in a linked list - c++

Qn) Given only a pointer to a node to be deleted in a singly linked list, how do
you delete it?
I am trying to delete the last element i.e., 1 but the else part goes into an infinite
loop printing garbage values.
Original link.
int main()
{
struct Node* head = NULL;
push(&head, 1);
push(&head, 4);
push(&head, 6);
push(&head, 8);
print(head);
del_p(head->next->next->next);
cout << endl;
print(head);
return 0;
}
void del_p(struct Node* current)
{
struct Node* temp;
if (current->next != NULL)
{
temp = current->next;
current->data = temp->data;
current->next = temp->next;
free(temp);
}
else
{
free(current);
current = NULL;
}
}

The else branch of your function tries to reassign current to NULL. This is problematic because current is a local copy of the pointer passed in. That is, you can't modify the value of the original pointer.
This is why you are receiving garbage, because you're accessing a node whose memory has already been deallocated.
You either need a double pointer, or preferably a reference to the node:
void del_p(struct Node*& current)

If you pass in the node to be deleted and the head node then you can loop until you find the node prior to the node to be deleted. You then need to point the prior node to the node that is pointed to by the node to be deleted and then you can free the node you want to delete.
void delete(struct Node* to_delete, struct Node* head)
{
// check if node to be deleted is the head
if (to_delete == head)
{
head = to_delete->next;
return;
}
// make a local copy of the head just in case as to not alter it
struct Node* tempHead = head;
while(tempHead->next != to_delete)
{
tempHead = tempHead->next;
}
tempHead->next = to_delete->next;
free(to_delete);
}
Just as a disclaimer I haven't tested this code, but conceptually it should work.

The typical algorithm for deleting a node on a linked list would follow the next steps:
Get a temp pointer started in Head.
Move your temp to the node you want to delete (in this case one before the last: temp->next == NULL).
Free the memory for temp2.
Set the pointer of temp->next to NULL.
Return the pointer to head.
Now this is not the only algorithm, there are a lot of ways you can accomplish this. The following code would be my solution to the function del_p (if you would want to delete the last node):
void del_p(struct Node *head)
{
if (head != NULL)
{
struct Node *temp = head;
while (temp->next != NULL) temp = temp->next;
free(temp);
}
}
You can make this code a little more general to make it possible to delete any Node, by passing a pointer to that node (or a value), the code would look as follows:
void del_p(struct Node **head, struct Node *delete_node)
{
if (head != NULL)
{
struct Node *temp = *head;
if (temp == delete_node)
{
*head = (*head)->next;
free(temp);
}
else
{
while (temp->next != NULL && temp->next != delete_node)
temp = temp->next;
if (temp->next != NULL && delete_node != NULL)
{
temp->next = delete_node->next;
free(delete_node);
}
}
}
}
Hope this works for you, this code isn't tested, but tell me if you have troubles!

Related

Optimizing the popBack method for your own list implementation

I am writing my own implementation of a single linked list. I wrote a popBack() method, but it seems to me that it is too overloaded with conditional operators, which should affect performance (and in general, it looks so-so). Is it possible to optimize it somehow so that it does not look so terrible?
void popBack() {
if (head == nullptr) {
return;
}
if (head->next == nullptr) {
delete head;
head = nullptr;
}
else {
Node* prev = nullptr;
Node* curr = head;
while(curr->next != nullptr) {
prev = curr;
curr = curr->next;
}
if(prev != nullptr) {
delete curr;
prev->next = nullptr;
}
}
size--;
}
The 2nd if block is not necessary. Also, the else block can be simplified if you use a Node** pointer to point at the Node* variable which needs to be null'ed when the Node object it is pointing at is delete'd, eg:
void popBack() {
if (!head) return;
Node *curr = head, **prev = &head;
while (curr->next) {
prev = &(curr->next);
curr = *prev;
}
*prev = nullptr;
delete curr;
--size;
}
Of course, if you really want to optimize the popping, you should implement a double-linked list instead, and add a tail member to your list class to point at the last Node in the list, then you won't need a while loop to hunt for the trailing Nodes in the list, eg:
void popBack() {
if (!tail) return;
Node *curr = tail;
tail = tail->previous;
Node **n = (tail) ? &(tail->next) : &head;
*n = nullptr;
delete curr;
--size;
}
One technique that can clean the code up a lot is to ensure the list always includes one node that you basically treat as empty. You never use the value it contains, but it eliminates many of the corner cases, so most of the code never had to deal with them.
Using this, pop_back can end up something like this:
void pop_back() {
if (head->next == nullptr)
return; // empty list--nothing to do.
// traverse to just before the sentinel node:
node* loc;
for (loc = head; loc->next->next != nullptr; loc = loc->next)
;
// at this point, `loc` points to the last "real" node in the list
// delete the old sentinel node
// then turn this into the sentinel node by setting its next pointer
// to a null pointer:
delete loc->next;
loc->next = nullptr;
}
Of course, to make this work, we have to assure the list always contains the sentinel node:
LinkedList() {
// node with its next pointer set to a null pointer:
head = new node(nullptr);
}
...and (of course) push_back has to be aware of the sentinel as well:
void push_back(T t) {
if (head->next == nullptr) {
head = new node(t, head);
return;
}
node* loc;
for (loc = head; loc->next->next != nullptr; loc = loc->next)
;
loc->next = new node(t, loc->next);
}
We can also carry this a step further by saving the location of the sentinel node. Although we still need to traverse the list to do a pop_back, we can avoid it when we want to do a push_back (we put the new value into the current sentinel node, then allocate a new sentinel node, and update our tail pointer to its location.
This general approach is, however, restricted to types that we don't mind creating a default-constructed item that we don't (currently) use. In my experience that's true a lot more often than not, but there are exceptions for which this design isn't suitable.

Deleting node in linked list causes output of seemingly random nubmers

I am trying to delete a node from a linked list using this function:
void del_node(int del_data)
{
node* temp = NULL;
node* trail = NULL;
node* del_ptr = NULL;
temp = head;
trail = head;
while (temp != NULL && temp->data != del_data)
{
trail = temp;
temp = temp->next;
}
if (temp != NULL) {
del_ptr = temp;
temp = temp->next;
trail->next = temp;
delete(del_ptr);
}
}
It seems like it deletes it fine until i print the linked list using this:
void print()
{
node* temp = NULL;
temp = head;
while (temp != NULL)
{
cout << temp->data << " ";
temp = temp->next;
}
cout << endl;
}
and it starts outputting seemingly random numbers, can anybody help me with this, really confused as this code comes from a tutorial.
Your algorithm doesn't manage the head pointer correctly whatsoever. Any changes that ultimately should modify the head pointer don't, and that's a huge problem. A pointer to pointer algorithm not only solves this problem, it also delivers a considerably more succinct solution:
void del_node(int del_data)
{
struct node **pp = &head;
while (*pp && (*pp)->data != del_data)
pp = &(*pp)->next;
if (*pp)
{
node *tmp = *pp;
*pp = tmp->next;
delete tmp;
}
}
This will work for any list condition including:
An empty list. i.e. head is null.
A single-node list. If the value matches head->data it will properly delete and reset the node pointer.
A multi-node list. The first matching node will be removed, and it will properly fix up the head node pointer if that was the matching location.
All of the above, in cases where there is no matching node, the list remains unchanged.
Fulfilling all of that in such a short algorithm + implementation is beneficial.
I'll comment on your code inline:
void del_node(int del_data)
{
node* temp = NULL;
node* trail = NULL;
node* del_ptr = NULL;
temp = head;
trail = head;
// This is fine, but recommend you use nullptr instead of NULL.
// This will find the first instance of data matches del_data,
// But if you are trying to delete all instances of del_data,
// You'll need to do this a little differently.
while (temp != NULL && temp->data != del_data)
{
trail = temp;
temp = temp->next;
}
// This if is fine, but see previous comment about using nullptr
// instead of NULL.
if (temp != NULL) {
del_ptr = temp;
temp = temp->next;
// Problematic: What if trail is null?
trail->next = temp;
delete(del_ptr);
}
}
Your code isn't bad. I wouldn't have written exactly like this, but I'm going to replace your if-statement:
if (temp != nullptr) {
// If trail is nullptr, then we're deleting from the head
if (trail == nullptr) {
head = temp->next;
}
else {
trail->next = temp->next;
}
delete(temp);
}
There's no need for the temporary. Just point around temp as you see in the if-else block and then delete temp.

Pointer in deleting a node in single linked list

In place of *head_ref = temp->next;, why can't I assign it as *head_ref = *head_ref->next?
Why should I use temp? Aren't they pointing to the same place?
class Node{
public:
int data;
Node* next;
};
void deleteNode(Node** head_ref, int key){
Node* temp = *head_ref;
Node* prev = NULL;
if(temp!=NULL && temp->data==key){
*head_ref = temp->next;
delete temp;
return;
}
else{
while(temp!=NULL && *head_ref->data!=key){
prev = temp;
temp = temp->next;
}
}
Your code does not compile, *head_ref->data should be (*head_ref)->data.
The reason why you should use temp is that you want to modify *head_ref only if the element you want to delete is the head element. If you delete any other element of the list, the head pointer must stay the same.
But your code is wrong anyway. You're doing things in the wrong order. You must first find the element you want to delete, and then handle the deletion.
Your code handles the deletion first and then finds the element to delete which is absurd.
You want this:
void deleteNode(Node** head_ref, int key) {
Node* current = *head_ref;
Node* previous = NULL;
// find element to delete
while (current && current->data != key)
{
previous = current;
current = current->next;
}
// if current is NULL here then the element has not been found
if (current != NULL)
{
// element found,
// current points to element found
// previous points to previous element or NULL if current is head
if (previous == NULL)
{
// deleting head element -> we need to update head_ref
*head_ref = current->next;
}
else
{
// deleting any other element -> update next pointer of previous element
previous->next = current->next;
}
delete current;
}
}
That being said, this is rather C code than C++ code. You should use standard containers rather than making your own, or at least use C++ idioms such as constructors.

insert at end of linked list

I am writing a simple function to insert at the end of a linked list on C++, but finally it only shows the first data. I can't figure what's wrong. This is the function:
node* Insert(node* head, int data)
{
if (head == NULL) {
head = new node();
head->data = data;
head->link = NULL;
return head;
}
else {
node* temp = head;
while (temp != NULL) {
temp = temp->link;
}
node* temp2 = new node();
temp2->data = data;
temp2->link = NULL;
(temp->link) = temp2;
return head;
}
}
Change the condition in while construct from:
while (temp!=NULL) {
temp=temp->link;
}
To
while (temp->link!=NULL) {
temp=temp->link;
}
In statement, temp->link = temp2, temp is a null pointer. You were dereferencing a NULL pointer.
To append a node at the back, temp pointer should point to the last node of the linked list. So, in the while loop, you need to just stop linked list traversal when you have reached the last node, i.e, the node whose link member points to nothing (has NULL). while (temp->link!=NULL) will stop at the last node as last node will have link member pointing to NULL.
You can simplify your logic by doing this:
void Insert(node **pnode, int data)
{
while (*pnode) {
pnode = &(*pnode)->link;
}
*pnode = new node(data, NULL);
}
assuming you have a node constructor that initializes data and link from arguments.
Instead of calling it as
head = Insert(head, 42);
you'd now do
Insert(&head, 42);
change while(temp!=NULL) to while(temp->link!=NULL)
node* Insert(node* head, int data)
{
if (head == NULL) {
head = new node();
}
else {
while (head->link != NULL) {
head = head->link;
}
head = head->link = new node();
}
head->data = data;
head->link = NULL;
return head;
}

C++ delete element function in a doubly linked list

I'm fairly new to C++ and it's data structures. I've made a few functions for my Doubly Linked List Class but i'm having trouble in the delete functions specifically. I've made a deleteHead() function which delete's the first node in the list, a deleteTail() function which deletes the last node in the list and finally a deleteElement(Item) function which goes over the list and deletes the node which has that the Item in it.
Problem is that whenever I try to delete the only remaining node from the list, the program crashes. i.e If I insert a node and then I call the deleteElement function on the same node (and if the node I inserted is the only node in the list) the program crashes.
here's my code.
template <class T>
void LinkedList<T>::deleteElement(T item)
{
ListItem<T>* current = head;
ListItem<T>* temp;
if (head == NULL) { // if list is empty end the function
return;
}
while (current) {
if (current->value == item) {
if (current == head) { // if item is in the head
deleteHead();
}
else if (current == getTail()) { // if item is in the tail
deleteTail();
}
// if item is between two nodes
else if (current != head && current != getTail()) {
temp = current;
temp->prev->next = temp->next;
temp->next->prev = temp->prev;
current->next = NULL;
current->prev = NULL;
if (current->next == NULL) {
deleteTail();
}
}
}
current = current->next;
}
}
// Delete the Head Node
template <class T>
void LinkedList<T>::deleteHead()
{
if (head != NULL) { // as long as head is not null, delete head
ListItem<T> *current = head;
head = head->next;
current->next = NULL;
head->prev = NULL;
} else {return;} // if head is null, end the function without doing anything
}
// Delete Tail Node
template <class T>
void LinkedList<T>::deleteTail()
{
ListItem<T> *tail = getTail();
tail = tail->prev;
tail->next->prev = NULL;
tail->next = NULL;
}
There are several issues in your code. First of all, you're not checking any pointers before you access them. For example, here:
temp = current;
temp->prev->next = temp->next;
temp->next->prev = temp->prev;
If there's only one element in the list, then its next and prev members will presumably be null. That means trying to assign something to temp->prev->next or temp->next->prev will be an access violation.
The same applies elsewhere, including this line at the end of your while loop:
current = current->next;
At that stage, current could hypothetically have been deleted, meaning trying to access its next member should fail. It probably won't fail at the moment though because of another problem...
Very importantly, you're not actually deleting anything. C++ doesn't have a garbage collector, so you can't just set a raw pointer to null and forget about it. You have to call delete on it to destroy and deallocate the object, otherwise it causes a memory leak. You'll need to fix that throughout your code, and then probably revise all your functions. You need to be very careful not to access an object after it's deleted.
With that said, instead of using raw pointers, you should really use smart pointers (i.e. std::shared_ptr). They take care of deleting the object for you when nothing else has a pointer to it. You still need to avoid accessing a deleted object, but it makes coding simpler, and hopefully more modern/robust.
Suggestions:
Step #1 - Add member variable ListItem<T>* tail to class ListItem<T>.
Step #2 - Replace all calls to getTail() with tail.
Step #3 - In function deleteElement, you can replace:
else if (current != head && current != tail)
with:
else
Step #4 - In function deleteElement, you should replace:
current->next = NULL;
current->prev = NULL;
if (current->next == NULL)
deleteTail();
with:
delete current;
Step #5 - Rewrite function deleteHead as follows:
ListItem<T>* current = head;
if (head == tail)
head = tail = NULL;
else
head = head->next;
delete current;
Step #6 - Rewrite function deleteTail as follows:
ListItem<T>* current = tail;
if (tail == head)
tail = head = NULL;
else
tail = tail->prev;
delete current;
Got the answer right. This was a question in my Data Structures assignment. Here's the code I implemented. Yes, i'm aware it has redundancies and inefficiencies but I had to work within the framework of the assignment.
This is my deleteElement() Function:
template <class T>
void LinkedList<T>::deleteElement(T item)
{
ListItem<T>* current = head;
ListItem<T>* temp;
if (head == NULL) { // if list is empty end the function
return;
}
while (current) {
if (current->value == item) {
if (current == head) { // if item is in the head
deleteHead();
}
else if (current == getTail()) { // if item is in the tail
deleteTail();
}
else if (current != head && current != getTail()) {
// if item is between two nodes
temp = current;
temp->prev->next = temp->next;
temp->next->prev = temp->prev;
current->next = NULL;
current->prev = NULL;
if (current->next == NULL) {
deleteTail();
}
}
}
current = current->next;
}
}