In the realm of data structures, while arrays are often the first tool developers reach for, Linked Lists stand as the second most commonly used structure for solving a wide array of problems. They elegantly address many limitations inherent in arrays and serve as the fundamental building block for more complex data structures like stacks, queues, and graphs.
Unlike static arrays, a linked list is a dynamic data structure that can automatically grow and shrink as needed, providing unparalleled flexibility in memory management.
What is a Linked List?
A linked list is a linear data structure consisting of a sequence of elements called nodes. Each node contains two pieces of information:
- Value: The actual data being stored.
- Pointer (or Link): The memory address of the next node in the sequence.
This chain of nodes forms the list. The first node is called the HEAD, and the last node, which points to null
, is called the TAIL.
Key Operations and Runtime Complexity
If you don't understand the performance metrics below, don't worry just click here.
The efficiency of operations on a linked list is a critical consideration. The following table outlines the common Big O time complexities:
Operation | Description | Complexity |
---|---|---|
Lookup | By Value | O(n) |
By Index | O(n) | |
Insert | At the Beginning | O(1) |
At the End | O(1) * | |
In the Middle | O(n) | |
Delete | From the Beginning | O(1) |
From the End | O(n) * | |
From the Middle | O(n) |
Linked Lists in Code: A Java Example
Java provides a built-in LinkedList
class in its Collections framework, which is actually a implementation of a doubly linked list. Here's a simple demonstration:
import java.util.LinkedList;
public class Main {
public static void main(String[] args) {
// Create a new linked list to store integers
LinkedList<Integer> list = new LinkedList<>();
// Add elements to the end of the list
list.addLast(10);
list.addLast(20);
list.addLast(30);
// Add an element to the beginning
list.addFirst(5);
// Print the size and the contents of the list
System.out.println("Size: " + list.size()); // Output: 4
System.out.println("List: " + list); // Output: [5, 10, 20, 30]
}
}
Linked Lists vs. Arrays:
- Arrays: Static arrays have a fixed size, which can lead to wasted memory if too large or a lack of space if too small. Dynamic arrays (like
ArrayList
in Java) solve the size issue but can waste memory when they grow, as they often allocate a new block that is 50-100% larger and copy the old elements over. - Linked Lists: Nodes are allocated individually in memory. They only use the memory required for the current number of elements, plus the overhead of the pointers. There is no wasted, unused pre-allocated space.
- When to Use Which:
- Use an array (or
ArrayList
) if you know the approximate number of items in advance (e.g., you can initialize it withnew ArrayList(100)
). - Use a linked list when you need frequent insertions and deletions at the beginning and end of the list and the size is highly variable.
- Use an array (or
Performance Overview
The Image blew provides a high-level comparison of the average time complexities for each data structure, highlighting their strengths and weaknesses.
Types of Linked Lists
There are several common variations of the basic linked list:
1. Singly Linked List
This is the most basic type. Each node points only to the next node in the sequence. The traversal is unidirectional, from head to tail.
2. Doubly Linked List
In this type, each node has two pointers: one to the next node and one to the previous node. This allows for bidirectional traversal, which makes some operations (like deleting the tail node) more efficient, albeit at the cost of increased memory usage per node.
Other types include Circular Linked Lists, where the tail node points back to the head, creating a circle.
Summary and Key Takeaways
- Purpose: Linked lists are the second most used data structure, ideal for scenarios requiring dynamic sizing and frequent insertions/removals from the ends.
- Memory: They can grow and shrink automatically, eliminating memory waste but incurring a small overhead for the pointers in each node.
- Performance:
- Strengths: Excellent performance for adding/removing elements at the beginning (O(1)).
- Weaknesses: Poor performance for lookups (O(n)) as they require sequential access.
Understanding the trade-offs between linked lists and arrays is fundamental to choosing the right tool for the job and writing efficient, effective software.
Top comments (2)
Continue this DSA series these are great
Okay I will do that