Here’s the thing: once you understand how nodes connect and move, most linked list problems start to feel predictable.
Table of Contents
What is a Linked List?
A linked list is a linear data structure in which elements are stored in nodes, and each node contains data along with a pointer to the next node in the sequence.Unlike arrays, linked lists do not store elements in contiguous memory locations, which allows flexible memory usage.
Features of a Linked List
- Dynamic Size: A linked list can grow or shrink during runtime as memory is allocated when needed.
- Non-Contiguous Storage: The nodes are stored at different memory locations and are connected using pointers.
- Efficient Insertion and Deletion: Insertion and deletion are efficient because they only require updating pointers instead of shifting elements.
- Sequential Access: Elements are accessed one by one from the head node, which makes access slower than arrays.
- Extra Memory for Pointers: Each node requires additional memory to store the pointer to the next node.
Syntax and Declaration of Linked List
Before solving problems, you must clearly understand how to declare and create a linked list.Node Declaration (C++)
class Node {
public:
int data;
Node* next;
Node(int val) {
data = val;
next = NULL;
}
};
Linked List Declaration
This statement creates an empty linked list where head points to nothing initially.Node* head = NULL;
Example:
// C++ program to define a node structure and
// initialize an empty linkedlist
#include iostream
using namespace std;
class Node {
public:
int data;
Node* next;
Node(int val) {
data = val;
next = NULL;
}
};
int main() {
Node* head = NULL; // Empty linked list
cout "Linked List Initialized";
return 0;
}
Output:
Explanation:Linked List Initialized
The Node class represents a single element in the linked list, where each node contains data and a pointer to the next node. The head pointer is initialized to NULL, which indicates that the list is empty.
Basic Operations Overview
| Operation | Descriptio | Time Complexity |
|---|---|---|
| Insertion at Head | Insert node at beginning | O(1) |
| Insertion at Tail | Insert node at end | O(n) |
| Deletion | Remove node by value | O(n) |
| Traversal | Visit all nodes | O(n) |
Insertion Operation
You need to insert elements at the beginning and at the end of a linked list and display the result.
// C++ program to implement insertion
// in linkedlist
#include iostream
using namespace std;
class Node {
public:
int data;
Node* next;
Node(int val) {
data = val;
next = NULL;
}
};
// Insert at head
void insertAtHead(Node* &head, int val) {
Node* newNode = new Node(val);
newNode-next = head;
head = newNode;
}
// Insert at tail
void insertAtTail(Node* &head, int val) {
Node* newNode = new Node(val);
if (head == NULL) {
head = newNode;
return;
}
Node* temp = head;
while (temp-next != NULL) {
temp = temp-next;
}
temp-next = newNode;
}
// Display
void display(Node* head) {
Node* temp = head;
while (temp != NULL) {
cout temp-data " - ";
temp = temp-next;
}
cout "NULL" endl;
}
int main() {
Node* head = NULL;
insertAtHead(head, 2);
insertAtHead(head, 1);
insertAtTail(head, 3);
insertAtTail(head, 4);
display(head);
return 0;
}
Output:
1 - 2 - 3 - 4 - NULL
Explanation:
The insertAtHead function creates a new node and makes it the new head by linking it to the previous head. The insertAtTail function traverses the list until the last node and attaches the new node at the end. The display function prints all nodes sequentially.
Deletion Operation
You need to delete a node with a given value from the linked list.
// C++ program to delete a node with given value
#include iostream
using namespace std;
class Node {
public:
int data;
Node* next;
Node(int val) {
data = val;
next = NULL;
}
};
void deleteNode(Node* &head, int key) {
if (head == NULL) {
cout "List is empty" endl;
return;
}
if (head-data == key) {
Node* temp = head;
head = head-next;
delete temp;
return;
}
Node* temp = head;
while (temp-next != NULL && temp-next-data != key) {
temp = temp-next;
}
if (temp-next == NULL) {
cout "Value not found" endl;
return;
}
Node* toDelete = temp-next;
temp-next = temp-next-next;
delete toDelete;
}
void display(Node* head) {
while (head != NULL) {
cout head-data " - ";
head = head-next;
}
cout "NULL" endl;
}
int main() {
Node* head = new Node(1);
head-next = new Node(2);
head-next-next = new Node(3);
head-next-next-next = new Node(4);
deleteNode(head, 3);
display(head);
return 0;
}
Output:
Explanation:1 - 2 - 4 - NULL
The function first checks whether the list is empty. If the node to be deleted is the head, it updates the head pointer. Otherwise, it traverses the list to find the node and updates the pointer of the previous node to remove the target node.
Important Interview Patterns
| Pattern | Purpose | Example Use Case |
|---|---|---|
| Two-Pointer | Traverse at different speeds | Cycle detection |
| Reversal | Reverse node connections | Reverse list |
| Dummy Node | Simplify edge cases | Merge lists |
Problem 1: Reverse a Linked List
You need to reverse a singly linked list by changing the direction of node connections.
// C++ program to reverse a linkedlist
#include iostream
using namespace std;
class Node {
public:
int data;
Node* next;
Node(int val) {
data = val;
next = NULL;
}
};
Node* reverseList(Node* head) {
Node* prev = NULL;
Node* curr = head;
while (curr != NULL) {
Node* nextTemp = curr-next;
curr-next = prev;
prev = curr;
curr = nextTemp;
}
return prev;
}
void display(Node* head) {
while (head != NULL) {
cout head-data " - ";
head = head-next;
}
cout "NULL" endl;
}
int main() {
Node* head = new Node(1);
head-next = new Node(2);
head-next-next = new Node(3);
head = reverseList(head);
display(head);
return 0;
}
Output:
Explanation:3 - 2 - 1 - NULL
The algorithm uses three pointers: previous, current, and next. It reverses the direction of each link one by one until the entire list is reversed.
Problem 2: Detect Cycle
You need to determine whether a linked list contains a cycle.
// C++ program to detect a cycle
#include iostream
using namespace std;
class Node {
public:
int data;
Node* next;
Node(int val) {
data = val;
next = NULL;
}
};
bool hasCycle(Node* head) {
Node* slow = head;
Node* fast = head;
while (fast != NULL && fast-next != NULL) {
slow = slow-next;
fast = fast-next-next;
if (slow == fast) {
return true;
}
}
return false;
}
int main() {
Node* head = new Node(1);
head-next = new Node(2);
head-next-next = new Node(3);
head-next-next-next = head; // cycle
if (hasCycle(head)) {
cout "Cycle Detected";
} else {
cout "No Cycle";
}
return 0;
}
Output:
Explanation:Cycle Detected
The slow pointer moves one step at a time, while the fast pointer moves two steps. If a cycle exists, both pointers eventually meet.
Problem 3: Find Middle Node
You need to find the middle node of a linked list.
// C++ program to find the middle node
#include iostream
using namespace std;
class Node {
public:
int data;
Node* next;
Node(int val)
{
data = val;
next = NULL;
}
};
Node* findMiddle(Node* head)
{
Node* slow = head;
Node* fast = head;
while (fast != NULL && fast-next != NULL)
{
slow = slow-next;
fast = fast-next-next;
}
return slow;
}
// Main function
int main()
{
Node* head = new Node(1);
head-next = new Node(2);
head-next-next = new Node(3);
head-next-next-next = new Node(4);
Node* mid = findMiddle(head);
cout "Middle Node: " mid-data;
return 0;
}
Output:
Explanation:Middle Node: 3
The slow pointer reaches the middle when the fast pointer reaches the end of the list.
Problem 4: Merge Two Sorted Lists
You need to merge two sorted linked lists into a single sorted linked list.
// C++ program to merge two sorted
// linkedlists
#include iostream
using namespace std;
class Node {
public:
int data;
Node* next;
Node(int val) {
data = val;
next = NULL;
}
};
Node* mergeLists(Node* l1, Node* l2) {
Node dummy(0);
Node* tail = &dummy;
while (l1 != NULL && l2 != NULL) {
if (l1-data l2-data) {
tail-next = l1;
l1 = l1-next;
} else {
tail-next = l2;
l2 = l2-next;
}
tail = tail-next;
}
if (l1 != NULL) tail-next = l1;
if (l2 != NULL) tail-next = l2;
return dummy.next;
}
void display(Node* head) {
while (head != NULL) {
cout head-data " - ";
head = head-next;
}
cout "NULL" endl;
}
int main() {
Node* l1 = new Node(1);
l1-next = new Node(3);
Node* l2 = new Node(2);
l2-next = new Node(4);
Node* result = mergeLists(l1, l2);
display(result);
return 0;
}
Output:
Explanation:1 - 2 - 3 - 4 - NULL
A dummy node is used to simplify the merging process. The algorithm compares nodes from both lists and attaches the smaller node to the result list.
Conclusion
Linked lists become easy when you stop thinking in terms of indices and start thinking in terms of connections. Every problem is essentially about adjusting pointers correctly.If you can trace your logic step by step and explain what each pointer is doing, you will be able to solve most linked list problems confidently in interviews.
Frequently Asked Questions
1. What is a linked list in simple terms?2. Why is insertion faster in a linked list?A linked list is a data structure where elements are stored in nodes, and each node points to the next node, forming a chain-like structure.
3. What is the role of the head pointer?Insertion is faster because you only need to update pointers, and no shifting of elements is required like in arrays.
4. When should you use a linked list instead of an array?The head pointer stores the address of the first node and acts as the starting point of the linked list.
5. What is the main drawback of a linked list?You should use a linked list when frequent insertion and deletion operations are required and dynamic size is needed.
The main drawback is that accessing elements takes more time because you must traverse the list sequentially.
0 Comments