Your Progress
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
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.
Doubly Linked List
Each node contains data and references to both the next and previous nodes. This allows traversal in both directions.
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:
Operation | Description | Time Complexity |
---|---|---|
Insertion at beginning | Adding a new node at the start of the list | O(1) |
Insertion at end | Adding a new node at the end of the list | O(1) with tail pointer O(n) without tail pointer |
Insertion at position | Adding a new node at a specific position | O(1) if position is known O(n) to find position |
Deletion | Removing a node from the list | O(1) if node is known O(n) to find node |
Search | Finding a specific node in the list | O(n) |
Traversal | Visiting each node in the list | O(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>34// Define the structure for a node5struct Node {6 int data;7 struct Node* next;8};910// Function to create a new node11struct 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}2122// Function to insert a node at the beginning23struct Node* insertAtBeginning(struct Node* head, int data) {24 struct Node* newNode = createNode(data);25 newNode->next = head;26 return newNode;27}2829// Function to display the linked list30void displayList(struct Node* head) {31 struct Node* current = head;3233 if (current == NULL) {34 printf("List is empty\n");35 return;36 }3738 printf("Linked List: ");39 while (current != NULL) {40 printf("%d -> ", current->data);41 current = current->next;42 }43 printf("NULL\n");44}4546// Function to free the memory47void freeList(struct Node* head) {48 struct Node* temp;49 while (head != NULL) {50 temp = head;51 head = head->next;52 free(temp);53 }54}5556int main() {57 struct Node* head = NULL;5859 // Insert nodes60 head = insertAtBeginning(head, 30);61 head = insertAtBeginning(head, 20);62 head = insertAtBeginning(head, 10);6364 // Display the list65 displayList(head);6667 // Free memory68 freeList(head);6970 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.
Operation | Linked List | Array |
---|---|---|
Access by index | O(n) | O(1) |
Insert at beginning | O(1) | O(n) |
Insert at end | O(1) with tail pointer | O(1) amortized for dynamic arrays |
Insert in middle | O(n) to find + O(1) to insert | O(n) |
Delete | O(n) to find + O(1) to delete | O(n) |
Memory allocation | Dynamic | Static (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:
Related Tutorials
Arrays
Learn about arrays, the most fundamental data structure.
Learn moreStacks
Explore the LIFO data structure built using linked lists or arrays.
Learn moreQueues
Learn about the FIFO data structure implemented with linked lists.
Learn more