// O(1): 항상 1번 실행
function accessFirstElement(arr) {
return arr[0];
}
// O(n): 배열 순회
function printAll(arr) {
arr.forEach(item => console.log(item));
}
// O(n²): 이중 반복문
function doubleLoop(arr) {
for (let i = 0; i < arr.length; i++) {
for (let j = 0; j < arr.length; j++) {
console.log(arr[i], arr[j]);
}
}
}
push와 pop만 쓰는 구조인가?const stack = [];
stack.push("(");
stack.push("(");
stack.pop(); // 닫힘 괄호와 비교
stack.pop(); // 또 비교
push()는 새로운 상태를 저장pop()은 가장 나중에 넣은 것을 꺼내어 현재 상황과 비교하거나 처리shift()로 꺼내는 구조인가?const queue = [];
queue.push("요청1");
queue.push("요청2");
const current = queue.shift(); // "요청1" 처리
push()는 대기열에 추가shift()는 제일 먼저 들어온 요청부터 처리→ 이 구조를 안 지키면 사용자 경험이 무너질 수 있음
(ex: 나중에 들어온 요청이 먼저 응답되는 웹 앱)
DFS는 한 방향으로 끝까지 파고드는 방식이기 때문에
→ 재귀적으로 탐색 깊이를 따라가는 구조에 자연스럽게 맞음
BFS는 가까운 노드부터 넓게 탐색하므로
→ 방문 순서를 큐에 저장해서 순차 처리해야 함
function dfs(node, visited = new Set()) {
if (visited.has(node)) return;
visited.add(node);
for (const neighbor of graph[node]) {
dfs(neighbor, visited);
}
}
function bfs(start) {
const visited = new Set();
const queue = [start];
while (queue.length) {
const node = queue.shift();
if (visited.has(node)) continue;
visited.add(node);
for (const neighbor of graph[node]) {
queue.push(neighbor);
}
}
}
queue.push(neighbor)는 다음에 방문해야 할 노드를 대기열에 등록function fib(n) {
if (n <= 1) return n;
return fib(n - 1) + fib(n - 2); // O(2ⁿ)
}
function fibDP(n) {
const dp = [0, 1];
for (let i = 2; i <= n; i++) {
dp[i] = dp[i - 1] + dp[i - 2]; // O(n)
}
return dp[n];
}
dp[i]는 이전 두 항의 합을 미리 저장해두고 쓰는 방식| 알고리즘/자료구조 | 왜 사용하나 (상황) | 왜 그렇게 구현하나 (핵심 원리) |
|---|---|---|
| 스택 | 최근 상태 먼저 처리 (괄호검사, 되돌리기) | 후입선출 구조 필요 → push/pop 사용 |
| 큐 | 순차 처리 필요 (BFS, 대기열) | 선입선출 유지 → push/shift 구조로 구현 |
| DFS | 깊이 우선 탐색 (끝까지 파고들기) | 재귀 호출이 깊이 탐색과 자연스럽게 연결됨 |
| BFS | 가까운 노드부터 탐색 (최단거리) | 순서대로 방문 → 큐 사용해 대기 노드 관리 |
| DP | 중복 계산 제거 (피보나치, 최적화 문제) | 계산 결과를 배열/객체에 저장하여 재사용 (메모이제이션) |