`

Before you start, try using this visualizer to understand how a linked list works.

linked list — grows as you go

head
10
20
30
40
null
nodes: 4
select an operation above to run it

Now let us learn in detail what a linked list is.

What is a linked list?

A linked list is a collection of nodes. Each node has two things:

  • The data — the value you want to store
  • A pointer — where the next node is

You create a node, and when you need another one you just create it and connect them together.

Image coming soon

With arrays you need to decide the size at the beginning of the program. With a linked list you do not have to decide anything upfront. You just add a new node whenever you need one and the linked list keeps growing.

Now let us see how to create a linked list.

How to create a node

Give this visualizer a try before reading.

Create a node

empty — create a node
nodes: 0
enter a value and click Create node

You know a node consists of two things — data and the address of the next node. For that let us use a struct to define it. A struct is just a way to group related variables together under one name.

struct Node {
    int data;
    Node* next;
};

data holds the value you want to store. next holds the address of the next node.

Note
We write Node* and not just Node because next is a pointer. A pointer does not hold the value itself, it holds the address of where that value lives in memory.

Now let us create a node:

Node* p = new Node;

new Node creates the node in memory and p holds the address of that node.

Now let us add data to it and set the next pointer:

p->data = 10;
p->next = nullptr;
Image coming soon

p->data = 10 stores the value 10 in the node. p->next = nullptr means this node is not pointing to any next node yet. nullptr just means nothing.

Here is how creating a node looks:

struct Node {
    int data;
    Node* next;
};

Node* p = new Node;
p->data = 10;
p->next = nullptr;

Adding a node

Play around with this visualizer first.

Add a node

head
10
null
nodes: 1
select an operation above to run it

We already know how to create a node. Let us create our first node:

Node* node1 = new Node;
node1->data = 10;
node1->next = nullptr;
Image coming soon

Now let us create one more node:

Node* node2 = new Node;
node2->data = 20;
node2->next = nullptr;
Image coming soon

Right now these two nodes exist in memory but they are not connected. To connect them we just store the address of node2 inside the next part of node1:

node1->next = node2;
Image coming soon

Now node1 knows where node2 is. That is how you connect two nodes.

Here is how adding and connecting two nodes looks:

Node* node1 = new Node;
node1->data = 10;
node1->next = nullptr;

Node* node2 = new Node;
node2->data = 20;
node2->next = nullptr;

node1->next = node2;

Head and tail

Every linked list has a starting point. That starting point is called the head. The head is just a pointer that points to the first node in the list.

The last node in the list is called the tail. Since there is no node after the tail, its next is set to nullptr.

Node* head = node1;
Image coming soon

head points to node1. node1 points to node2. node2 points to nullptr because it is the last node.

Important
If you ever lose the head pointer you lose the entire list. There is no way to get back to the beginning. So always keep track of head.

Traversing a linked list

Before we start, try this visualizer to see how traversal works.

Traverse

head
10
20
30
40
null
nodes: 4
select an operation above to run it

Traversing means going through every node in the list one by one. You have to start from the head node and go through each node one by one until you reach the end.

To traverse we use a pointer called current and start it at the head:

Node* current = head;
Image coming soon

Now we will print the current node:

cout << current->data << "\n";

And then current will move to the next node:

current = current->next;

We keep doing this until current does not point to anything, which means we have reached the end of the list:

while (current != nullptr) {
    cout << current->data << "\n";
    current = current->next;
}
Image coming soon
Time complexity
If there are n nodes it takes n steps. So the time complexity is O(n).

Inserting a node

At the beginning

Play with this visualizer to see how inserting at the beginning works.

Insert — begin

head
10
20
30
40
null
nodes: 4
select an operation above to run it

To insert a node at the beginning let us first create a new node:

Node* newNode = new Node;
newNode->data = 5;
newNode->next = nullptr;
Image coming soon

Now we will add this to the beginning of the list. For that we simply make the new node point to the current head. This way the new node knows where the rest of the list is:

newNode->next = head;
Image coming soon

Now since the first node has changed we just make head point to the new node:

head = newNode;
Image coming soon
Important
Always point newNode to head first before updating head. If you do it the other way you will lose the entire list.

Here is how inserting at the beginning looks:

Node* newNode = new Node;
newNode->data = 5;
newNode->next = head;
head = newNode;
Time complexity
No matter how many nodes are in the list this always takes one step. So the time complexity is O(1).

At a specific position

Try this visualizer before reading.

Insert — position

head
10
20
30
40
null
nodes: 4
select an operation above to run it

To insert a node at a specific position we first need to walk to the node just before where we want to insert. Let us say we want to insert after the second node.

First we create a new node:

Node* newNode = new Node;
newNode->data = 15;
newNode->next = nullptr;
Image coming soon

Now we need to reach the second node. We are just traversing the list but stopping there:

Node* current = head;
for (int i = 0; i < 2; i++) {
    current = current->next;
}
Image coming soon

Now current is at the second node. We first point newNode to current->next so the new node knows where the rest of the list is:

newNode->next = current->next;
Image coming soon

Now we make current point to the new node:

current->next = newNode;
Image coming soon
Important
Always point newNode to current->next first before updating current->next. If you do it the other way you will lose the rest of the list.

Here is how inserting at a specific position looks:

Node* newNode = new Node;
newNode->data = 15;
newNode->next = current->next;
current->next = newNode;
Time complexity
We have to walk to the position first. If there are n nodes it takes n steps. So the time complexity is O(n).

Removing a node

From the beginning

Give this visualizer a try before we start.

Remove — begin

head
10
20
30
40
null
nodes: 4
select an operation above to run it

To remove the first node we just make head point to the second node:

head = head->next;
Image coming soon

That is it. The first node is now disconnected from the list and head is pointing to the second node which is now the new first node.

Time complexity
No matter how many nodes are in the list this always takes one step. So the time complexity is O(1).

From the end

Play around with this visualizer first.

Remove — end

head
10
20
30
40
null
nodes: 4
select an operation above to run it

To remove the last node we need to walk to the second last node and set its next to nullptr.

First we start at the head and walk until we reach the second last node:

Node* current = head;
while (current->next->next != nullptr) {
    current = current->next;
}
Image coming soon

Now current is at the second last node. We just set its next to nullptr:

current->next = nullptr;
Image coming soon

The last node is now disconnected from the list.

Time complexity
We have to walk to the end of the list. If there are n nodes it takes n steps. So the time complexity is O(n).

From a specific position

Try this visualizer to see how it works before reading.

Remove — position

head
10
20
30
40
null
nodes: 4
select an operation above to run it

To remove a node from a specific position we need to walk to the node just before it and make it skip over the node we want to remove.

Let us say we want to remove the third node. We first walk to the second node:

Node* current = head;
for (int i = 0; i < 2; i++) {
    current = current->next;
}
Image coming soon

Now current is at the second node. We just make current point to the node after the one we want to remove:

current->next = current->next->next;
Image coming soon

The third node is now disconnected from the list. Nobody is pointing to it anymore.

Time complexity
We have to walk to the position first. If there are n nodes it takes n steps. So the time complexity is O(n).

Complete linked list code

#include <iostream>
using namespace std;

struct Node {
    int data;
    Node* next;
};

void insertAtBeginning(Node*& head, int value) {
    Node* newNode = new Node;
    newNode->data = value;
    newNode->next = head;
    head = newNode;
}

void insertAtPosition(Node* head, int value, int position) {
    Node* newNode = new Node;
    newNode->data = value;
    Node* current = head;
    for (int i = 0; i < position - 1; i++) {
        current = current->next;
    }
    newNode->next = current->next;
    current->next = newNode;
}

void removeFromBeginning(Node*& head) {
    head = head->next;
}

void removeFromEnd(Node* head) {
    Node* current = head;
    while (current->next->next != nullptr) {
        current = current->next;
    }
    current->next = nullptr;
}

void removeFromPosition(Node* head, int position) {
    Node* current = head;
    for (int i = 0; i < position - 1; i++) {
        current = current->next;
    }
    current->next = current->next->next;
}

void traverse(Node* head) {
    Node* current = head;
    while (current != nullptr) {
        cout << current->data << "\n";
        current = current->next;
    }
}

int main() {
    Node* node1 = new Node;
    node1->data = 10;
    node1->next = nullptr;

    Node* node2 = new Node;
    node2->data = 20;
    node2->next = nullptr;

    Node* node3 = new Node;
    node3->data = 30;
    node3->next = nullptr;

    node1->next = node2;
    node2->next = node3;

    Node* head = node1;

    insertAtBeginning(head, 5);
    insertAtPosition(head, 15, 2);
    removeFromBeginning(head);
    removeFromEnd(head);
    removeFromPosition(head, 2);

    traverse(head);

    return 0;
}