0Pricing
Coding Interview Prep · 강의

공간 복잡도와 트레이드오프

호출 스택과 보조 자료 구조에 필요한 보조 공간을 측정하고, 메모이제이션과 제자리 알고리즘의 시간·공간 트레이드오프를 알아봅니다.

공간 복잡도와 트레이드오프은(는) CoddyKit의 무료 Coding Interview Prep 강의입니다. 이것은 4개 중 4번째 강의입니다. 아래에서 전체 강의를 무료로 읽을 수 있으며, 내장 코드 에디터와 24/7 AI 튜터와 함께 브라우저에서 직접 실습할 수 있습니다. 이 강의는 Coding Interview Prep 학습 경로의 일부이며, 진행 상황이 웹과 CoddyKit 앱에 동기화됩니다. Coding Interview Prep 강의에는 총 4개의 강의가 포함되어 있습니다.

공간 복잡도는 무엇을 측정할까요

공간 복잡도는 입력을 제외한 추가 메모리, 즉 보조 공간을 측정합니다. 변수 몇 개만 사용하면 O(1)이고, 결과 배열이나 해시 맵을 사용하면 O(n)입니다. 코드를 확인해 보세요.

# O(1) auxiliary space
def sum_array(nums):
    total = 0       # one integer variable
    for n in nums:
        total += n  # constant extra space
    return total

# O(n) auxiliary space
def copy_array(nums):
    return list(nums)  # allocates n slots

print(sum_array([1, 2, 3, 4]))  # 10
print(copy_array([1, 2, 3, 4]))  # [1, 2, 3, 4]

재귀 호출 스택 공간

재귀 호출마다 스택 프레임이 추가되므로 깊이가 공간을 결정합니다. 선형 재귀는 O(n)이고, 균형 잡힌 트리 DFS는 O(log n)입니다. 반복문 버전을 사용하면 이를 더 잘 제어할 수 있습니다.

import sys

def recursive_sum(n):
    if n == 0: return 0
    return n + recursive_sum(n - 1)
# Space: O(n) stack frames

def iterative_sum(n):
    total = 0
    while n > 0:
        total += n
        n -= 1
    return total
# Space: O(1)

print(recursive_sum(100))   # 5050
print(iterative_sum(100))   # 5050

병합 정렬 공간 복잡도: O(n)

병합 정렬은 임시 배열을 위해 O(n)의 추가 공간이 필요합니다. 이것이 안정적인 O(n log n) 정렬을 위한 비용입니다. 힙 정렬은 공간을 절약하지만 안정적이지 않습니다. 코드를 확인해 보세요.

import tracemalloc

tracemalloc.start()

def merge_sort(arr):
    if len(arr) <= 1: return arr
    m = len(arr) // 2
    l = merge_sort(arr[:m])    # new list
    r = merge_sort(arr[m:])    # new list
    out, i, j = [], 0, 0
    while i < len(l) and j < len(r):
        if l[i] <= r[j]: out.append(l[i]); i+=1
        else:             out.append(r[j]); j+=1
    return out + l[i:] + r[j:]

data = list(range(1000, 0, -1))
merge_sort(data)
_, peak = tracemalloc.get_traced_memory()
print(f'Peak memory: {peak} bytes')  # proportional to n

제자리 알고리즘: O(1) 공간

제자리 알고리즘은 비례해서 증가하는 추가 저장 공간 없이 입력을 직접 변경합니다. 두 포인터로 배열을 뒤집는 것이 한 예입니다. 따라서 공간 복잡도를 O(1)로 유지할 수 있습니다. 코드를 확인해 보세요.

def reverse_inplace(arr):
    l, r = 0, len(arr) - 1
    while l < r:
        arr[l], arr[r] = arr[r], arr[l]  # swap
        l += 1
        r -= 1
    # Space: O(1) -- only two pointer variables

def rotate_right(arr, k):
    '''Rotate array right by k positions in-place.'''
    n = len(arr)
    k %= n
    arr.reverse()          # O(1) space
    arr[:k] = arr[:k][::-1]
    arr[k:]  = arr[k:][::-1]

a = [1, 2, 3, 4, 5]
rotate_right(a, 2)
print(a)  # [4, 5, 1, 2, 3]

시간-공간 절충: 두 수의 합

시간-공간 절충은 어디에서나 나타납니다. 두 수의 합 문제는 O(1) 공간으로 O(n^2) 시간에 풀거나, 해시 맵을 사용해 O(n) 공간으로 O(n) 시간에 풀 수 있습니다. 두 방법을 모두 언급하고 무엇이 더 중요한지 물어보세요.

# O(n^2) time, O(1) space
def two_sum_slow(nums, target):
    for i in range(len(nums)):          # O(n)
        for j in range(i+1, len(nums)): # O(n)
            if nums[i] + nums[j] == target:
                return [i, j]
    return []

# O(n) time, O(n) space
def two_sum_fast(nums, target):
    seen = {}                    # O(n) space
    for i, n in enumerate(nums):
        comp = target - n
        if comp in seen:         # O(1) lookup
            return [seen[comp], i]
        seen[n] = i
    return []

print(two_sum_fast([2, 7, 11, 15], 9))  # [0, 1]

메모이제이션과 타뷸레이션의 공간 사용량

하향식 메모이제이션은 O(n)의 메모와 O(n)의 스택 공간을 사용하지만, 상향식 타뷸레이션은 스택을 사용하지 않습니다. 마지막 몇 행만 유지하면 공간을 O(1)로 줄일 수 있는데, 이를 공간 최적화 DP라고 합니다.

# Fibonacci: O(n) space with full table
def fib_table(n):
    dp = [0] * (n + 1)
    dp[1] = 1
    for i in range(2, n + 1):
        dp[i] = dp[i-1] + dp[i-2]
    return dp[n]

# O(1) space: keep only last two values
def fib_optimal(n):
    if n <= 1: return n
    a, b = 0, 1
    for _ in range(2, n + 1):
        a, b = b, a + b
    return b

print(fib_table(10))    # 55
print(fib_optimal(10))  # 55

해시 맵 공간 복잡도: O(n)

해시 맵은 풀이에서 흔히 O(n)의 공간 비용을 만듭니다. 방문한 항목을 기록하는 집합이나 개수를 세는 빈도 맵이 그 예입니다. 항상 이를 밝혀야 합니다. "시간 O(n), 공간 O(n)"이 완전한 답입니다.

def contains_duplicate(nums):
    # O(n) time, O(n) space
    seen = set()
    for n in nums:
        if n in seen: return True
        seen.add(n)
    return False

def group_anagrams(words):
    # O(n*m) time, O(n) space  (m = avg word length)
    from collections import defaultdict
    groups = defaultdict(list)
    for w in words:
        groups[tuple(sorted(w))].append(w)
    return list(groups.values())

print(contains_duplicate([1,2,3,1]))  # True
print(group_anagrams(['eat','tea','tan','ate','nat','bat']))

그래프 알고리즘의 공간 분석

그래프는 실제로 공간을 사용합니다. 인접 리스트는 O(V + E), BFS의 방문 집합과 큐는 O(V), DFS의 재귀 깊이는 O(V)입니다. 그래프의 공간 복잡도는 V와 E를 사용해 표현하세요.

from collections import deque

def bfs(graph, start):
    # Space: O(V) for visited set + O(V) for queue
    visited = set()      # O(V)
    queue = deque([start])  # O(V) max
    order = []
    while queue:
        node = queue.popleft()
        if node in visited: continue
        visited.add(node)
        order.append(node)
        for nb in graph.get(node, []):
            queue.append(nb)
    return order

g = {0:[1,2], 1:[3], 2:[3], 3:[]}
print(bfs(g, 0))  # [0, 1, 2, 3]

문자열과 배열 할당에서 주의할 점

숨은 메모리 할당으로 O(n)의 공간이 추가될 수 있습니다. 슬라이싱은 새 리스트를 만들고, 반복문에서 문자열에 +를 사용하면 O(n^2)이 됩니다. 정렬 함수는 복사본을 만들지만, lst.sort()는 제자리에서 동작합니다. 코드를 확인해 보세요.

# Hidden allocations:
nums = [1, 2, 3, 4, 5]

# Creates a NEW list -- O(n) space
slice_copy = nums[1:4]  # [2, 3, 4]

# Creates a NEW sorted list -- O(n) space
sorted_copy = sorted(nums)  # nums unchanged

# Sorts IN PLACE -- O(1) extra space
nums.sort()

print(slice_copy)   # [2, 3, 4]
print(sorted_copy)  # [1, 2, 3, 4, 5]
print(nums)         # [1, 2, 3, 4, 5]

면접에서 공간 절충 인식하기

공간 복잡도를 먼저 밝히세요. 면접관이 더 적은 공간을 원한다면 메모이제이션 대신 상향식 DP를 사용하거나, 해시 맵 대신 제자리 정렬을 사용하는 방법이 일반적입니다. 코드를 확인해 보세요.

# Problem: find if array has duplicates
# Option 1: O(1) time-per-check, O(n) space
def has_dup_hash(nums):
    return len(nums) != len(set(nums))

# Option 2: O(n log n) time, O(1) extra space
def has_dup_sort(nums):
    nums_copy = sorted(nums)  # O(n) space -- still!
    for i in range(1, len(nums_copy)):
        if nums_copy[i] == nums_copy[i-1]:
            return True
    return False

# Option 3: truly O(1) extra -- sort in-place
def has_dup_inplace(nums):
    nums.sort()               # modifies original
    for i in range(1, len(nums)):
        if nums[i] == nums[i-1]: return True
    return False

전체 복잡도 표기 템플릿

항상 시간과 공간을 포함한 완전한 설명을 제시하세요. "시간은 O(n), 추가 공간은 O(1)입니다."라고 말하면 됩니다. 절충점이 있다면 함께 언급하세요. 이것이 숙련된 지원자를 구분하는 요소입니다.

# Complete complexity example: Merge Intervals
def merge_intervals(intervals):
    # Time: O(n log n) for sort + O(n) for merge = O(n log n)
    # Space: O(n) for output (could be n/2 to n intervals)
    intervals.sort(key=lambda x: x[0])  # O(n log n)
    merged = [intervals[0]]
    for start, end in intervals[1:]:
        if start <= merged[-1][1]:
            merged[-1][1] = max(merged[-1][1], end)
        else:
            merged.append([start, end])
    return merged

print(merge_intervals([[1,3],[2,6],[8,10],[15,18]]))
# [[1,6],[8,10],[15,18]]

빠른 확인

빠르게 확인해 보겠습니다. 공간 복잡도에 대한 내용이 얼마나 잘 이해되었는지 살펴보세요. 이제 준비가 되었습니다. ✅

학습 내용 복습

복습해 보겠습니다. 보조 공간은 입력과 별도로 계산하고, 재귀는 깊이에 비례하는 O(depth) 스택 공간을 사용하며, 시간-공간 절충은 대부분의 알고리즘 설계 선택을 좌우합니다.

자주 묻는 질문

“공간 복잡도와 트레이드오프” 강의는 무료인가요?

네 — “공간 복잡도와 트레이드오프” 전체 내용을 이 웹사이트에서 무료로 읽을 수 있습니다. 인터랙티브하게 실습하려면(내장 코드 에디터와 24/7 AI 튜터), CoddyKit PRO로 업그레이드하면 Coding Interview Prep 강의 전체를 잠금 해제할 수 있습니다. Coding Interview Prep 강의에는 총 4개의 강의가 포함되어 있습니다.

“공간 복잡도와 트레이드오프”에서 뭘 배우나요?

호출 스택과 보조 자료 구조에 필요한 보조 공간을 측정하고, 메모이제이션과 제자리 알고리즘의 시간·공간 트레이드오프를 알아봅니다. 브라우저에서 직접 실행하는 실습 코드로 Coding Interview Prep을(를) 배우며, 24/7 AI 튜터가 강의를 진행하면서 질문에 답변해줍니다.

Coding Interview Prep을(를) 시작하는 데 경험이 필요한가요?

사전 경험은 필요하지 않습니다. CoddyKit의 Coding Interview Prep은(는) 초급자부터 고급 학습자까지를 위해 구성되어 있으므로, 여기서 시작하거나 처음부터 시작할 수 있으며 자신의 속도대로 진행할 수 있습니다. 이것은 4개 중 4번째 강의입니다.

“공간 복잡도와 트레이드오프” 강의는 얼마나 걸리나요?

대부분의 CoddyKit 강의는 약 5~10분이 소요됩니다. 각 강의는 간결하고 인터랙티브하여 꾸준한 진행이 가능하며, 웹과 앱에서 중단한 부분부터 바로 시작할 수 있습니다.

이 Coding Interview Prep 강의에서 코드를 작성하고 실행할 수 있나요?

네. 모든 Coding Interview Prep 강의에는 내장 코드 에디터가 포함되어 있으므로, 브라우저에서 바로 실제 코드를 작성하고 실행한 후 즉시 AI 피드백을 받을 수 있습니다 — 로컬 설정이 필요 없습니다.

이 강의의 모든 강의

  1. Big-O 표기법 기초
  2. 반복문과 중첩 반복문 분석
  3. 재귀와 재귀 트리 방법
  4. 공간 복잡도와 트레이드오프
← Coding Interview Prep(으)로 돌아가기