Big-O and what Go operations cost
Big-O describes how the work grows as the input grows, ignoring constant factors. O(1) takes the same time for 10 items or 10 million; O(n) doubles when the input doubles; O(log n) barely grows (a million items is about 20 steps); O(n²) quadruples. For interviews and for real code, the question is always: what happens when n is 1,000,000?
| Operation | Go code | Cost |
|---|---|---|
| Index a slice | s[i] | O(1) |
| Append to a slice | s = append(s, x) | O(1) amortised (occasionally copies) |
| Pop from the end | s = s[:len(s)-1] | O(1) |
| Insert / delete in the middle | slices.Insert, slices.Delete | O(n) — shifts the tail |
| Find in an unsorted slice | slices.Index(s, x) | O(n) |
| Find in a sorted slice | slices.BinarySearch(s, x) | O(log n) |
| Map get / set / delete | m[k], delete(m, k) | O(1) average |
| Sort | slices.Sort(s) | O(n log n) |
| Heap push / pop | heap.Push, heap.Pop | O(log n) |
| Linked list insert at a known element | l.InsertAfter(v, e) | O(1) |
| String concatenation in a loop | s += x | O(n²) total — use strings.Builder |
Counting steps makes the difference concrete. Both functions answer "does the list contain a duplicate?":
package main
import "fmt"
func dupNested(xs []int) (bool, int) {
steps := 0
for i := range xs {
for j := i + 1; j < len(xs); j++ {
steps++
if xs[i] == xs[j] {
return true, steps
}
}
}
return false, steps
}
func dupMap(xs []int) (bool, int) {
seen := make(map[int]bool, len(xs))
steps := 0
for _, x := range xs {
steps++
if seen[x] {
return true, steps
}
seen[x] = true
}
return false, steps
}
func main() {
xs := make([]int, 1000)
for i := range xs {
xs[i] = i // no duplicates: the worst case
}
fmt.Println(dupNested(xs))
fmt.Println(dupMap(xs))
}false 499500
false 1000O(n²) vs O(n): 499,500 comparisons against 1,000 map lookups. At a million items the nested loop does about 500 billion.
Make the list 2,000 long. Roughly how much do the two step counts grow?
