Overview
Single linked lists are a versatile data structure commonly used in computer science and programming. In this article, we will explore different approaches to implement single linked lists in C++. Each method provides a unique perspective on how to manage linked data structures, giving you the flexibility to choose the one that best suits your needs. By the end, you will have a solid understanding of how to use and manipulate single linked lists in your C++ programs.
What is a Single Linked List?
A single linked list is a linear data structure consisting of a sequence of elements called nodes. Each node contains two parts: the data or value it holds and a reference (a pointer) to the next node in the list. The last node typically points to a null reference, indicating the end of the list. This linked structure allows for efficient insertions and deletions.
Implementation of Single Linked Lists
Method 1: Using Structs
One of the most straightforward ways to create a single linked list in C++ is by using structs to define nodes and a class to manage the list.
#include <iostream>
// Define the Node structure
struct Node {
int data;
Node* next;
};
// Define the Linked List class
class LinkedList {
public:
LinkedList() : head(nullptr) {}
// Append a new element to the end of the list
void append(int value) {
Node* newNode = new Node{value, nullptr};
if (!head) {
head = newNode;
} else {
Node* current = head;
while (current->next) {
current = current->next;
}
current->next = newNode;
}
}
// Display the elements in the list
void display() {
Node* current = head;
while (current) {
std::cout << current->data << " -> ";
current = current->next;
}
std::cout << "nullptr" << std::endl;
}
private:
Node* head;
};
int main() {
LinkedList myList;
myList.append(1);
myList.append(2);
myList.append(3);
myList.display();
return 0;
}
Method 2: Using Custom Classes
Another way to implement a single linked list is by creating custom classes for nodes and the linked list. This approach provides more encapsulation and can be useful in complex scenarios.
#include <iostream>
// Define the Node class
class Node {
public:
int data;
Node* next;
Node(int value) : data(value), next(nullptr) {}
};
// Define the Linked List class
class LinkedList {
public:
LinkedList() : head(nullptr) {}
// Append a new element to the end of the list
void append(int value) {
Node* newNode = new Node(value);
if (!head) {
head = newNode;
} else {
Node* current = head;
while (current->next) {
current = current->next;
}
current->next = newNode;
}
}
// Display the elements in the list
void display() {
Node* current = head;
while (current) {
std::cout << current->data << " -> ";
current = current->next;
}
std::cout << "nullptr" << std::endl;
}
private:
Node* head;
};
int main() {
LinkedList myList;
myList.append(1);
myList.append(2);
myList.append(3);
myList.display();
return 0;
}
Method 3: Using Templates
C++ templates provide a way to create a generic linked list that can store elements of any data type. This approach is highly flexible and can be adapted for various use cases.
#include <iostream>
// Define the Node template
template <typename T>
struct Node {
T data;
Node* next;
Node(T value) : data(value), next(nullptr) {}
};
// Define the Linked List template
template <typename T>
class LinkedList {
public:
LinkedList() : head(nullptr) {}
// Append a new element to the end of the list
void append(T value) {
Node<T>* newNode = new Node<T>(value);
if (!head) {
head = newNode;
} else {
Node<T>* current = head;
while (current->next) {
current = current->next;
}
current->next = newNode;
}
}
// Display the elements in the list
void display() {
Node<T>* current = head;
while (current) {
std::cout << current->data << " -> ";
current = current->next;
}
std::cout << "nullptr" << std::endl;
}
private:
Node<T>* head;
};
int main() {
LinkedList<int> intList;
intList.append(1);
intList.append(2);
intList.append(3);
intList.display();
LinkedList<std::string> strList;
strList.append("Hello");
strList.append("World");
strList.display();
return 0;
}Single Linked List Operations and Their Complexities
Single linked lists support various operations, and it's important to understand their time complexities.
Insertion (at the beginning):
- Operation: insertAtBeginning(value)
- Time Complexity: O(1)
- Explanation: Adding a node at the beginning involves changing a few pointers.
Insertion (at the end):
- Operation: append(value)
- Time Complexity: O(n)
- Explanation: To add an element at the end, you need to traverse the entire list to find the last node, making it a linear-time operation.
Deletion (from the beginning):
- Operation: deleteFromBeginning()
- Time Complexity: O(1)
- Explanation: Removing the first element requires updating the head pointer.
Deletion (from a specific position):
- Operation: deleteAtPosition(position)
- Time Complexity: O(n)
- Explanation: Deleting a node at a specific position involves traversing the list to find the previous node.
Traversal:
- Operation: display()
- Time Complexity: O(n)
- Explanation: To display all elements, you need to traverse the entire list.
Searching:
- Operation: search(value)
- Time Complexity: O(n)
- Explanation: In the worst case, you may have to search through the entire list to find a specific value.
Below is the code implementation that includes common operations:
#include <iostream>
// Define the Node structure
struct Node {
int data;
Node* next;
Node(int value) : data(value), next(nullptr) {}
};
// Define the Linked List class
class LinkedList {
public:
LinkedList() : head(nullptr) {}
// Append a new element to the end of the list
void append(int value) {
Node* newNode = new Node(value);
if (!head) {
head = newNode;
} else {
Node* current = head;
while (current->next) {
current = current->next;
}
current->next = newNode;
}
}
// Insert a new element at the beginning of the list
void insertAtBeginning(int value) {
Node* newNode = new Node(value);
newNode->next = head;
head = newNode;
}
// Delete the first element from the list
void deleteFromBeginning() {
if (head) {
Node* temp = head;
head = head->next;
delete temp;
}
}
// Display the elements in the list
void display() {
Node* current = head;
while (current) {
std::cout << current->data << " -> ";
current = current->next;
}
std::cout << "nullptr" << std::endl;
}
// Search for a specific value in the list
bool search(int value) {
Node* current = head;
while (current) {
if (current->data == value) {
return true;
}
current = current->next;
}
return false;
}
};
int main() {
LinkedList myList;
myList.append(1);
myList.append(2);
myList.append(3);
std::cout << "Original List: ";
myList.display();
myList.insertAtBeginning(0);
std::cout << "After Insertion at Beginning: ";
myList.display();
myList.deleteFromBeginning();
std::cout << "After Deletion from Beginning: ";
myList.display();
int searchValue = 2;
if (myList.search(searchValue)) {
std::cout << searchValue << " found in the list." << std::endl;
} else {
std::cout << searchValue << " not found in the list." << std::endl;
}
return 0;
}
append: Adds an element at the end of the list.insertAtBeginning: Inserts an element at the beginning of the list.deleteFromBeginning: Removes the first element.display: Displays the elements in the list.search: Searches for a specific value in the list.
Difference between arrays and linked lists:
Memory Allocation:
- Arrays: Arrays are allocated in contiguous memory locations. The size of an array is fixed when it is declared.
- Linked Lists: Linked lists do not require contiguous memory. They are dynamically allocated as new elements are added, making them more flexible in terms of size.
Insertions and Deletions:
- Arrays: Insertions and deletions in an array can be slow because elements may need to be shifted to maintain the contiguous memory layout. This can result in a time complexity of O(n) in the worst case.
- Linked Lists: Linked lists excel at insertions and deletions, as they only require changing a few pointers. Insertions and deletions can be performed in O(1) time, assuming you have a reference to the element to be modified.
Access Time:
- Arrays: Accessing elements in an array is fast and has a constant time complexity of O(1), assuming you know the index.
- Linked Lists: Accessing elements in a linked list is slower because you must traverse the list from the beginning. The time complexity for access is O(n) in the worst case.
Dynamic Sizing:
- Arrays: Arrays have a fixed size, which makes dynamic resizing complex and potentially inefficient. You may need to allocate a new array, copy elements, and deallocate the old array.
- Linked Lists: Linked lists are dynamic in size and can easily accommodate the addition or removal of elements without resizing concerns.
Random Access:
- Arrays: Arrays support efficient random access to elements using indices.
- Linked Lists: Linked lists do not support efficient random access; you must traverse the list sequentially to access an element.

Join the discussion
Sign in with your account to post comments, reply to others, and participate in the conversation.
Login to Comment