---
id: cs-big-o-practical
title: Big-O 실전 — 실제 성능 / 측정 / 함정
category: Coding
status: draft
source_trust_level: B
verification_status: conceptual
created_at: 2026-05-09
updated_at: 2026-05-09
tags: [cs, algorithm, big-o, vibe-coding]
tech_stack: { language: "TS", applicable_to: ["Backend", "Frontend"] }
applied_in: []
aliases: [Big O, time complexity, space complexity, amortized, hidden constant]
---

# Big-O 실전

> Big-O 가 알고리즘의 핵심 지표. **그러나 hidden constant + cache locality + 입력 크기 도 중요**. O(N) 가 O(log N) 보다 느릴 수 있다 (작은 N).

## 📖 핵심 개념
- O(1): 상수.
- O(log N): 이진 검색.
- O(N): linear scan.
- O(N log N): merge sort, sort.
- O(N²): nested loop.
- O(2^N), O(N!): 거의 사용 X (작은 N).

## 💻 코드 패턴

### 자주 쓰는 자료구조 복잡도
```
Array (JS):
  push/pop:       O(1) amortized
  shift/unshift:  O(N) — 큰 시작 element 이동
  index access:   O(1)
  indexOf:        O(N)

Map / Set (V8):
  get/set/has:    O(1) average (hash collision 시 worst O(N))
  iteration:      insertion order

Object:
  property access: O(1) average

LinkedList:
  prepend/append: O(1)
  index access:   O(N)

Heap (priority queue):
  insert/pop:     O(log N)

Binary search:
  on sorted array: O(log N)

Sort:
  Array.sort:     O(N log N) (TimSort in V8)
```

### Common 변환
```ts
// O(N²) — nested
for (const x of arr) {
  for (const y of arr) {
    if (x.id === y.parent) ...;
  }
}

// O(N) with Map
const byId = new Map(arr.map(x => [x.id, x]));
for (const x of arr) {
  const parent = byId.get(x.parent);
  if (parent) ...;
}
```

```ts
// O(N²) — array.includes
for (const x of arr) {
  if (otherArr.includes(x.id)) ...;  // O(M) per loop
}

// O(N+M)
const set = new Set(otherArr);
for (const x of arr) {
  if (set.has(x.id)) ...;  // O(1)
}
```

### Hidden constant
```
O(log N) binary search:
- ~20 comparison for 1M items
- 매 comparison = 비교 + index calc

O(N) linear with simd:
- 1M cmp = 100ns 같은 cache friendly

→ 작은 N (<100) = 상수 차이 큼.
N = 100K → log N (17) vs N = 차이 명확.
```

### Cache locality
```ts
// ✅ Sequential access — cache 친화
for (let i = 0; i < arr.length; i++) sum += arr[i];

// ❌ Random — cache miss
for (const i of shuffled) sum += arr[i];
// → 5-20x 느림
```

### Amortized
```ts
// Array.push: 보통 O(1).
// 가끔 capacity 확장 → 모두 copy = O(N).
// 평균 O(1) (amortized).

// Hash map resize: 같은 원리.
```

### 작은 vs 큰 N
```
N = 10:    O(N²) = 100, OK.
N = 1000:  O(N²) = 1M, 느려질 수 있음.
N = 1M:    O(N²) = 10^12, 절대 X. O(N log N).
N = 1G:    O(N) 도 비쌈. 분산.
```

→ 입력 크기 모르면 안전하게 좋은 알고리즘.

### 측정
```ts
const t = performance.now();
const result = myFunction(input);
console.log('took', performance.now() - t, 'ms');

// 더 정확
const COUNT = 10000;
let total = 0;
for (let i = 0; i < COUNT; i++) {
  const t = performance.now();
  myFunction(input);
  total += performance.now() - t;
}
console.log('avg', total / COUNT, 'ms');
```

### Common O(N log N)
```ts
// Sort
arr.sort((a, b) => a.value - b.value);

// 그 후 binary search
function find(target: number): number | undefined {
  let lo = 0, hi = arr.length - 1;
  while (lo <= hi) {
    const mid = (lo + hi) >> 1;
    if (arr[mid].value === target) return arr[mid];
    if (arr[mid].value < target) lo = mid + 1;
    else hi = mid - 1;
  }
}
```

→ Sort 한 번 + N 검색 = O(N log N). N 검색 X = O(N²).

### Top-K (heap)
```ts
import Heap from 'heap-js';

function topK(arr: number[], k: number): number[] {
  const minHeap = new Heap<number>();
  for (const x of arr) {
    minHeap.push(x);
    if (minHeap.size() > k) minHeap.pop();  // 가장 작은 제거
  }
  return minHeap.toArray();  // top K
}

// O(N log K) — N log N (sort) 보다 빠름.
```

### Sliding window
```ts
// 합 N 길이 array 의 K 길이 sub 의 max
function maxSumWindow(arr: number[], k: number): number {
  let sum = 0;
  for (let i = 0; i < k; i++) sum += arr[i];
  let max = sum;
  for (let i = k; i < arr.length; i++) {
    sum += arr[i] - arr[i - k];  // O(1) update
    max = Math.max(max, sum);
  }
  return max;
}
// O(N) — 매번 sum 다시 X.
```

### Two pointer
```ts
// Sorted array — sum = target?
function twoSum(arr: number[], target: number): [number, number] | null {
  let lo = 0, hi = arr.length - 1;
  while (lo < hi) {
    const sum = arr[lo] + arr[hi];
    if (sum === target) return [lo, hi];
    if (sum < target) lo++;
    else hi--;
  }
  return null;
}
// O(N).
```

### Dynamic programming
```ts
// Fibonacci O(2^N) → O(N) memo
const memo = new Map<number, number>();
function fib(n: number): number {
  if (n <= 1) return n;
  if (memo.has(n)) return memo.get(n)!;
  const r = fib(n - 1) + fib(n - 2);
  memo.set(n, r);
  return r;
}
```

### Graph
```ts
// BFS — O(V + E)
function bfs(start: Node) {
  const visited = new Set<Node>();
  const queue = [start];
  while (queue.length > 0) {
    const node = queue.shift()!;  // ❌ O(N) shift
    // 또는 더 나은 = deque 또는 index
    if (visited.has(node)) continue;
    visited.add(node);
    queue.push(...node.neighbors);
  }
}
```

→ Array shift 가 O(N). 큰 BFS = deque library.

### When to optimize
```
1. Profile first — hot path 만.
2. Big-O 가 일반 답.
3. Cache / locality 가 micro-opt.
4. Algorithm > implementation.

"Premature optimization is the root of all evil." (Knuth)
But: O(N²) → O(N) 는 premature 가 아님.
```

### V8 (JavaScript) 특이
```
.shift() / .unshift(): O(N).
.push() / .pop(): O(1) amortized.

Object property access:
- 같은 shape: O(1)
- 다른 shape mix: 느림 (위 V8 문서)
```

### Memory complexity
```ts
// O(N²) memory
const matrix: number[][] = [];
for (let i = 0; i < N; i++) {
  matrix[i] = [];
  for (let j = 0; j < N; j++) matrix[i][j] = ...;
}

// 큰 N (10K+) = OOM 가능.
```

### Algorithm 선택 cheat sheet
```
Search:
  Sorted: binary search O(log N)
  Unsorted: linear O(N) 또는 hash set O(1)

Sort:
  General: TimSort (V8) O(N log N)
  Small (<10): insertion O(N²) but 빠름
  Distinct integers: counting / radix O(N)

Top-K:
  K << N: heap O(N log K)
  K close to N: sort O(N log N)

Group:
  reduce / Map O(N)

Distinct:
  Set O(N)

Combinations: backtracking O(2^N) — 작은 N 만.
```

## 🤔 의사결정 기준
| 입력 크기 | 알고리즘 |
|---|---|
| <100 | 어떤 거나 OK |
| <10K | O(N²) 일부 가능 |
| <1M | O(N log N) 안정 |
| <1B | O(N) 또는 분산 |
| 1B+ | 분산 + sampling |

## ❌ 안티패턴
- **`Array.includes` in loop**: O(N²). Set / Map.
- **`arr.shift()` 반복**: O(N²). Index 또는 deque.
- **모든 곳 micro-opt**: 가독성 잃음.
- **Big-O 무시 + V8 trick**: 알고리즘 우선.
- **Sort 매번**: cache.
- **Recursion + 작은 N — overhead 가 cost**: iterative.
- **JSON.parse 큰 string**: O(N) but 큰 constant. stream parser.

## 🤖 LLM 활용 힌트
- Big-O 우선 — 그 후 micro.
- Set / Map = 80% 개선의 답.
- Profile → hot path.

## 🔗 관련 문서
- [[Perf_V8_Optimization]]
- [[CS_Bloom_Filter]]
- [[CS_Probabilistic_Data_Structures]]
