Files
2nd/10_Wiki/Topics/Coding/CS_Big_O_Practical.md
T
2026-05-09 21:08:02 +09:00

7.1 KiB

id, title, category, status, source_trust_level, verification_status, created_at, updated_at, tags, tech_stack, applied_in, aliases
id title category status source_trust_level verification_status created_at updated_at tags tech_stack applied_in aliases
cs-big-o-practical Big-O 실전 — 실제 성능 / 측정 / 함정 Coding draft B conceptual 2026-05-09 2026-05-09
cs
algorithm
big-o
vibe-coding
language applicable_to
TS
Backend
Frontend
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 변환

// 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) ...;
}
// 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

// ✅ 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

// 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) 도 비쌈. 분산.

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

측정

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)

// 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)

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

// 합 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

// 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

// 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

// 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

// 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.

🔗 관련 문서