📖 21 min read
Last updated on

Go Coding Interview Cheat Sheet — Complete Syntax & Patterns Guide


A practical, no-fluff guide to the Go syntax, built-ins, and patterns you’ll actually reach for during a live coding interview.

Whimsical cartoony stick figure happily riding a cute, chubby gopher mascot that is digging a tunnel rapidly

Why Go for Coding Interviews?

Go is gaining ground fast in technical interviews, especially at infrastructure-heavy companies like Google, Cloudflare, Uber, and Dropbox. Its simplicity is a genuine advantage — no generics maze, no inheritance hierarchy, no implicit magic. But that simplicity cuts both ways: Go’s standard library is lean, which means you need to know exactly what’s available and when to roll your own.

The biggest adjustment for candidates coming from Python or Java is that Go has no built-in set, no built-in heap interface that’s plug-and-play, and sorts require implementing an interface or using a comparator func. Once you know these gaps and how to fill them, Go becomes a clean, fast, interview-friendly language.


If you're building your full preparation plan, start with our How to Prepare for a Coding Interview guide. This also pairs perfectly with Data Structures for Coding Interviews.

Looking for a different language? Python, Java, C++, JavaScript.

Already know why Go? Try a free Go mock interview on Intervu →

1. Data Types & Variables

package main

import "fmt"

func main() {
    // Basic types
    var x int    = 10
    var f float64 = 3.14
    var b bool   = true
    var s string = "hello"

    // Short declaration (most common in practice)
    x  := 10
    pi := 3.14159
    ok := true

    // Integer types
    var i8  int8   // -128 to 127
    var i32 int32  // ~2.1 billion
    var i64 int64  // ~9.2 × 10^18
    var n   int    // platform-size (64-bit on modern systems)

    // Unsigned
    var u uint
    var u32 uint32

    // Constants
    const MOD = 1_000_000_007
    const INF = int(^uint(0) >> 1)   // max int
    const INF32 = 1<<31 - 1          // max int32

    // Multiple assignment
    a, b, c := 1, 2, 3
    a, b = b, a    // swap

    // Zero values (Go initializes everything)
    var zi int     // 0
    var zf float64 // 0.0
    var zb bool    // false
    var zs string  // ""

    // Type conversion (explicit — no implicit casting)
    i := 42
    f2 := float64(i)
    u2 := uint(f2)
    s2 := string(rune(65))  // "A"  — convert int to char

    // Infinity proxies for interviews
    posInf := math.MaxInt
    negInf := math.MinInt
    // or use math.Inf(1) / math.Inf(-1) for float64
}

Key tip: Go has no implicit type conversion. int32(x) + int64(y) is a compile error — you must cast explicitly. In interviews, stick to plain int unless the problem specifically requires 64-bit precision, then use int64 consistently.


2. Strings & Runes

import (
    "strings"
    "strconv"
    "unicode"
)

s := "hello world"

// Basics
len(s)                        // 11  (bytes, not runes!)
s[0]                          // 104  ('h' as byte)
string(s[0])                  // "h"
s[1:5]                        // "ello"  [start, end)
s[6:]                         // "world"
s[:5]                         // "hello"

// Strings package
strings.ToUpper(s)            // "HELLO WORLD"
strings.ToLower(s)            // "hello world"
strings.TrimSpace(s)          // remove leading/trailing whitespace
strings.Trim(s, "hd")         // trim chars in cutset from both ends
strings.TrimLeft(s, "h")
strings.TrimRight(s, "d")
strings.Contains(s, "world")  // true
strings.HasPrefix(s, "hel")   // true
strings.HasSuffix(s, "rld")   // true
strings.Index(s, "world")     // 6  (-1 if not found)
strings.LastIndex(s, "l")     // 9
strings.Count(s, "l")         // 3
strings.Replace(s, "l", "r", -1)  // replace all (-1 = all)
strings.Replace(s, "l", "r", 1)   // replace first occurrence only
strings.Split(s, " ")         // ["hello", "world"]
strings.Join([]string{"a","b","c"}, ",")  // "a,b,c"
strings.Repeat("ab", 3)       // "ababab"
strings.Fields(s)             // split by any whitespace

// strconv — type conversions
strconv.Itoa(42)              // int → "42"
strconv.Atoi("42")            // "42" → 42, error
n, err := strconv.Atoi("123")
strconv.FormatInt(255, 16)    // int64 → hex string "ff"
strconv.ParseInt("ff", 16, 64) // hex string → int64

// Runes — for Unicode-correct character iteration
for i, r := range s {
    fmt.Println(i, r, string(r))  // byte index, rune value, char
}

runes := []rune(s)            // string → rune slice (Unicode-safe)
runes[0]                      // first character as rune
string(runes)                 // rune slice → string

// Char checks (use unicode package)
unicode.IsLetter(r)
unicode.IsDigit(r)
unicode.IsUpper(r)
unicode.IsLower(r)
unicode.ToUpper(r)
unicode.ToLower(r)

// Byte-level char check (ASCII only — fine for most interview problems)
r >= 'a' && r <= 'z'          // is lowercase letter
r >= '0' && r <= '9'          // is digit
r - 'a'                       // distance from 'a'  (0 for 'a', 25 for 'z')
int(r - '0')                  // digit char → int value

// Build string efficiently
var sb strings.Builder
sb.WriteString("hello")
sb.WriteByte(' ')
sb.WriteRune('W')
sb.WriteString("orld")
sb.String()                   // "hello World"

// String → byte slice and back
b := []byte(s)
b[0] = 'H'
string(b)                     // "Hello world"

Key tip: len(s) returns the number of bytes, not characters. For ASCII problems this is fine. For Unicode, convert to []rune first: runes := []rune(s) then len(runes). In most coding interviews, inputs are ASCII, so len(s) and s[i] are safe.


3. Arrays & Slices

Slices are Go’s primary sequence type and what you’ll use in almost every interview problem.

// Array (fixed size — rarely used directly)
arr := [5]int{3, 1, 4, 1, 5}
arr[0]                        // 3
len(arr)                      // 5

// Slice (dynamic — use this)
s := []int{3, 1, 4, 1, 5}
s2 := make([]int, 10)         // len=10, cap=10, all zeros
s3 := make([]int, 0, 10)      // len=0, cap=10  (pre-allocated)

// Access
s[0]                          // 3
s[len(s)-1]                   // 5  (last element)
s[1:4]                        // [1, 4, 1]  [start, end)
s[:3]                         // [3, 1, 4]
s[2:]                         // [4, 1, 5]

// Append
s = append(s, 9)              // [3, 1, 4, 1, 5, 9]
s = append(s, 7, 8)           // append multiple
s = append(s, other...)       // append another slice

// Length and capacity
len(s)                        // number of elements
cap(s)                        // allocated capacity

// Copy
dst := make([]int, len(s))
copy(dst, s)                  // copy(dst, src) — returns bytes copied

// 2D slice
matrix := make([][]int, rows)
for i := range matrix {
    matrix[i] = make([]int, cols)
}

// Initialize with values
ones := make([]int, n)
for i := range ones { ones[i] = 1 }

// Fill
for i := range s { s[i] = -1 }

// Iterate
for i, v := range s {
    fmt.Println(i, v)
}
for i := range s { }          // index only
for _, v := range s { }       // value only

// Reverse
for i, j := 0, len(s)-1; i < j; i, j = i+1, j-1 {
    s[i], s[j] = s[j], s[i]
}

// Contains (no built-in — linear scan or use map)
func contains(s []int, val int) bool {
    for _, v := range s {
        if v == val { return true }
    }
    return false
}

// Remove element at index i (order-preserving — O(n))
s = append(s[:i], s[i+1:]...)

// Remove element at index i (swap with last — O(1), changes order)
s[i] = s[len(s)-1]
s = s[:len(s)-1]

// Stack (use slice)
stack := []int{}
stack = append(stack, val)     // push
top := stack[len(stack)-1]     // peek
stack = stack[:len(stack)-1]   // pop

// Slices are reference types — copy before modifying if needed
original := []int{1, 2, 3}
shallow := original            // same underlying array!
deepCopy := append([]int{}, original...)  // true copy

Key tip: Slices in Go are reference types — assigning a slice to another variable does not copy the data. Use append([]int{}, s...) or copy for a true deep copy. This trips up many candidates coming from Python or Java.


4. Maps

// Declare and initialize
m := map[string]int{}                     // empty map
m2 := map[string]int{"alice": 10, "bob": 20}
m3 := make(map[string]int)               // same as {}

// Access
m["alice"]                    // 10
m["missing"]                  // 0  (zero value — no error!)

// Safe access with existence check
val, ok := m["alice"]
if ok {
    fmt.Println(val)
}

// Modify
m["carol"] = 30               // insert / update
delete(m, "bob")              // delete key (no error if missing)

// Check existence
_, exists := m["alice"]       // exists = true

// Length
len(m)

// Frequency map pattern
freq := map[byte]int{}
for _, ch := range []byte(s) {
    freq[ch]++                // zero value of int is 0, so this works!
}

// Iterate (random order — do not rely on insertion order)
for k, v := range m {
    fmt.Println(k, v)
}
for k := range m { }         // keys only
for _, v := range m { }      // values only

// Map of slices (adjacency list)
graph := map[int][]int{}
graph[u] = append(graph[u], v)

// Map as set
seen := map[int]bool{}
seen[x] = true
if seen[x] { }               // contains check

// Or use struct{} to save memory
seen2 := map[int]struct{}{}
seen2[x] = struct{}{}
_, inSet := seen2[x]

Key tip: Accessing a missing key in a Go map returns the zero value of the value type — 0 for int, false for bool, nil for slices. This is useful (e.g. freq[ch]++ just works) but can mask bugs if you’re not careful.


5. Structs

// Basic struct
type Point struct {
    X, Y int
}

p := Point{X: 1, Y: 2}
p.X                           // 1
p.Y = 5

// Anonymous struct (useful for heap items, pairs)
item := struct {
    val, priority int
}{val: 10, priority: 3}

// Struct with methods
type ListNode struct {
    Val  int
    Next *ListNode
}

// Constructor pattern
func NewListNode(val int) *ListNode {
    return &ListNode{Val: val}
}

// Methods
func (n *ListNode) String() string {
    return fmt.Sprintf("%d", n.Val)
}

// Tree node
type TreeNode struct {
    Val   int
    Left  *TreeNode
    Right *TreeNode
}

// Comparable struct for use as map key
type Coord struct{ Row, Col int }
visited := map[Coord]bool{}
visited[Coord{0, 0}] = true

6. Control Flow

// if / else if / else
if x > 0 {
} else if x == 0 {
} else {
}

// if with short statement (scope limited to if block)
if v, err := strconv.Atoi(s); err == nil {
    fmt.Println(v)
}

// for — the only loop in Go
for i := 0; i < n; i++ { }
for i := n - 1; i >= 0; i-- { }
for i := 0; i < n; i += 2 { }

// while equivalent
for condition { }

// infinite loop
for { }

// range loop
for i, v := range slice { }
for k, v := range m { }
for i, r := range str { }     // byte index, rune value

// break / continue
for i := 0; i < n; i++ {
    if i == 5 { break }
    if i%2 == 0 { continue }
}

// Labeled break (nested loops)
outer:
for i := 0; i < m; i++ {
    for j := 0; j < n; j++ {
        if grid[i][j] == target {
            break outer
        }
    }
}

// switch (no fallthrough by default)
switch x {
case 1:
    fmt.Println("one")
case 2, 3:
    fmt.Println("two or three")
default:
    fmt.Println("other")
}

// switch with no condition (like if-else chain)
switch {
case x < 0:  fmt.Println("negative")
case x == 0: fmt.Println("zero")
default:     fmt.Println("positive")
}

7. Functions

// Basic function
func add(a, b int) int {
    return a + b
}

// Multiple return values (idiomatic Go)
func minMax(s []int) (int, int) {
    min, max := s[0], s[0]
    for _, v := range s {
        if v < min { min = v }
        if v > max { max = v }
    }
    return min, max
}
lo, hi := minMax(arr)

// Named return values
func divide(a, b float64) (result float64, err error) {
    if b == 0 {
        err = fmt.Errorf("division by zero")
        return
    }
    result = a / b
    return
}

// Variadic functions
func sum(nums ...int) int {
    total := 0
    for _, n := range nums { total += n }
    return total
}
sum(1, 2, 3)
sum(arr...)          // spread slice into variadic

// First-class functions
apply := func(f func(int) int, x int) int {
    return f(x)
}
apply(func(x int) int { return x * x }, 5)  // 25

// Closures
func makeCounter() func() int {
    count := 0
    return func() int {
        count++
        return count
    }
}
counter := makeCounter()
counter()  // 1
counter()  // 2

// Recursive function
func fib(n int) int {
    if n <= 1 { return n }
    return fib(n-1) + fib(n-2)
}

// Pass slice by reference (slices are already references)
func reverse(s []int) {
    for i, j := 0, len(s)-1; i < j; i, j = i+1, j-1 {
        s[i], s[j] = s[j], s[i]
    }
}

8. Sorting

import "sort"

s := []int{3, 1, 4, 1, 5}

// Sort ascending
sort.Ints(s)                  // in-place

// Sort descending
sort.Sort(sort.Reverse(sort.IntSlice(s)))

// sort.Slice — custom comparator (most flexible)
sort.Slice(s, func(i, j int) bool {
    return s[i] < s[j]        // ascending
})
sort.Slice(s, func(i, j int) bool {
    return s[i] > s[j]        // descending
})

// Sort strings
words := []string{"banana", "fig", "apple"}
sort.Strings(words)           // lexicographic
sort.Slice(words, func(i, j int) bool {
    return len(words[i]) < len(words[j])  // by length
})

// Sort 2D slice
pairs := [][]int{{1, 3}, {2, 1}, {3, 2}}
sort.Slice(pairs, func(i, j int) bool {
    return pairs[i][1] < pairs[j][1]      // by second element
})

// Sort by multiple keys
sort.Slice(pairs, func(i, j int) bool {
    if pairs[i][0] != pairs[j][0] {
        return pairs[i][0] < pairs[j][0]  // by first asc
    }
    return pairs[i][1] > pairs[j][1]      // tie-break by second desc
})

// Sort structs
type Person struct{ Name string; Age int }
people := []Person{{"Bob", 25}, {"Alice", 30}, {"Carol", 25}}
sort.Slice(people, func(i, j int) bool {
    if people[i].Age != people[j].Age {
        return people[i].Age < people[j].Age
    }
    return people[i].Name < people[j].Name
})

// Stable sort (preserves relative order of equal elements)
sort.SliceStable(s, func(i, j int) bool {
    return s[i] < s[j]
})

// Check if sorted
sort.IntsAreSorted(s)
sort.StringsAreSorted(words)
sort.SliceIsSorted(s, func(i, j int) bool { return s[i] <= s[j] })

import "sort"

s := []int{1, 3, 5, 7, 9}  // must be sorted

// sort.SearchInts — returns leftmost index where s[i] >= target
idx := sort.SearchInts(s, 5)    // 2
idx2 := sort.SearchInts(s, 4)   // 2  (insertion point)

// Check if value exists
i := sort.SearchInts(s, 5)
exists := i < len(s) && s[i] == 5

// sort.Search — general binary search (find first i where f(i) is true)
// f must be: false, false, ..., true, true (monotonically non-decreasing)
n := len(s)
i = sort.Search(n, func(i int) bool { return s[i] >= 5 })  // lower bound
i = sort.Search(n, func(i int) bool { return s[i] > 5 })   // upper bound

// Count occurrences
lo := sort.SearchInts(s, target)
hi := sort.Search(len(s), func(i int) bool { return s[i] > target })
count := hi - lo

// Manual binary search template
lo, hi := 0, len(s)-1
for lo <= hi {
    mid := lo + (hi-lo)/2     // avoids overflow
    if s[mid] == target {
        return mid
    } else if s[mid] < target {
        lo = mid + 1
    } else {
        hi = mid - 1
    }
}

// Binary search on answer (monotonic predicate)
lo, hi = 0, int(1e9)
for lo < hi {
    mid := lo + (hi-lo)/2
    if feasible(mid) {
        hi = mid
    } else {
        lo = mid + 1
    }
}
// lo is the minimum feasible value

10. Stack & Queue

Go has no built-in stack or queue — use slices for both.

// Stack (LIFO) — use slice
stack := []int{}

// Push
stack = append(stack, val)

// Peek
top := stack[len(stack)-1]

// Pop
top = stack[len(stack)-1]
stack = stack[:len(stack)-1]

// Is empty
len(stack) == 0

// Queue (FIFO) — two options:

// Option 1: slice with index pointer (O(1) dequeue, wastes memory)
queue := []int{}
head := 0

queue = append(queue, val)    // enqueue
front := queue[head]          // peek
head++                        // dequeue

// Option 2: slice with re-slicing (simple, GC handles memory)
queue = append(queue, val)    // enqueue
front = queue[0]              // peek
queue = queue[1:]             // dequeue  (O(1) view, not copy)

// For interview use, option 2 is cleaner to write
// For performance-critical BFS, use option 1

// BFS queue pattern (most common)
queue := []int{start}
for len(queue) > 0 {
    node := queue[0]
    queue = queue[1:]
    // process node
    for _, neighbor := range graph[node] {
        queue = append(queue, neighbor)
    }
}

11. Heap (Priority Queue)

Go’s container/heap package requires implementing an interface — verbose but powerful.

import "container/heap"

// Min-heap of integers
type MinHeap []int

func (h MinHeap) Len() int           { return len(h) }
func (h MinHeap) Less(i, j int) bool { return h[i] < h[j] }  // min-heap
func (h MinHeap) Swap(i, j int)      { h[i], h[j] = h[j], h[i] }
func (h *MinHeap) Push(x any)        { *h = append(*h, x.(int)) }
func (h *MinHeap) Pop() any {
    old := *h
    n := len(old)
    x := old[n-1]
    *h = old[:n-1]
    return x
}

// Usage
h := &MinHeap{3, 1, 4, 1, 5}
heap.Init(h)                  // O(n) heapify

heap.Push(h, 2)               // O(log n)
min := heap.Pop(h).(int)      // O(log n)
(*h)[0]                       // peek min — O(1)

// Max-heap: flip the Less comparison
type MaxHeap []int
func (h MaxHeap) Less(i, j int) bool { return h[i] > h[j] }  // max-heap

// Heap of pairs — sort by priority
type Item struct {
    val, priority int
}
type PairHeap []Item

func (h PairHeap) Len() int           { return len(h) }
func (h PairHeap) Less(i, j int) bool { return h[i].priority < h[j].priority }
func (h PairHeap) Swap(i, j int)      { h[i], h[j] = h[j], h[i] }
func (h *PairHeap) Push(x any)        { *h = append(*h, x.(Item)) }
func (h *PairHeap) Pop() any {
    old := *h; n := len(old)
    x := old[n-1]; *h = old[:n-1]
    return x
}

// K largest elements using min-heap of size k
func kLargest(nums []int, k int) []int {
    h := &MinHeap{}
    heap.Init(h)
    for _, n := range nums {
        heap.Push(h, n)
        if h.Len() > k {
            heap.Pop(h)
        }
    }
    return []int(*h)
}

Key tip: The heap interface boilerplate is verbose — practice writing it from memory before your interview. In a pinch, you can also sort the slice and use it as a pseudo-priority queue if you don’t need dynamic insertions.


12. Sets (via Map)

Go has no built-in set. Use map[T]bool or map[T]struct{}.

// Set using map[T]bool (simpler)
set := map[int]bool{}
set[1] = true
set[2] = true
set[1]              // true
delete(set, 1)
len(set)            // 1

// Contains check
if set[x] { }       // false for missing keys — works!

// Set using map[T]struct{} (zero memory for value)
set2 := map[int]struct{}{}
set2[1] = struct{}{}
_, exists := set2[1]   // explicit exists check

// Build set from slice
func toSet(s []int) map[int]bool {
    m := make(map[int]bool, len(s))
    for _, v := range s { m[v] = true }
    return m
}

// Set operations
func intersection(a, b map[int]bool) map[int]bool {
    result := map[int]bool{}
    for k := range a {
        if b[k] { result[k] = true }
    }
    return result
}

func union(a, b map[int]bool) map[int]bool {
    result := map[int]bool{}
    for k := range a { result[k] = true }
    for k := range b { result[k] = true }
    return result
}

func difference(a, b map[int]bool) map[int]bool {
    result := map[int]bool{}
    for k := range a {
        if !b[k] { result[k] = true }
    }
    return result
}

13. Math & Numbers

import "math"

// Math functions
math.Max(float64(a), float64(b))   // requires float64!
math.Min(float64(a), float64(b))
math.Abs(float64(x))
math.Sqrt(float64(x))
math.Pow(2, 10)                    // 1024.0
math.Floor(3.7)                    // 3.0
math.Ceil(3.2)                     // 4.0
math.Log(x)                        // natural log
math.Log2(x)
math.Log10(x)

// Integer max/min (no built-in for int — write helpers)
func maxInt(a, b int) int {
    if a > b { return a }
    return b
}
func minInt(a, b int) int {
    if a < b { return a }
    return b
}
func absInt(x int) int {
    if x < 0 { return -x }
    return x
}

// Go 1.21+ has built-in min/max for ordered types
// min(a, b)
// max(a, b)

// Integer limits
math.MaxInt    // largest int
math.MinInt    // smallest int
math.MaxInt32
math.MinInt32
math.MaxInt64
math.MinInt64

// GCD
func gcd(a, b int) int {
    for b != 0 {
        a, b = b, a%b
    }
    return a
}

// LCM
func lcm(a, b int) int {
    return a / gcd(a, b) * b  // divide first to avoid overflow
}

// Modular arithmetic
result := ((a % MOD) * (b % MOD)) % MOD
result = ((result + MOD) % MOD)  // ensure positive

// Fast modular exponentiation
func powmod(base, exp, mod int) int {
    result := 1
    base %= mod
    for exp > 0 {
        if exp&1 == 1 {
            result = result * base % mod
        }
        base = base * base % mod
        exp >>= 1
    }
    return result
}

// Integer square root
func isqrt(n int) int {
    return int(math.Sqrt(float64(n)))
}

Key tip: Go’s math.Max and math.Min only work on float64. For int comparisons you need to write a helper or use an if expression inline. Go 1.21 added built-in min and max for ordered types — worth knowing if your interviewer allows a recent Go version.


14. Bit Manipulation

// Bitwise operators
x & y           // AND
x | y           // OR
x ^ y           // XOR
^x              // NOT (bitwise complement) — note: ^ not ~
x << k          // left shift
x >> k          // right shift (arithmetic for signed int)

// Useful tricks
x & 1           // check if x is odd
x & (x - 1)     // clear lowest set bit
x & (-x)        // isolate lowest set bit
x ^ x           // 0
x ^ 0           // x

// Power of 2 check
func isPow2(x int) bool {
    return x > 0 && x&(x-1) == 0
}

// Count set bits (no built-in)
func popcount(x int) int {
    count := 0
    for x != 0 {
        x &= x - 1
        count++
    }
    return count
}

// Or use math/bits package
import "math/bits"
bits.OnesCount(uint(x))    // count set bits
bits.Len(uint(x))          // bits needed to represent x
bits.TrailingZeros(uint(x)) // count trailing zeros
bits.LeadingZeros(uint(x))  // count leading zeros

// Set / clear / toggle / read bit k
x |= 1 << k                // set bit k
x &^= 1 << k               // clear bit k  (&^ is Go's AND NOT)
x ^= 1 << k                // toggle bit k
(x >> k) & 1               // read bit k

// Iterate all subsets of n elements
for mask := 0; mask < 1<<n; mask++ {
    for i := 0; i < n; i++ {
        if mask>>i&1 == 1 {
            // element i is in this subset
        }
    }
}

Key tip: Go uses ^ for both XOR (binary operator) and bitwise NOT (unary operator). Also, Go’s “AND NOT” is &^ — used to clear bits: x &^= mask clears all bits set in mask. This trips up candidates who expect ~ from C/C++/Java.


15. Common Algorithm Patterns

These are the templates that cover the vast majority of Go coding interview questions. The patterns are the same as other languages — Go’s syntax just requires a few more lines.

Two Pointers

func twoSumSorted(arr []int, target int) (int, int) {
    l, r := 0, len(arr)-1
    for l < r {
        sum := arr[l] + arr[r]
        if sum == target {
            return l, r
        } else if sum < target {
            l++
        } else {
            r--
        }
    }
    return -1, -1
}

Sliding Window

func maxSumSubarray(arr []int, k int) int {
    window := 0
    for i := 0; i < k; i++ {
        window += arr[i]
    }
    best := window
    for i := k; i < len(arr); i++ {
        window += arr[i] - arr[i-k]
        if window > best {
            best = window
        }
    }
    return best
}

Fast & Slow Pointers

func hasCycle(head *ListNode) bool {
    slow, fast := head, head
    for fast != nil && fast.Next != nil {
        slow = slow.Next
        fast = fast.Next.Next
        if slow == fast {
            return true
        }
    }
    return false
}

BFS Template

func bfs(graph map[int][]int, start int) {
    visited := map[int]bool{start: true}
    queue := []int{start}
    for len(queue) > 0 {
        node := queue[0]
        queue = queue[1:]
        for _, neighbor := range graph[node] {
            if !visited[neighbor] {
                visited[neighbor] = true
                queue = append(queue, neighbor)
            }
        }
    }
}

DFS Template (Recursive)

func dfs(graph map[int][]int, node int, visited map[int]bool) {
    visited[node] = true
    for _, neighbor := range graph[node] {
        if !visited[neighbor] {
            dfs(graph, neighbor, visited)
        }
    }
}

DFS Template (Iterative)

func dfsIterative(graph map[int][]int, start int) {
    visited := map[int]bool{}
    stack := []int{start}
    for len(stack) > 0 {
        node := stack[len(stack)-1]
        stack = stack[:len(stack)-1]
        if visited[node] {
            continue
        }
        visited[node] = true
        for _, neighbor := range graph[node] {
            stack = append(stack, neighbor)
        }
    }
}

Binary Search on Answer

lo, hi := 0, int(1e9)
for lo < hi {
    mid := lo + (hi-lo)/2
    if feasible(mid) {
        hi = mid
    } else {
        lo = mid + 1
    }
}
// lo is the minimum feasible value

Dynamic Programming — Memoization

func solve(n int, memo map[int]int) int {
    if n <= 1 { return n }
    if v, ok := memo[n]; ok { return v }
    result := solve(n-1, memo) + solve(n-2, memo)
    memo[n] = result
    return result
}

// Call with: solve(n, map[int]int{})

// With a slice for dense integer states
var dp func(i int) int
cache := make([]int, n+1)
for i := range cache { cache[i] = -1 }
dp = func(i int) int {
    if i <= 1 { return i }
    if cache[i] != -1 { return cache[i] }
    cache[i] = dp(i-1) + dp(i-2)
    return cache[i]
}

Prefix Sum

func buildPrefix(arr []int) []int {
    prefix := make([]int, len(arr)+1)
    for i, v := range arr {
        prefix[i+1] = prefix[i] + v
    }
    return prefix
}
// Range sum [l, r] inclusive: prefix[r+1] - prefix[l]

Monotonic Stack

func nextGreater(arr []int) []int {
    n := len(arr)
    result := make([]int, n)
    for i := range result { result[i] = -1 }
    stack := []int{}  // stores indices
    for i := 0; i < n; i++ {
        for len(stack) > 0 && arr[i] > arr[stack[len(stack)-1]] {
            top := stack[len(stack)-1]
            stack = stack[:len(stack)-1]
            result[top] = arr[i]
        }
        stack = append(stack, i)
    }
    return result
}

Union-Find

type UnionFind struct {
    parent []int
    rank   []int
}

func NewUnionFind(n int) *UnionFind {
    parent := make([]int, n)
    rank := make([]int, n)
    for i := range parent { parent[i] = i }
    return &UnionFind{parent, rank}
}

func (uf *UnionFind) Find(x int) int {
    if uf.parent[x] != x {
        uf.parent[x] = uf.Find(uf.parent[x])  // path compression
    }
    return uf.parent[x]
}

func (uf *UnionFind) Unite(x, y int) bool {
    px, py := uf.Find(x), uf.Find(y)
    if px == py { return false }
    if uf.rank[px] < uf.rank[py] { px, py = py, px }
    uf.parent[py] = px
    if uf.rank[px] == uf.rank[py] { uf.rank[px]++ }
    return true
}

func (uf *UnionFind) Connected(x, y int) bool {
    return uf.Find(x) == uf.Find(y)
}

Quick Reference Cheat Sheet

TaskSnippet
Declare slices := []int{1, 2, 3}
Make slicemake([]int, n)
Appends = append(s, val)
Copy sliceappend([]int{}, s...)
Last elements[len(s)-1]
Pop (stack)s = s[:len(s)-1]
Reverse slicefor i,j:=0,len(s)-1; i<j; i,j=i+1,j-1 { s[i],s[j]=s[j],s[i] }
Sort ascendingsort.Ints(s)
Sort customsort.Slice(s, func(i,j int) bool { return s[i] < s[j] })
Binary searchsort.SearchInts(s, val)
Map defaultm[key] returns zero value — m[k]++ works
Map existsv, ok := m[k]
Setmap[int]bool{}
String buildervar sb strings.Builder; sb.WriteString(...)
String → intstrconv.Atoi(s)
Int → stringstrconv.Itoa(n)
Rune iterationfor i, r := range s { }
Bit NOT^x (not ~x)
Clear bit kx &^= 1 << k
Count set bitsbits.OnesCount(uint(x))
Max intmath.MaxInt
GCDfor b!=0 { a,b=b,a%b }; return a

Go-Specific Interview Gotchas

These are the traps that catch candidates who know Go casually but haven’t coded it under pressure:

GotchaWrongRight
Bitwise NOT~x^x
Clear bitx &= ~(1<<k)x &^= 1 << k
Slice copyb := a (same backing array)b := append([]int{}, a...)
math.Max on intmath.Max(a, b)write maxInt helper (or use Go 1.21 max)
String lengthlen(s) (bytes)len([]rune(s)) (chars)
Map nil panicvar m map[int]int; m[1]=1m := map[int]int{}
Heap pop typeheap.Pop(h)heap.Pop(h).(int) (type assert)
No built-in setset := []int{}set := map[int]bool{}

Comfortable with Go fundamentals? The next step is applying these tools to the core problem categories:

  • Arrays & Hashingmap[int]int frequency maps, map[int]bool sets, sliding window
  • Binary Searchsort.Search, sort.SearchInts, binary search on answer
  • Trees & Graphs — BFS with slice queue, recursive DFS, topological sort
  • Dynamic Programming — memoization with map[int]int, tabulation with []int
  • Heaps & Greedycontainer/heap interface, k-th largest, Dijkstra

Each of these patterns has dedicated walkthroughs on the Intervu blog.


Practice Under Real Interview Conditions

Go’s simplicity is a genuine strength in interviews — but only if you’re fluent enough that the syntax doesn’t slow you down. Forgetting the heap interface, blanking on sort.Slice’s comparator signature, or spending two minutes on a minInt helper can cost you under a real time limit.

The best way to build that fluency is repetition under pressure. Intervu gives you AI-powered mock coding interviews that simulate exactly that — timed, with follow-up questions, and instant feedback on your approach and your code.

Ready to put it to the test? Start a free mock interview on Intervu →

Practice Like It's the Real Interview

Get instant feedback on your approach, communication, and code — powered by AI.

Start a Mock Interview →