Can we delete the last node of a Single Linked list if we only know the address of last node - singly-linked-list

// Variables
typedef struct node
{
int value;
struct node *next;
}mynode;
// Globals (not required, though).
mynode *head, *tail, *temp;
// Functions
void add(int value);
// Function to add new nodes to the linked list
void add(int value)
{
temp = (mynode *) malloc(sizeof(struct node));
temp->next=(mynode *)0;
temp->value=value;
if(head==(mynode *)0)
{
head=temp;
tail=temp;
}
else
{
tail->next=temp;
tail=temp;
}
}
// The main() function
int main()
{
head=(mynode *)0;
// Construct the linked list.
add(1);
add(2);
add(3);
return(0);
}
If I only have a pointer to a node, whose value is 3(The Last node as seen in the aforementioned code) , Can we delete it and make a node whose value is 2(aforementioned code) as the last node.

No you can not. Unless you have some reference to previous node. like head pointer. If you have other reference than its pretty much easier. In fact if you don't have any pointers you will loose the list itself

No, but if you know what you are doing, you can modify the last node in-place. Deleting the last node requires access to the second-to-last node, and specifically its link to the last node.

The answer is no.
You can call free on that pointer to the last node, but that just means that the memory occupied by that node is no longer claimed. The data will most likely stay there unchanged for a while. And that means that the next-to-last node's pointer to it is still valid, even though it should not be.
To delete the node in a way that is meaningful to the list, that pointer contained in the next-to-last node has to be nullified. And that can't be done unless that next-to-last node can be accessed, either by a direct pointer to it, or by traversing the list from a preceding node.

You can use a doubly linked list to access the previous node. Or iterate through the entire list.

Yes you can.. Try the following code:
void deleteNode()
{
mynode *temp1;
for(temp1 = head; temp->next!= tail; temp1 = temp1->next);
tail = temp1;
free(tail->next);
}
It will delete the last node.

Related

Pointer to pointer assignment and dereference in C++

Let's say I have a linked list node like the following:
struct ListNode {
int val;
ListNode *next;
ListNode(int x) : val(x), next(NULL) {}
};
The goal is to write a function to delete a node in a singly-linked list. One efficient way to do it in constant time is something like this:
void deleteNode(ListNode* node) {
auto *tmp = node->next;
*node = *tmp;
delete tmp;
}
This works, but why do we need to dereference the pointers?
If node is a pointer and tmp is a pointer, why does it need to dereferenced? Why can't I do node = tmp?
When performing *node=*tmp you copy all the bytes of *tmp into *node thus
node->val now holds tmp->val and node->next now holds tmp->next.
The old content of node has been forgotten (it's normal since you want to get rid of this node) but you saved the content of the next node at this same place.
Then if you delete the next node (known through tmp) you don't lose its content (it has been saved in the previous node).
Let's break down the three lines of your deleteNode function:
auto *tmp = node->next;
This creates a local variable, tmp which will be a copy of the next field of the passed node parameter. This is a pointer to the next structure in the list and, once we've made a copy of it, we can erase or overwrite that member.
*node = *tmp;
This copies the actual data of the structure pointed to by tmp (that is, the next node in the list) to the current node, overwriting the next field as it does so. We need to dereference both pointers in order to copy the values of the structures pointed to.
delete tmp;
This deletes the 'next' node in the given list. However, we have already copied all its data (including its next member) into our current node, so our modified list now starts with (effectively) the second one in the original list (the passed parameter); notably, the next field of *node will now be the address originally stored in node->next->next – thus, we have 'skipped' an entry (the second) in the list and deleted it.
The reason you can't just write node = tmp is because that wouldn't change anything outside of your function.
Given this linked list
node0 -> node1 -> node2 -> node3
If you want to delete node1, the desired outcome would be
node0 -> node2 -> node3
If you don't want to actively modify the pointer value (that is, the address next) in node0, you have to move the value inside node2 to where node1 was.
Why can't I do node = tmp ?
You can do that, but it won't do anything useful. node is a local variable in deleteNode. As it is a pointer you can use that local pointer to modify what it points to, but modfying the pointer itself has no effect outside of the function.
Actually pointers are not different with respect to that. You also cannot observe any effect from outside when you have
void foo(int x) {
x = 42;
}
Passing a reference is different:
void bar(int& x) {
x = 42;
}
int a = 0;
bar(a); // now a == 42
Same with pointers:
void bar_ptr(int*& x) {
x = nullptr;
}
int* b = &a;
bar_ptr(b); // now b == nullptr
If you do node = tmp and after that delete tmp you will be deleting the ListNode, which node points to.
As others pointed out, node = tmp just changes the argument(local variable)
*node = *tmp is to copy the content of ListNode which is equivalent to
node.val = tmp.val; node.next = tmp.next
This function actually removes the next element - it works, but it invalidates the next pointer(if there was something that refers node->next as a pointer, it is now a dangling pointer)
What your function really does, is that it doesn't delete the node from the parameter, but the next node, overwriting the current node with the follower.
The dereferencing of the pointer acts like a memcpy() and moves the data from the next node to the current. You are not copying the pointers but the data it points to.
This way you can repeatedly call the function with the same node pointer, and it will move down the chain.
However, since you are not checking the pointer, the last node probably has a NULL pointer and will crash on derefencing.
So you need to do
if (tmp)
*node = *tmp;
Example:
typedef struct list
{
struct list *next;
int value;
} List;
void deleteNext(List* node)
{
auto *tmp = node->next;
if(tmp)
*node = *tmp;
delete tmp;
}
int main(int argc, char *argv[])
{
List *l0 = new List;
List *l1 = new List;
l0->value = 0;
l0->next = l1;
l1->value = 1;
l1->next = NULL;
deleteNext(l0);
deleteNext(l0); // Without the 'if' it will crash here.
return 0;
}
but why do we need to dereference the pointers?
Let's explore what happens if we don't indirect through the pointers:
auto *tmp = node->next;
node = tmp;
delete tmp;
This would be equivalent to just
delete node->next;
// resulting structure
previous node next (deleted) next next (leaked)
1---------->2----dangling--->_ 4
// desired structure that we get from the correct code
previous node next (deleted) next next
_
1-----------3---------------------------------->4
So, we end up with wrong node being deleted, and with a dangling pointer in the node that was supposed to be deleted.
Note that even the correct inirecting version is broken when attempting to delete the last node.

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

Deleting a linked list with specified data recursively

I want to delete a linked list recursively. I figured how to do this iteratively but I'm curious on how to do this. So far I have:
void deleteNodeRecursively(LinkedList *list, int value){
Node *curr=list->head;
if (list->head==NULL){
return;
}
else if (list->head->data==value){
Node *x=list->head->next;
delete list->head;
list->head=x;
}
else{
LinkedList *newlist;
newlist->head=list->head->next;
deleteNodeRecursively(newlist,value);
}
}
Where I defined
struct LinkedList{
Node *head;
};
struct Node{
int data;
Node *next;
};
I can remove the head if need be, but I can't figure out how to remove the body or tails and then correctly stitch up the list, let alone do it recursively. How do I proceed? Why won't this work?
EDIT: Removed question marks and replaced with code that I thought would work.
Assuming you have a "correct" constructor and destructor for your Node data.
You would have to track address of the deletion, for which you could pass a double pointer or a reference to pointer.
void deleteNodeRecursively(Node** list, int value){
// ^^^ double pointer to track address withing recursive call
Node *curr= *list ;
if (curr ==NULL){ // Base case for recursion
return;
}
else if ( curr->data==value){ // If node to be deleted is found
*list = curr->next; // Update the address for recursive calls
delete curr; // Delete this current "got" node
}
// Else simple recurse into
deleteNodeRecursively( &(*list)->next, value );
}
Note: This implementation will delete all nodes with data that matches value .

Deleting first node of linked list (C++)

I want to know whether this code deletes the first node correctly or should I necessarily pass list's head as a pointer?
void List::deleteFirst()
{
temp = head;
head = head->next;
delete temp;
}
This is the class List
class List
{
private:
struct node
{
int data;
node * next;
};
node * head;
node * curr;
node * temp;
public:
//List();
//void AddNode(int addData);
//void DeleteNode(int delData);
void deleteFirst();
//void PrintList();
};
This will work, bit only if:
Your Nodes are allocated using new
You ensure that head always points to a valid node (the list is not empty).
Otherwise, you will cause undefined behavior.
But you really shouldn't store temp as a member variable, it should be a local variable instead.
The same goes for the curr variable, make sure it really needs to be a member.
Also, if you delete an object (e.g. your node), all remaining pointers to it become invalid, so be careful that you don't try to access it afterwards, for ex. through the curr* pointer.
Yup that will delete the the data pointed to by the original value of head.

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.