Your Progress

4/30 completed

13% complete. Continue learning to master DSA!

Linked Lists in Data Structures

Linked lists are dynamic data structures that allow efficient insertions and deletions. Unlike arrays, linked lists don't require contiguous memory allocation, making them more flexible.

Key Takeaways

  • Linked lists store elements in nodes that contain data and a reference to the next node
  • They provide efficient insertions and deletions (O(1) time) when the position is known
  • Types include singly linked lists, doubly linked lists, and circular linked lists
  • Linked lists don't support random access (O(n) time to access an element)
  • They use more memory than arrays due to the storage of pointers/references

What is a Linked List?

A linked list is a linear data structure where elements are stored in nodes that are connected using pointers or references. Each node contains two parts: data and a reference to the next node. This structure allows for efficient insertions and deletions as it doesn't require the elements to be stored in contiguous memory locations.

Key Characteristics of Linked Lists

Linked lists are dynamic in size, can grow or shrink during execution, and are ideal for applications where the number of elements is unknown in advance. They excel at insertion and deletion operations but have slower access times compared to arrays.

Visual Representation of a Linked List

Data: A
Next →
Head
Data: B
Next →
Node
Data: C
Next: NULL
Tail

A singly linked list with 3 nodes

Note: In a linked list, the first node is called the "head" and the last node points to NULL (or None), indicating the end of the list. In some implementations, a "tail" pointer is also maintained for quick access to the end of the list.

Types of Linked Lists

Linked lists come in several variations, each with its own advantages and use cases:

Singly Linked List

Each node contains data and a reference to the next node in the sequence. Traversal is only possible in one direction.

A
B
C
NULL

Doubly Linked List

Each node contains data and references to both the next and previous nodes. This allows traversal in both directions.

NULL
A
B
C
NULL

Circular Linked List

Similar to a singly linked list, but the last node points back to the first node, creating a circle. Useful for applications that require cycling through a list repeatedly.

Circular Doubly Linked List

Combines features of doubly linked and circular lists. The last node points to the first, and the first node points back to the last. Allows bidirectional traversal and cycling.

Common Linked List Operations

Let's explore the most common operations performed on linked lists and their time complexities:

OperationDescriptionTime Complexity
Insertion at beginningAdding a new node at the start of the listO(1)
Insertion at endAdding a new node at the end of the listO(1) with tail pointer
O(n) without tail pointer
Insertion at positionAdding a new node at a specific positionO(1) if position is known
O(n) to find position
DeletionRemoving a node from the listO(1) if node is known
O(n) to find node
SearchFinding a specific node in the listO(n)
TraversalVisiting each node in the listO(n)

Efficiency Insight

Linked lists shine when it comes to insertions and deletions, especially when the position is known. However, they are less efficient than arrays for random access operations. Choose the right data structure based on your specific use case and required operations.

Linked List Implementation

Let's look at how to implement a simple singly linked list in different programming languages:

1#include <stdio.h>
2#include <stdlib.h>
3
4// Define the structure for a node
5struct Node {
6 int data;
7 struct Node* next;
8};
9
10// Function to create a new node
11struct Node* createNode(int data) {
12 struct Node* newNode = (struct Node*)malloc(sizeof(struct Node));
13 if (newNode == NULL) {
14 printf("Memory allocation failed\n");
15 exit(1);
16 }
17 newNode->data = data;
18 newNode->next = NULL;
19 return newNode;
20}
21
22// Function to insert a node at the beginning
23struct Node* insertAtBeginning(struct Node* head, int data) {
24 struct Node* newNode = createNode(data);
25 newNode->next = head;
26 return newNode;
27}
28
29// Function to display the linked list
30void displayList(struct Node* head) {
31 struct Node* current = head;
32
33 if (current == NULL) {
34 printf("List is empty\n");
35 return;
36 }
37
38 printf("Linked List: ");
39 while (current != NULL) {
40 printf("%d -> ", current->data);
41 current = current->next;
42 }
43 printf("NULL\n");
44}
45
46// Function to free the memory
47void freeList(struct Node* head) {
48 struct Node* temp;
49 while (head != NULL) {
50 temp = head;
51 head = head->next;
52 free(temp);
53 }
54}
55
56int main() {
57 struct Node* head = NULL;
58
59 // Insert nodes
60 head = insertAtBeginning(head, 30);
61 head = insertAtBeginning(head, 20);
62 head = insertAtBeginning(head, 10);
63
64 // Display the list
65 displayList(head);
66
67 // Free memory
68 freeList(head);
69
70 return 0;
71}

Memory Management

In languages without automatic garbage collection (like C), it's crucial to free memory allocated for nodes when they're no longer needed to prevent memory leaks. Languages like Java and Python handle this automatically through their garbage collection mechanisms.

Linked Lists vs. Arrays

When deciding between linked lists and arrays, consider their relative strengths and weaknesses:

Linked Lists Advantages

  • Dynamic size: Can grow or shrink during execution without reallocation.
  • Efficient insertions/deletions: O(1) time when the position is known.
  • No waste of space: Memory is allocated as needed.
  • Implementation of other data structures: Useful for implementing stacks, queues, and graphs.

Arrays Advantages

  • Random access: O(1) time to access elements by index.
  • Memory efficiency: Less overhead per element (no need for pointers).
  • Cache locality: Better performance due to memory locality.
  • Simplicity: Easier to use and implement.
OperationLinked ListArray
Access by indexO(n)O(1)
Insert at beginningO(1)O(n)
Insert at endO(1) with tail pointerO(1) amortized for dynamic arrays
Insert in middleO(n) to find + O(1) to insertO(n)
DeleteO(n) to find + O(1) to deleteO(n)
Memory allocationDynamicStatic (fixed arrays) or dynamic (resizable)

Applications of Linked Lists

Linked lists are used in a variety of applications across computer science:

  • Implementation of other data structures - Stacks, queues, and hash tables can be implemented using linked lists.
  • Dynamic memory allocation - Operating systems use linked lists to manage memory blocks.
  • Undo functionality - Applications use linked lists to implement undo operations.
  • Symbol tables in compilers - Linked lists can be used to implement symbol tables.
  • Adjacency lists for graphs - Graph representations often use linked lists for storing adjacent vertices.
  • Music player playlists - Circular linked lists can be used to play songs in a loop.

Next Steps

Now that you understand linked lists, you're ready to explore more complex data structures:

  • Stacks-Learn about the Last-In-First-Out (LIFO) data structure
  • Queues-Discover the First-In-First-Out (FIFO) data structure
  • Trees-Explore hierarchical data structures

Related Tutorials

Arrays

Learn about arrays, the most fundamental data structure.

Learn more

Stacks

Explore the LIFO data structure built using linked lists or arrays.

Learn more

Queues

Learn about the FIFO data structure implemented with linked lists.

Learn more