Linked List vs Array When to Choose Which in DSA Go - Complexity Comparison
Choosing between a linked list and an array depends on how fast operations run as data grows.
We want to know which data structure handles adding, removing, and accessing items better as the list gets bigger.
Analyze the time complexity of adding and accessing elements in arrays and linked lists.
// Adding element at end in array
func addToArray(arr []int, val int) []int {
return append(arr, val)
}
// Adding element at end in linked list
func addToLinkedList(head *Node, val int) *Node {
newNode := &Node{val, nil}
if head == nil {
return newNode
}
current := head
for current.next != nil {
current = current.next
}
current.next = newNode
return head
}
// Access element at index in array
func accessArray(arr []int, index int) int {
return arr[index]
}
// Access element at index in linked list
func accessLinkedList(head *Node, index int) int {
current := head
for i := 0; i < index; i++ {
current = current.next
}
return current.val
}
This code shows how to add elements at the end and access elements by index in arrays and linked lists.
Look at loops and repeated steps in the code.
- Primary operation for adding to array: append operation, usually fast without loop.
- Primary operation for adding to linked list: loop through nodes to find the end (repeats for each element).
- Primary operation for accessing array: direct access by index, no loop.
- Primary operation for accessing linked list: loop through nodes up to the index.
As the list size grows, how do these operations change?
| Input Size (n) | Array Add End | Linked List Add End | Array Access | Linked List Access |
|---|---|---|---|---|
| 10 | ~1 operation | ~10 operations | 1 operation | 10 operations |
| 100 | ~1 operation | ~100 operations | 1 operation | 100 operations |
| 1000 | ~1 operation | ~1000 operations | 1 operation | 1000 operations |
Adding to array is usually fast but can slow if resizing happens. Adding to linked list always needs to walk to the end, so it grows with size. Accessing array is always quick, but linked list access grows with index.
Time Complexity: O(1) for array access, O(n) for linked list access and adding at end without tail pointer
This means arrays let you quickly get any item, while linked lists take longer as they grow when you add or get items by position.
[X] Wrong: "Linked lists are always faster for adding elements than arrays."
[OK] Correct: Without a pointer to the end, linked lists must walk through all nodes to add at the end, making it slower than arrays that add at the end quickly most times.
Understanding these differences helps you pick the right data structure for your problem and explain your choice clearly in interviews.
"What if the linked list kept a pointer to the last node? How would adding at the end change time complexity?"
Practice
Solution
Step 1: Understand random access in arrays
Arrays store elements contiguously, so accessing any index is fast and direct.Step 2: Compare with linked list access
Linked lists require traversal from the start to reach an index, which is slower.Final Answer:
Array -> Option AQuick Check:
Fast random access = Array [OK]
- Thinking linked lists provide fast random access
- Confusing stack or queue with random access
- Ignoring array's contiguous memory advantage
Solution
Step 1: Recall Go array declaration syntax
In Go, arrays are declared with var name [size]type, e.g., var arr [5]int.Step 2: Check other options for errors
arr := []int{5} declares a slice with one element 5, not an array of size 5. var arr int[5] syntax is invalid. arr := int[5] syntax is invalid.Final Answer:
var arr [5]int -> Option AQuick Check:
Go array syntax = var arr [5]int [OK]
- Confusing slices with arrays
- Using incorrect syntax like int[5]
- Assuming shorthand always creates arrays
package main
import "fmt"
type Node struct {
value int
next *Node
}
func main() {
head := &Node{value: 10}
head.next = &Node{value: 20}
head.next.next = &Node{value: 30}
current := head
for current != nil {
fmt.Print(current.value, " -> ")
current = current.next
}
fmt.Print("null")
}Solution
Step 1: Trace node insertion order
Nodes are linked as 10 -> 20 -> 30 in order, so head points to 10, next to 20, then 30.Step 2: Trace print loop
Loop prints current.value then moves to next until nil, printing 10 -> 20 -> 30 -> null.Final Answer:
10 -> 20 -> 30 -> null -> Option CQuick Check:
Linked list traversal prints nodes in insertion order [OK]
- Assuming reverse order print
- Confusing next pointers
- Forgetting to print 'null' at end
package main
type Node struct {
value int
next *Node
}
func main() {
head := &Node{value: 10}
head.next = &Node{value: 20}
newNode := &Node{value: 15}
newNode.next = head.next
head = newNode
}Solution
Step 1: Analyze pointer changes
newNode.next points to head.next (20), but then head is set to newNode, losing original head node (10).Step 2: Identify correct insertion
To insert after head, should set head.next = newNode, not head = newNode.Final Answer:
The new node is not linked correctly; head pointer is overwritten -> Option BQuick Check:
Head pointer overwritten = bug [OK]
- Overwriting head pointer instead of next
- Confusing insertion before vs after head
- Ignoring pointer updates needed
Solution
Step 1: Understand insertion/deletion needs
Frequent insertions/deletions at unknown positions are costly in arrays due to shifting elements.Step 2: Compare linked list advantages
Linked lists allow changing pointers to insert/delete nodes efficiently without shifting.Step 3: Consider random access requirement
Random access is rare, so linked list's slower access is acceptable.Final Answer:
Linked List, because it allows efficient insertions and deletions anywhere -> Option DQuick Check:
Frequent insert/delete unknown pos = Linked List [OK]
- Choosing array for frequent insert/delete
- Confusing random access speed importance
- Ignoring memory overhead of linked lists
Ready to prove it?
Pass the test and this pattern moves unknown
