13.4. Delete nodes in a linked list#

In the previous section, we have been dynamically allocating memory for nodes and never deleting them. This is a problem because we are wasting memory. We can fix this by deleting nodes when we are done with them. In this section, we will implement functions that help us delete nodes in a linked list.

Generally, to delete a node, we need to do the following:

  1. Find the node we want to delete

  2. Fix the links before and after the node we want to delete

  3. free the dynamically allocated memory for the node we want to delete

  4. (if necessary) Update the head of the list

13.4.1. Deleting a node at the beginning/front of the list#

To delete a node at the beginning of the list, we need to do the following:

  1. Declare a pointer to the second node in the linked list

  2. free the memory allocated for the first node pointed to by the head pointer of the list

  3. Update the head pointer to point to the second node in the list

delete at front-image

To implement this function, we don’t need to return anything, and we only pass the LinkedList*. We can do the following:

Code

delete at front-code

We check for if the list is empty or not, because we may not have any nodes in the list. This will cause a segmentation fault if we access list->head->next when the list is empty. If the list is not empty, we can safely delete the first node in the list. Otherwise, we do nothing.

The function above works if we have only one node. newHead will have NULL and eventually list->head will be set to newHead which is NULL. This will cause the list to be empty.

13.4.2. Deleting a node at the end of the list#

To delete a node at the end of the list, we need to do the following:

  1. Find the second to last node in the list

  2. free the memory allocated for the last node in the list

  3. Set the next of the second to last node to NULL

The following figure illustrates the steps above:

delete back image

Fig. 13.6 Illustration of deleting the last node in a linked list. Step 1 is to find the second last node. Step 2 is to free the space allocated for the last node. Step 3 is to set the next of the second last node to NULL making it the last node.#

To implement this function, we don’t need to return anything, and we only pass the LinkedList*. To identify the last node, we previously said that if current->next == NULL, then current is pointing at the last node. If current->next->next == NULL, then current is pointing at the second last node. We can do the following to delete the last node in the list:

Code

delete back code

However, if the code above can work if we have two nodes in the list, otherwise current->next->next will cause a segmentation fault. We can fix this by checking if the list is empty or if it has one node before checking current->next->next.

If the list is empty, we can return as there is nothing to delete.

If the list has one node, we can delete that one node by calling deleteFront(list).

The following code implements the above logic. Download deleteBack.c if you want to play with the code.

 1void deleteBack(LinkedList *list) {
 2  if (list->head == NULL) {
 3    // The list is empty, there is nothing to delete.
 4    return;
 5  }
 6
 7  if (list->head->next == NULL) {
 8    // There is only one node in this list.
 9    deleteFront(list);
10    return;
11  }
12
13  Node *current = list->head;
14
15  // Traverse the list until we reach the second last element.
16  // Thanks to the previous if-statement, we know that list->head->next is
17  // non-NULL.
18  while (current->next->next != NULL) {
19    current = current->next;
20  }
21
22  // current now points to the second last element of the list.
23  free(current->next);  // Delete the last element of the list.
24  current->next = NULL; // The second last element is now the last element.
25}

In lines \(2 - 5\), we check if the list is empty. If it is, we return as there is nothing to delete.

In lines \(7 - 11\), we check if the list has one node. If it does, we call deleteFront(list) to delete the only node in the list.

13.4.3. Deleting all nodes in the linked list#

To delete all nodes in the linked list, we need to call deleteFront (or deleteBack) until the list is empty.

We implement a function named deleteAllNodes that deletes all nodes and returns the number of nodes deleted. It takes LinkedList* as a parameter. We implement it as follows:

Code

int deleteAllNodes(LinkedList *list) {
  int numDeleted = 0;

  while (list->head != NULL) {
    deleteFront(list);
    numDeleted++;
  }

  // The list is now empty. Optionally, set it to NULL to confirm.
  // deleteFront already sets list->head to NULL.
  list->head = NULL;

  return numDeleted;
}

13.4.4. Delete the first matching node with data = searchVal#

To delete a node with a specific value, e.g, \(3\) we need to do the following:

  1. Find the node with the specified data

  2. Link the previous node to the next node after the node with specified data

  3. free the space dynamically allocated for the node with the specified data

The following figure illustrates the steps above:

delete first match order

To find the node with the specified data, we can traverse the linked list using a Node* named current. If current stops at the node with the specified data, we can delete it, but we won’t be able to go back to the previous node, and link it’s next to the next of the node we want to delete. To solve this problem, we can make current stop at the node before the node we want to delete.

Given that current stopped at the node before the node we want to delete, and we were to delete the current->next node, we won’t be able to access current->next->next, since current->next is freed. To solve this problem, we can store the address of the next node after current in a Node* named temp. Then, first link current->next to temp->next, then delete the node pointed to by temp. We can’t link current->next to temp->next if we deleted/freed temp first, since we won’t be able to access the next of the freed node temp.

This means that the order of steps is crucial. We should do the steps in the following order:

  1. Store the address of current->next in temp

  2. Connect current->next to temp->next

  3. free the node pointed to by temp

The steps are illustrated in the following figure:

delete first match image

We can write a function named deleteFirstMatch that takes the LinkedList* and the searchVal as parameters to implement the steps above. It returns true if it found the node and deleted it, and false otherwise. A draft is shown below:

delete first match code

Special case 1: What if the node was not found? If the node was not found, there is a possibility of segmentation fault when checking the while loop condition. current will traverse the list till the end current->next == NULL, and checking current->next->data will cause a segmentation fault. To solve this, we can check current->next != NULL before checking the data in the next node after current, as we show in line \(5\).

 1bool deleteFirstMatch(LinkedList *list, int searchVal) {
 2  // Search for a node that matches the value, but maintain a pointer to the
 3  // node just before it.
 4  Node *current = list->head;
 5  while (current->next != NULL && current->next->data != searchVal) {
 6    current = current->next;
 7  }
 8
 9  // current now points to a node just before the node that matched, 
10  // OR current points to the last node.
11  if (current->next != NULL) {
12    // current does not point to the last node.
13    Node *temp = current->next; // temp is the node we must delete.
14    current->next = temp->next; // Update n so that temp is no longer linked.
15    free(temp);
16
17    return true;
18  }
19
20  return false;
21}

We exit the loop if current is pointing to the node before the node to be deleted, or if current is pointing at the last node and the node with searchVal is not found. In the first case, current->next is not NULL, and current->next->data is equal to searchVal, we can delete the node at current->next as in lines \(11\) to \(17\). In the second case, current->next is NULL, the node was not found, and we should return false as in line \(20\) to indicate that the node was not found.

Special case 2: What if the node to be deleted is the first node? The present code starts checking data of nodes starting from the second node. If the node to be deleted is the first node, we can call deleteFront(list) to delete it. We show this in lines \(2\) to \(6\).

 1bool deleteFirstMatch(LinkedList *list, int searchVal) {
 2  if (list->head->data == searchVal) {
 3    // The first node matches the value.
 4    deleteFront(list);
 5    return true;
 6  }
 7
 8  // Search for a node that matches the value, but maintain a pointer to the
 9  // node just before it.
10  Node *current = list->head;
11  while (current->next != NULL && current->next->data != searchVal) {
12    current = current->next;
13  }
14
15  // current now points to a node just before the node that matched, 
16  // OR current points to the last node.
17  if (current->next != NULL) {
18    // current does not point to the last node.
19    Node *temp = current->next; // temp is the node we must delete.
20    current->next = temp->next; // Update n so that temp is no longer linked.
21    free(temp);
22
23    return true;
24  }
25
26  return false;
27}

Special case 3: What if the linked list is empty? If the linked list is empty, there is nothing to delete, and we can return false as we show in lines \(2\) to \(5\).

 1bool deleteFirstMatch(LinkedList *list, int searchVal) {
 2  if (list->head == NULL) {
 3    // There is nothing to do in an empty list.
 4    return false;
 5  }
 6
 7  if (list->head->data == searchVal) {
 8    // The first node matches the value.
 9
10    deleteFront(list);
11    return true;
12  }
13
14  // Search for a node that matches the value, but maintain a pointer to the
15  // node just before it.
16  Node *current = list->head;
17  while (current->next != NULL && current->next->data != searchVal) {
18    current = current->next;
19  }
20
21  // current now points to a node just before the node that matched, 
22  // OR current points to the last node.
23  if (current->next != NULL) {
24    // current does not point to the last node.
25    Node *temp = current->next; // temp is the node we must delete.
26    current->next = temp->next; // Update n so that temp is no longer linked.
27    free(temp);
28
29    return true;
30  }
31
32  return false;
33}

Special case 4: What if the node to be deleted is the last node? current will point to the second last node before the node to be deleted. In this case, our code will perfectly work to delete the last node by freeing temp and setting current->next to NULL.

Download deleteFirstMatch.c if you want to play with the code we developed for deleteFirstMatch function.

13.4.5. Delete all matching nodes with data = searchVal#

In the previous section, we deleted only the first matching node in a linked list. In this section, we develop a function that deletes all the nodes in a linked list that have data equal to searchVal.

The steps to do so include:

  1. Delete first match of searchVal using deleteFirstMatch function

  2. Repeat 1 till there is no more matches

How can we check for the condition of no more matches? We can use a while loop that checks if deleteFirstMatch returns true or false. If it returns true, it means that a node was deleted, and we should repeat the process. If it returns false, it means that no node was deleted, and we should exit the loop.

In the body of the loop, we can increment a counter than counts the number of matches deleted. The following code shows the implementation of deleteAllMatches function.

int deleteAllMatches(LinkedList *list, int value) {
  int numDeleted = 0;

  while (deleteFirstMatch(list, value)) {
    numDeleted++;
  }

  return numDeleted;
}

Quiz

0 Questions

X