Skip to content

DSA Language Cheatsheet

Quick-reference for DSA interview rounds across JavaScript, TypeScript, Go, and Python 3.


DSA Interview Cheatsheet JS TS Go Python
Core ops: declare, init with size, slice/subarray, sort, reverse, two-pointer setup.
JavaScript
TypeScript
Go
Python 3
Declare / init
const a = [1,2,3]; const z = new Array(5).fill(0);
const a: number[] = [1,2,3]; const z = new Array<number>(5).fill(0);
a := []int{1,2,3} z := make([]int, 5) // all 0
a = [1, 2, 3] z = [0] * 5
Append / push
a.push(4); a.unshift(0); // front O(n)
a.push(4); a.unshift(0); // front O(n)
a = append(a, 4) // prepend: a = append([]int{0}, a...)
a.append(4) a.insert(0, 0) # front O(n)
Slice / subarray
a.slice(1, 3) // [1,3) copy a.splice(1, 2) // mutates
a.slice(1, 3) // [1,3) copy a.splice(1, 2) // mutates
a[1:3] // view, shares mem append([]int{}, a[1:3]...) // copy
a[1:3] # copy a[::-1] # reversed copy
Sort
a.sort((x,y)=>x-y); // nums a.sort(); // LEXICOGRAPHIC!
a.sort((x,y)=>x-y); // same pitfall as JS!
sort.Ints(a) sort.Slice(a, func(i,j int) bool { return a[i] < a[j] })
a.sort() # in-place sorted(a) # new list a.sort(key=lambda x: -x)
Two-pointer template
let l=0, r=a.length-1; while(l<r){ // logic l++; r--; }
let l=0, r=a.length-1; while(l<r){ // logic l++; r--; }
l, r := 0, len(a)-1 for l < r { // logic l++; r-- }
l, r = 0, len(a)-1 while l < r: # logic l += 1; r -= 1
String → char array & back
[..."hello"] // array arr.join("") // back
[..."hello"] // string[] arr.join("") // back
[]rune("hello") // unicode []byte("hello") // ASCII string(arr) // back
list("hello") # list of chars "".join(arr) # back to str
Frequency count, visited set, and existence check are the most common interview patterns.
JavaScript
TypeScript
Go
Python 3
Create map / dict
const m = new Map(); const obj = {}; // plain obj
const m = new Map<string,number>(); const obj: Record<string,number> = {};
m := make(map[string]int) m := map[string]int{"a":1}
m = {} m = {"a": 1}
Get / set / delete
m.get(k); m.set(k,v); m.delete(k); m.has(k);
m.get(k); m.set(k,v); m.delete(k); m.has(k);
v := m[k] // 0 if missing v, ok := m[k] // safe check delete(m, k)
m[k]; m[k]=v; del m[k] m.get(k, default) # safe
Frequency counter
const freq = new Map(); for (const x of arr) freq.set(x,(freq.get(x)??0)+1);
const freq = new Map<number,number>(); for (const x of arr) freq.set(x,(freq.get(x)??0)+1);
freq := make(map[int]int) for _, v := range arr { freq[v]++ }
from collections import Counter freq = Counter(arr) freq[x] += 1 # auto-init 0
Set operations
const s = new Set([1,2,3]); s.add(4); s.has(2); s.delete(2);
const s = new Set<number>([1,2]); s.add(4); s.has(2); s.delete(2);
// use map[T]bool or map[T]struct{} seen := make(map[int]bool) seen[x] = true _, ok := seen[x]
s = {1, 2, 3} s.add(4); 2 in s; s.discard(2)
Iterate map
for (const [k,v] of m) {} m.forEach((v,k) => {})
for (const [k,v] of m) {} // Object.entries(obj) for plain
for k, v := range m { // order NOT guaranteed }
for k, v in m.items(): ... for k in m: ... # keys only
Stack = LIFO. Queue = FIFO. JS/TS array shift() is O(n) — avoid for queues. Python deque is the correct choice.
JavaScript
TypeScript
Go
Python 3
Stack (LIFO)
const stk = []; stk.push(x); // O(1) stk.pop(); // O(1) stk[stk.length-1]; // peek
const stk: number[] = []; stk.push(x); stk.pop(); stk.at(-1); // peek
stk := []int{} stk = append(stk, x) top := stk[len(stk)-1] stk = stk[:len(stk)-1] // pop
stk = [] stk.append(x) # push O(1) stk.pop() # O(1) stk[-1] # peek
Queue (FIFO)
// array.shift() is O(n)! const q = []; q.push(x); // enqueue q.shift(); // dequeue O(n) ⚠
// same O(n) caveat const q: number[] = []; q.push(x); q.shift(); // O(n) ⚠
q := []int{} q = append(q, x) // enqueue front := q[0] q = q[1:] // dequeue
from collections import deque q = deque() q.append(x) # O(1) q.popleft() # O(1) ✓
Monotonic stack pattern
const stk = []; for (const x of arr) { while(stk.length && stk.at(-1) > x) stk.pop(); stk.push(x); }
const stk: number[] = []; for (const x of arr) { while(stk.length && stk.at(-1)! > x) stk.pop(); stk.push(x); }
stk := []int{} for _, x := range arr { for len(stk)>0 && stk[len(stk)-1]>x { stk = stk[:len(stk)-1] } stk = append(stk, x) }
stk = [] for x in arr: while stk and stk[-1] > x: stk.pop() stk.append(x)
JS/TS have no built-in heap — write a class or use the negate trick with a MinHeap. Python heapq and Go's container/heap are min-heaps natively.
JavaScript
TypeScript
Go
Python 3
Min-heap push / pop
// No built-in. Minimal class: class MinHeap { push(v) { /* sift up */ } pop() { /* sift down */ } peek() { return this.h[0] } }
class MinHeap<T> { constructor(private cmp: (a:T,b:T)=>number) {} push(v:T) { /* ... */ } pop(): T { /* ... */ } }
import "container/heap" // implement heap.Interface: // Len, Less, Swap, Push, Pop heap.Push(&h, x) heap.Pop(&h)
import heapq h = [] heapq.heappush(h, x) x = heapq.heappop(h) h[0] # peek min
Max-heap trick
// Negate → use MinHeap heap.push(-x); const max = -heap.pop();
// same negate trick heap.push(-x); const max = -heap.pop();
// flip Less: a[i] > a[j] // or negate values before Push
# negate for max-heap heapq.heappush(h, -x) max_val = -heapq.heappop(h)
Heapify existing array
// O(n log n) — push each arr.forEach(x => h.push(x));
// same O(n log n)
h := append(IntHeap(nil), arr...) heap.Init(&h) // O(n)
heapq.heapify(arr) # O(n) # modifies in-place
Top-K pattern
for (const x of arr) { h.push(x); if (h.size() > k) h.pop(); } // heap has top-k elements
for (const x of arr) { h.push(x); if (h.size() > k) h.pop(); }
for _, x := range arr { heap.Push(&h, x) if h.Len() > k { heap.Pop(&h) } }
# simplest way heapq.nlargest(k, arr) heapq.nsmallest(k, arr)
Dummy head eliminates edge cases. Fast/slow pointer detects cycles and finds midpoints. Always track prev for in-place reversal.
JavaScript
TypeScript
Go
Python 3
Node definition
class ListNode { constructor(val=0, next=null) { this.val=val; this.next=next; } }
class ListNode { constructor( public val=0, public next: ListNode|null=null ) {} }
type ListNode struct { Val int Next *ListNode }
class ListNode: def __init__(self, val=0, nxt=None): self.val = val self.next = nxt
Reverse in-place
let prev=null, cur=head; while(cur){ let nxt=cur.next; cur.next=prev; prev=cur; cur=nxt; } return prev;
let prev: ListNode|null=null; let cur=head; while(cur){ const nxt=cur.next; cur.next=prev; prev=cur; cur=nxt; } return prev;
var prev *ListNode cur := head for cur != nil { nxt := cur.Next cur.Next = prev prev = cur; cur = nxt } return prev
prev, cur = None, head while cur: nxt = cur.next cur.next = prev prev, cur = cur, nxt return prev
Fast / slow pointer
let slow=head, fast=head; while(fast&&fast.next){ slow=slow.next; fast=fast.next.next; } // slow = midpoint
let slow=head, fast=head; while(fast?.next){ slow=slow!.next!; fast=fast.next.next; } // slow = midpoint
slow, fast := head, head for fast != nil && fast.Next != nil { slow = slow.Next fast = fast.Next.Next } // slow = midpoint
slow = fast = head while fast and fast.next: slow = slow.next fast = fast.next.next # slow = midpoint
Dummy head pattern
const dummy = new ListNode(0); dummy.next = head; let cur = dummy; // build / modify ... return dummy.next;
const dummy = new ListNode(0); dummy.next = head; let cur: ListNode = dummy; return dummy.next;
dummy := &ListNode{} dummy.Next = head cur := dummy // ... return dummy.Next
dummy = ListNode(0) dummy.next = head cur = dummy # ... return dummy.next
BFS for shortest path / level-order. DFS for path/subtree problems. Use iterative DFS for deep trees to avoid stack overflow.
JavaScript
TypeScript
Go
Python 3
Tree node
class TreeNode { constructor(val=0, left=null, right=null) { this.val=val; this.left=left; this.right=right; } }
class TreeNode { constructor(public val=0, public left:TreeNode|null=null, public right:TreeNode|null=null ){} }
type TreeNode struct { Val int Left, Right *TreeNode }
class TreeNode: def __init__(self, val=0, left=None, right=None): self.val=val self.left=left; self.right=right
DFS inorder (iterative)
const stk=[], res=[]; let cur=root; while(cur||stk.length){ while(cur){stk.push(cur);cur=cur.left;} cur=stk.pop();res.push(cur.val); cur=cur.right; }
const stk:TreeNode[]=[]; let cur:TreeNode|null=root; while(cur||stk.length){ while(cur){stk.push(cur);cur=cur.left;} cur=stk.pop()!;res.push(cur.val); cur=cur.right; }
stk := []*TreeNode{}; cur := root for cur != nil || len(stk) > 0 { for cur != nil { stk = append(stk, cur); cur = cur.Left } n := len(stk)-1 cur = stk[n]; stk = stk[:n] res = append(res, cur.Val); cur = cur.Right }
stk, cur, res = [], root, [] while cur or stk: while cur: stk.append(cur); cur = cur.left cur = stk.pop() res.append(cur.val); cur = cur.right
BFS level-order
const q=[root]; while(q.length){ const node=q.shift(); if(node.left) q.push(node.left); if(node.right) q.push(node.right); }
const q=[root]; while(q.length){ const node=q.shift()!; if(node.left) q.push(node.left); if(node.right) q.push(node.right); }
q := []*TreeNode{root} for len(q) > 0 { node := q[0]; q = q[1:] if node.Left != nil { q = append(q, node.Left) } if node.Right != nil { q = append(q, node.Right) } }
from collections import deque q = deque([root]) while q: node = q.popleft() if node.left: q.append(node.left) if node.right: q.append(node.right)
Adjacency list + visited
const g = new Map(); g.set(u, [...(g.get(u)??[]), v]); const vis = new Set();
const g = new Map<number,number[]>(); const vis = new Set<number>();
g := make(map[int][]int) g[u] = append(g[u], v) vis := make(map[int]bool)
from collections import defaultdict g = defaultdict(list) g[u].append(v) vis = set()
Binary search invariant: what does lo always satisfy? What does hi always satisfy? The answer is lo when the loop ends.
JavaScript
TypeScript
Go
Python 3
Binary search (exact)
let lo=0, hi=n-1; while(lo<=hi){ const mid=(lo+hi)>>1; if(a[mid]===t) return mid; else if(a[mid]<t) lo=mid+1; else hi=mid-1; }
let lo=0, hi=n-1; while(lo<=hi){ const mid=(lo+hi)>>1; if(a[mid]===t) return mid; else if(a[mid]<t) lo=mid+1; else hi=mid-1; }
lo, hi := 0, len(a)-1 for lo <= hi { mid := (lo + hi) >> 1 if a[mid] == t { return mid } else if a[mid] < t { lo = mid+1 } else { hi = mid-1 } }
import bisect # use bisect_left for left-bound lo, hi = 0, len(a)-1 while lo <= hi: mid = (lo+hi)>>1 if a[mid]==t: return mid elif a[mid]<t: lo=mid+1 else: hi=mid-1
Left-bound (first ≥ target)
let lo=0, hi=n; while(lo<hi){ const mid=(lo+hi)>>1; if(a[mid]<t) lo=mid+1; else hi=mid; } // lo = first index >= t
let lo=0, hi=n; while(lo<hi){ const mid=(lo+hi)>>1; if(a[mid]<t) lo=mid+1; else hi=mid; }
lo, hi := 0, len(a) for lo < hi { mid := (lo+hi) >> 1 if a[mid] < t { lo = mid+1 } else { hi = mid } } // or: sort.SearchInts(a, t)
bisect.bisect_left(a, t) # leftmost i s.t. a[i] >= t bisect.bisect_right(a, t) # leftmost i s.t. a[i] > t
Custom sort comparator
arr.sort((a,b) => { if(a.x !== b.x) return a.x-b.x; return b.y-a.y; // desc y });
arr.sort((a,b): number => { if(a.x !== b.x) return a.x-b.x; return b.y-a.y; });
sort.Slice(arr, func(i,j int) bool { if arr[i].X != arr[j].X { return arr[i].X < arr[j].X } return arr[i].Y > arr[j].Y })
from functools import cmp_to_key arr.sort(key=lambda x: (x.x, -x.y)) # tuple key: primary asc, secondary desc # cmp_to_key for complex 3-way compare
Define: state, transition, base case, answer location. Top-down is easier to write; bottom-up avoids recursion depth limits.
JavaScript
TypeScript
Go
Python 3
Top-down memo
const memo = new Map(); function dp(i) { if(memo.has(i)) return memo.get(i); const res = /* recurrence */; memo.set(i, res); return res; }
const memo = new Map<number,number>(); function dp(i: number): number { if(memo.has(i)) return memo.get(i)!; const res = /* ... */; memo.set(i, res); return res; }
memo := make(map[int]int) var dp func(int) int dp = func(i int) int { if v, ok := memo[i]; ok { return v } res := /* ... */ memo[i] = res; return res }
from functools import lru_cache @lru_cache(maxsize=None) def dp(i): if base_case: return ... return # recurrence
Bottom-up 1D
const dp = new Array(n+1).fill(0); dp[0] = 1; // base case for(let i=1;i<=n;i++) dp[i] = dp[i-1] + dp[i-2];
const dp: number[] = new Array(n+1).fill(0); dp[0] = 1; for(let i=1;i<=n;i++) dp[i] = dp[i-1] + dp[i-2];
dp := make([]int, n+1) dp[0] = 1 for i := 1; i <= n; i++ { dp[i] = dp[i-1] + dp[i-2] }
dp = [0] * (n + 1) dp[0] = 1 for i in range(1, n + 1): dp[i] = dp[i-1] + dp[i-2]
2D DP (knapsack / LCS)
const dp = Array.from({length:m+1}, () => new Array(n+1).fill(0)); for(let i=1;i<=m;i++) for(let j=1;j<=n;j++) dp[i][j] = /* transition */;
const dp:number[][] = Array.from( {length:m+1},()=>new Array(n+1).fill(0)); for(let i=1;i<=m;i++) for(let j=1;j<=n;j++) {}
dp := make([][]int, m+1) for i := range dp { dp[i] = make([]int, n+1) } for i:=1;i<=m;i++ { for j:=1;j<=n;j++ {} }
dp = [[0]*(n+1) for _ in range(m+1)] for i in range(1, m+1): for j in range(1, n+1): dp[i][j] = ...
Infinity init
dp.fill(Infinity); // min problems dp.fill(-Infinity); // max problems
dp.fill(Infinity); dp.fill(-Infinity);
import "math" math.MaxInt math.MinInt // or: 1<<60 and -(1<<60)
dp = [float('inf')] * n dp = [float('-inf')] * n
Language-specific gotchas that cost time in interviews. Know these cold.
JavaScript pitfalls
// Sort is LEXICOGRAPHIC by default! [10,9,1].sort() // [1,10,9] WRONG [10,9,1].sort((a,b)=>a-b) // [1,9,10] ✓ // Integer division 7 / 2 // 3.5 (not 3!) Math.floor(7/2) // 3 (7/2)|0 // 3 (bitwise) // Char codes "a".charCodeAt(0) // 97 String.fromCharCode(97) // "a"
TypeScript extras
// Non-null assertion when sure arr.pop()! // T, not T|undefined // as const for literal tuple types const dirs = [[-1,0],[1,0]] as const // Tuple key pitfall in Map! // [1,2] !== [1,2] — use "1,2" string const key = `${r},${c}` // Readonly prevents accidental mutation function f(a: Readonly<number[]>) {}
Go gotchas
// Slice is a VIEW — copy if needed b := make([]int, len(a)) copy(b, a) // Missing map key returns zero value v, ok := m[k] // always use 2-val form // No built-in min/max for ints if a > b { return a }; return b // String concat in loop → Builder var sb strings.Builder sb.WriteString(s) result := sb.String()
Python power moves
# No integer overflow (arbitrary precision) # float('inf') works in min() / max() # Tuple as hashable set/dict key seen.add((r, c)) # Swap without temp variable a, b = b, a # Zip for matrix transpose list(zip(*matrix)) # enumerate + tuple unpacking for i, v in enumerate(arr): ... # defaultdict avoids KeyError from collections import defaultdict
Complexity quick-ref
OperationJS / TSGoPython
Array push / popO(1) amortO(1) amortO(1) amort
Array shift (front)O(n) ⚠O(n) ⚠deque O(1)
Map get / setO(1) avgO(1) avgO(1) avg
SortO(n log n)O(n log n)O(n log n)
Heap push / popO(log n)O(log n)O(log n)
Binary searchO(log n)O(log n)O(log n)