217 lines
5.2 KiB
C++
217 lines
5.2 KiB
C++
/**
|
|
* @file main.cpp
|
|
* @brief Demonstrates the classic three-pointer algorithm for reversing a singly linked list.
|
|
*
|
|
* This example is intentionally small and self-contained.
|
|
* It is not meant to show that reversing linked lists is a common production task.
|
|
* Instead, it documents the interview pattern clearly enough that a reader unfamiliar
|
|
* with it can compile the program, run it, and inspect the output.
|
|
*/
|
|
|
|
#include <iostream>
|
|
#include <initializer_list>
|
|
|
|
/**
|
|
* @brief A minimal singly linked list node.
|
|
*
|
|
* Each node stores an integer value and a pointer to the next node.
|
|
* The last node in the list has @c next equal to @c nullptr.
|
|
*/
|
|
struct Node {
|
|
int value; ///< Payload stored in the node.
|
|
Node *next; ///< Pointer to the next node, or nullptr for the last node.
|
|
};
|
|
|
|
/**
|
|
* @brief Appends a new value to the end of the list.
|
|
*
|
|
* @param head Reference to the head pointer of the list.
|
|
* @param value Value to store in the new node.
|
|
*
|
|
* This helper is used only to build the demonstration list.
|
|
* It keeps the example simple and avoids using STL containers for the list itself,
|
|
* because the goal is to demonstrate raw pointer manipulation.
|
|
*/
|
|
void appendNode (Node *&head, int value) {
|
|
Node *node = new Node{value, nullptr};
|
|
|
|
if (head == nullptr) {
|
|
head = node;
|
|
return;
|
|
}
|
|
|
|
Node *current = head;
|
|
|
|
while (current->next != nullptr)
|
|
current = current->next;
|
|
|
|
current->next = node;
|
|
}
|
|
|
|
/**
|
|
* @brief Creates a linked list from an initializer list.
|
|
*
|
|
* @param values Values to insert into the list in the given order.
|
|
* @return Pointer to the first node of the created list.
|
|
*
|
|
* The caller owns the returned list and must release it with freeList().
|
|
*/
|
|
Node *createList (std::initializer_list<int> values) {
|
|
Node *head = nullptr;
|
|
|
|
for (int value : values)
|
|
appendNode (head, value);
|
|
|
|
return head;
|
|
}
|
|
|
|
/**
|
|
* @brief Prints the list without modifying it.
|
|
*
|
|
* @param head Pointer to the first node of the list.
|
|
*
|
|
* Output example:
|
|
* @code
|
|
* 1 -> 2 -> 3 -> 4 -> null
|
|
* @endcode
|
|
*/
|
|
void printList (const Node *head) {
|
|
const Node *current = head;
|
|
|
|
while (current != nullptr) {
|
|
std::cout << current->value << " -> ";
|
|
current = current->next;
|
|
}
|
|
|
|
std::cout << "null" << std::endl;
|
|
}
|
|
|
|
/**
|
|
* @brief Reverses a singly linked list in place.
|
|
*
|
|
* @param head Pointer to the first node of the original list.
|
|
* @return Pointer to the first node of the reversed list.
|
|
*
|
|
* This is the classic three-pointer interview algorithm.
|
|
*
|
|
* The three pointers are:
|
|
*
|
|
* - @c previous — the already reversed part of the list
|
|
* - @c current — the node we are processing right now
|
|
* - @c next — the original next node saved before we overwrite @c current->next
|
|
*
|
|
* Why @c next is necessary:
|
|
*
|
|
* In a singly linked list, each node only knows where the next node is.
|
|
* When we execute:
|
|
*
|
|
* @code
|
|
* current->next = previous;
|
|
* @endcode
|
|
*
|
|
* we destroy the original forward link.
|
|
* Without saving it first, the rest of the list would be lost.
|
|
*
|
|
* The algorithm works by moving one node at a time from the original forward chain
|
|
* into the reversed chain.
|
|
*
|
|
* Initial state:
|
|
*
|
|
* @code
|
|
* previous = null
|
|
* current = 1 -> 2 -> 3 -> 4 -> null
|
|
* @endcode
|
|
*
|
|
* After processing node 1:
|
|
*
|
|
* @code
|
|
* previous = 1 -> null
|
|
* current = 2 -> 3 -> 4 -> null
|
|
* @endcode
|
|
*
|
|
* After processing node 2:
|
|
*
|
|
* @code
|
|
* previous = 2 -> 1 -> null
|
|
* current = 3 -> 4 -> null
|
|
* @endcode
|
|
*
|
|
* When @c current becomes @c nullptr, @c previous points to the new head.
|
|
*
|
|
* Complexity:
|
|
*
|
|
* - Time: O(n), because each node is visited once.
|
|
* - Extra memory: O(1), because only a fixed number of pointers is used.
|
|
*/
|
|
Node *reverseList (Node *head) {
|
|
Node *previous = nullptr;
|
|
Node *current = head;
|
|
|
|
while (current != nullptr) {
|
|
/*
|
|
* Save the original next node before changing current->next.
|
|
* Without this line, the rest of the list would become unreachable.
|
|
*/
|
|
Node *next = current->next;
|
|
|
|
/*
|
|
* Reverse the direction of the link.
|
|
* The current node now points to the already reversed part.
|
|
*/
|
|
current->next = previous;
|
|
|
|
/*
|
|
* Move previous forward.
|
|
* The current node becomes the new head of the reversed part.
|
|
*/
|
|
previous = current;
|
|
|
|
/*
|
|
* Continue with the node that originally followed current.
|
|
*/
|
|
current = next;
|
|
}
|
|
|
|
return previous;
|
|
}
|
|
|
|
/**
|
|
* @brief Releases all nodes in the list.
|
|
*
|
|
* @param head Pointer to the first node of the list.
|
|
*
|
|
* This function is separated from the reversal and printing logic.
|
|
* It exists only because this example uses raw @c new to keep the node structure explicit.
|
|
*/
|
|
void freeList (Node *head) {
|
|
Node *current = head;
|
|
|
|
while (current != nullptr) {
|
|
Node *next = current->next;
|
|
delete current;
|
|
current = next;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief Program entry point.
|
|
*
|
|
* Builds a small list, prints it, reverses it, prints it again,
|
|
* and finally releases all allocated nodes.
|
|
*/
|
|
int main() {
|
|
Node *list = createList ({1, 2, 3, 4, 5});
|
|
|
|
std::cout << "Original list:" << std::endl;
|
|
printList (list);
|
|
|
|
list = reverseList (list);
|
|
|
|
std::cout << "\nReversed list:" << std::endl;
|
|
printList (list);
|
|
|
|
freeList (list);
|
|
|
|
return 0;
|
|
}
|