Move All Occurrences Of An Element To End In A Linked List In C++

Linked lists are a basic data structure in computer science as well as in programming languages that arise in almost all types of computer systems. It is different from an array as it is dynamic and efficient in the use of memory through combining Sequential nodes connected through pointers to from a sequence. A typical problem when using linked lists is how to change their element's position while preserving a certain order or structure.

In this blog post, we will discuss a classic linked list problem: All the occurrences of an element in a linked list to the end of the list. This problem is a common implementation of working with linked liscpp tutorialers , the order of elements and time complexity.

Suppose you have your linked list wherein you have multiple instances of your value and you want to sort them in ascending order to place them at the end of the linked list partially. The twist is that any element except the target must keep the same order as it has been in the initial permutation. Furthermore, in the case of repeating target element occurrences, these also must preserve their order. This makes the problem different from a simple reshuffling of nodes variously, as had been mentioned.

To make this idea more tangible, let me use an example. Suppose you are given a linked list:

Example

Input: 1 -> 2 -> 2 -> 3 -> 4 -> 2 -> 5
Target: 2
Output: 1 -> 3 -> 4 -> 5 -> 2 -> 2 -> 2

The list begins with a variety of elements; in fact, there are numerous twos in the list. At the end of the operation, all the 2's are shifted just before the last element of the list, leaving all other elements non-affected and remaining in their order 1, 3, 4, 5. The 2s that were moved also have their order intact.

This problem is popular in competitive programming and technical interviews as it checks the programmer their understanding of the particularities of working with linked liscpp tutorialers and knowing how to solve it quickly and correctly, even at first sight. It is also important in real-life applications where similar operations are to be performed on interrelated data and where re-ordering is needed, for example, ordering tasks or rearranging the structural components of dynamic data structures.

Problem Statement

The task can be formally described as follows:

Suppose that you are given a singly linked list and a value x that must be placed at the list's tail; rearrange items of the list in this way. The position of the other elements should not be altered, the target values should also remain unchanged as to their position.

For example:

Example

Input: 1 -> 4 -> 5 -> 4 -> 3, Target: 4 Output: 1 -> 5 -> 3 -> 4 -> 4
Input: 7 -> 7 -> 8 -> 7, Target: 7 Destination: 8 -> 7 -> 7 -> 7
Input: state 9 -> 1 -> 9 -> 2 -> 9 Target: 3 -> Output: 9 -> 1 -> 9 -> 2 -> 9 No transition because the target does not exist.

To achieve this, we must carefully handle a few key challenges associated with linked list manipulation:

Pointer Management: A linked list is somewhat different than an array because there is no direct index pointer; one must traverse the linked list by pointers to get to the data element. As these pointers are the only ways given by the authors to shift around the nodes, the proper control of these pointers is important so as not to lose the stored data or even make the list break.

Order Preservation: Therefore, maintaining the relative order of non-targets and targets also poses another level of difficulty. For example, in the output, 1 -> 3 -> 4 -> 4; For the input, 1 -> 4 -> 4 -> 3. 1 and 3 mustn't be interchanged. Similarly, 4 and 4 are not interchanged as 4 and 4 are.

Efficiency: One possible simple solution is to use another list or reiterate through the original to shift target elements. This, however, results in questionable performance given a large list; hence may not be ideal for a large list. The perfect solution should be able to go through the list just once or, in the best solution, at most twice.

Key Challenges

Swapping elements in a linked list or in general with pointers when seeking to post all occurrences of a given value is not easy. These challenges come mainly from the facts associated with the linked list type, in which a node pointer is used for node operations rather than an index, as in the case with arrays .

1. Pointer Management

The linked list does not support directly accessing the elements because and only directs the nodes through the pointers. In order to shift elements, we have to update the nexcpp tutorialer of the nodes such that that list remains intact. Malfunctions of pointers cause link breaks, circular references, or loss of data views necessary restrictions.

For example, when the node is moving to the tail of the list, it is required to first remove the node, then change the pointer of the previous node and make the node the new tail. The problem with managing such changes is that any churning at one level has the potential to affect other aspects of the list.

2. Order Preservation

The idea of preserving the relative order of non-target and target nodes also complicates things. For instance, if the input is 1 -> 2 -> 3 -> 2 -> 4 -> 2, the output must contain a sequence in which non-target nodes (1, 3, 4) have to remain in the same order as target nodes (2, 2, 2). This eliminates direct solutions to the problem, for instance, by grouping nodes in such a way that their flow does not matter.

3. Efficiency

If the algorithm is not efficient, for instance, by requiring the one-pass search of a list to find and relocate the target nodes, the time complexity is O(n^2). For more extensive linked lists this combing process turns out to be expensive computationally. The optimality of the solution means that its implementation must go through this list in O(n) time, affecting each node only once.

4. Edge Cases

Some considerations, like when the list is empty, the list is a single node, or the list is equal to all or none of the target values, must be solved. These cases give some additional information that can be useful to challenge the solution solution and define its applicability.

Code:

Example

#include <iostream>
using namespace std;
// Definition of a linked list node
struct Node {
    int data;
    Node* next;
    Node(int val) : data(val), next(nullptr) {}
};
// Function to move all occurrences of 'target' to the end of the linked list
Node* moveOccurrencesToEnd(Node* head, int target) {
    if (!head) return head;
    // Dummy nodes for two partitions: one for non-target and one for target
    Node *nonTargetDummy = new Node(0), *targetDummy = new Node(0);
    Node *nonTargetTail = nonTargetDummy, *targetTail = targetDummy;
    Node* current = head;
    // Traverse the list and partition nodes
    while (current) {
        if (current->data == target) {
            targetTail->next = current;
            targetTail = targetTail->next;
        } else {
            nonTargetTail->next = current;
            nonTargetTail = nonTargetTail->next;
        }
        current = current->next;
    }
    // Combine the two partitions
    nonTargetTail->next = targetDummy->next;
    targetTail->next = nullptr;
    Node* newHead = nonTargetDummy->next;
    // Free the dummy nodes
    delete nonTargetDummy;
    delete targetDummy;
    return newHead;
}
// Helper function to add a node to the end of the list
Node* addNode(Node* head, int data) {
    if (!head) return new Node(data);
    Node* current = head;
    while (current->next) current = current->next;
    current->next = new Node(data);
    return head;
}
// Helper function to print the linked list
void printList(Node* head) {
    while (head) {
        cout << head->data << " -> ";
        head = head->next;
    }
    cout << "nullptr\n";
}
// Helper function to free the linked list memory
void freeList(Node* head) {
    while (head) {
        Node* temp = head;
        head = head->next;
        delete temp;
    }
}
// Main function to test the program
int main() {
    Node* head = nullptr;
    // Create a linked list: 1 -> 2 -> 2 -> 3 -> 4 -> 2 -> 5
    head = addNode(head, 1);
    head = addNode(head, 2);
    head = addNode(head, 2);
    head = addNode(head, 3);
    head = addNode(head, 4);
    head = addNode(head, 2);
    head = addNode(head, 5);
    cout << "Original List:\n";
    printList(head);
    int target = 2;
    // Move all occurrences of the target to the end
    head = moveOccurrencesToEnd(head, target);
    cout << "\nList After Moving Target (" << target << ") to the End:\n";
    printList(head);
    // Free allocated memory
    freeList(head);
    return 0;
}

Input:

Example

Original list:
1 -> 2 -> 2 -> 3 -> 4 -> 2 -> 5 -> nullptr
Target: 2

Output:

Output

List after moving all occurrences of the target (2) to the end:
1 -> 3 -> 4 -> 5 -> 2 -> 2 -> 2 -> nullptr

Input Required

This code uses input(). Please provide values below: