Big-O 표기법 기초
점근적 증가율을 살펴보는 이유, 상수와 저차항을 생략하는 방법, Big-O를 한눈에 읽는 방법을 이해합니다.
Big-O 표기법 기초은(는) CoddyKit의 무료 Coding Interview Prep 강의입니다. 이것은 4개 중 1번째 강의입니다. 아래에서 전체 강의를 무료로 읽을 수 있으며, 내장 코드 에디터와 24/7 AI 튜터와 함께 브라우저에서 직접 실습할 수 있습니다. 이 강의는 Coding Interview Prep 학습 경로의 일부이며, 진행 상황이 웹과 CoddyKit 앱에 동기화됩니다. Coding Interview Prep 강의에는 총 4개의 강의가 포함되어 있습니다.
알고리즘 효율성을 측정하는 이유
두 프로그램이 모두 올바르더라도 하나는 눈 깜짝할 사이에 끝나고 다른 하나는 몇 시간 동안 실행될 수 있습니다. 시간 복잡도는 입력이 커질 때 실행 시간이 어떻게 증가하는지 설명합니다.
# O(n) approach
def find_max_linear(nums):
m = nums[0]
for n in nums:
if n > m: m = n
return m
# O(n^2) approach (unnecessary double loop)
def find_max_quadratic(nums):
for i in range(len(nums)):
is_max = all(nums[i] >= nums[j] for j in range(len(nums)))
if is_max: return nums[i]
print(find_max_linear([3, 1, 4, 1, 5, 9])) # 9Big-O: 점근적 상한
Big-O는 비용이 증가하는 속도의 최악의 경우 상한을 설명합니다. 상수와 더 작은 항을 제거하는 것이 핵심입니다. 규모가 커지면 지배적인 항만 중요하기 때문입니다. 코드를 확인해 보십시오.
# T(n) = 3n^2 + 5n + 100 is O(n^2)
# because the n^2 term dominates for large n
# T(n) = 2n + 1000 is O(n)
# the constant 1000 becomes negligible
# Rule: drop constants and lower-order terms
# 5n^3 + 2n^2 + n + 1 => O(n^3)
# 100 * log(n) + n => O(n)
print('O(n^2) example: counting iterations')
n = 1000
count = sum(1 for i in range(n) for j in range(n))
print(count) # 1_000_000 = n^2일반적인 복잡도 유형
가장 빠른 것부터 가장 느린 것까지 다음과 같습니다. O(1), O(log n), O(n), O(n log n), O(n^2), O(2^n), O(n!). 이러한 유형을 알면 코드를 한 줄도 작성하기 전에 적절한 접근법을 선택할 수 있습니다.
import math
n = 1000
print(f'O(1): {1}')
print(f'O(log n): {int(math.log2(n))}')
print(f'O(n): {n}')
print(f'O(n log n): {int(n * math.log2(n))}')
print(f'O(n^2): {n**2}')
# O(2^n) for n=1000 is astronomically large
# O(n!) even larger상수를 생략하는 이유
5n단계를 실행하든 2n단계를 실행하든 둘 다 O(n)입니다. 상수는 알고리즘이 아니라 하드웨어에 따라 달라집니다. 빅오는 상수를 제외하므로 같은 기준에서 증가 양상을 비교할 수 있습니다.
# Both are O(n) — different constants
def count_a(n):
total = 0
for i in range(n): # n ops
total += 1
for i in range(n): # n ops
total += 1
return total # T(n) = 2n => O(n)
def count_b(n):
total = 0
for i in range(5 * n): # 5n ops
total += 1
return total # T(n) = 5n => O(n)
print(count_a(10), count_b(10)) # 20 50최선, 평균, 최악의 경우
빅오는 최악의 경우를, 오메가는 최선의 경우를, 세타는 둘 모두에 대한 정확한 경계를 나타냅니다. 면접관이 "복잡도"를 물으면 거의 항상 최악의 경우를 의미합니다.
def linear_search(nums, target):
for i, n in enumerate(nums):
if n == target:
return i # best case: target at index 0 => O(1)
return -1 # worst case: not found => O(n)
# Best case O(1): target is first element
print(linear_search([5,1,2,3], 5)) # 0
# Worst case O(n): target not in list
print(linear_search([1,2,3,4], 9)) # -1O(log n): 검색 공간 절반으로 줄이기
알고리즘이 각 단계에서 입력을 절반으로 줄이면 O(log n)입니다. 이진 검색처럼 동작하는 경우입니다. 항목이 10억 개여도 약 30단계만 필요하므로 매우 빠릅니다. 코드를 확인해 보세요.
def binary_search(arr, target):
lo, hi = 0, len(arr) - 1
steps = 0
while lo <= hi:
steps += 1
mid = (lo + hi) // 2
if arr[mid] == target:
return mid, steps
elif arr[mid] < target:
lo = mid + 1
else:
hi = mid - 1
return -1, steps
import math
arr = list(range(1000))
idx, s = binary_search(arr, 999)
print(f'Found at {idx} in {s} steps (log2(1000)~={math.log2(1000):.1f})')O(n log n): 정렬의 하한
비교 정렬은 최악의 경우 최소한 O(n log n)이 필요합니다. 이는 실제 수학적 하한입니다. 따라서 정렬 후 스캔의 전체 복잡도는 O(n^2)이 아니라 O(n log n)입니다. 코드에서 병합 정렬을 보여 줍니다.
# Merge sort: O(n log n)
def merge_sort(arr):
if len(arr) <= 1:
return arr
mid = len(arr) // 2
left = merge_sort(arr[:mid])
right = merge_sort(arr[mid:])
return merge(left, right)
def merge(a, b):
res, i, j = [], 0, 0
while i < len(a) and j < len(b):
if a[i] <= b[j]: res.append(a[i]); i+=1
else: res.append(b[j]); j+=1
return res + a[i:] + b[j:]
print(merge_sort([5,2,8,1,9,3])) # [1,2,3,5,8,9]상각 복잡도
상각 분석은 여러 연산에 걸쳐 비용의 평균을 계산합니다. 파이썬의 append는 상각 기준으로 O(1)입니다. 대부분은 즉시 끝나고, 드물게 발생하는 O(n) 크기 조정 비용이 모든 append에 걸쳐 분산되기 때문입니다.
# Dynamic array append is O(1) amortised
import sys
lst = []
capacities = []
for i in range(16):
lst.append(i)
capacities.append(sys.getsizeof(lst))
# Size jumps show reallocation events
for i, c in enumerate(capacities):
if i > 0 and capacities[i] != capacities[i-1]:
print(f'Realloc at i={i}, new size={c} bytes')코드에서 복잡도 알아보기
간단한 규칙은 반복문을 세는 것입니다. 반복문 하나는 O(n), 중첩된 반복문 두 개는 O(n^2), 절반씩 줄어드는 반복문은 O(log n)입니다. 서로 독립적인 순회는 add하고, 중첩된 반복문만 multiply합니다. 코드를 확인해 보세요.
# Two independent passes: O(n) + O(n) = O(n)
def two_passes(nums):
total = sum(nums) # O(n)
mean = total / len(nums)
diffs = [abs(n - mean) for n in nums] # O(n)
return max(diffs) # O(n)
# Overall: O(n) -- NOT O(n^2)
# Nested loops: O(n) * O(n) = O(n^2)
def all_pairs(nums):
pairs = []
for i in range(len(nums)): # O(n)
for j in range(i+1, len(nums)): # O(n)
pairs.append((nums[i], nums[j]))
return pairs # O(n^2)공간 복잡도 기초
공간 복잡도는 입력 자체를 제외하고 추가로 사용하는 메모리를 추적합니다. 제자리 뒤집기는 O(1)이고 해시 맵은 O(n)입니다. 시간과 공간을 맞바꿀 때는 항상 두 복잡도를 모두 밝혀야 합니다.
# O(1) space: reverse in-place
def reverse_inplace(arr):
l, r = 0, len(arr) - 1
while l < r:
arr[l], arr[r] = arr[r], arr[l]
l += 1; r -= 1
# O(n) space: create reversed copy
def reverse_copy(arr):
return arr[::-1]
a = [1, 2, 3, 4, 5]
reverse_inplace(a)
print(a) # [5, 4, 3, 2, 1]면접에서 복잡도 설명하기
질문을 받지 않아도 항상 복잡도를 먼저 말하세요. "시간 복잡도는 O(n log n)이고, 공간 복잡도는 O(n)입니다."라고 한 뒤 더 빠른 방법을 제안하세요. 이런 습관은 진정한 숙련도를 보여 줍니다.
# Example of explaining complexity step by step
def two_sum(nums, target):
# O(n) time: one pass through nums
# O(n) space: hash map stores up to n elements
seen = {} # value -> index
for i, n in enumerate(nums):
complement = target - n
if complement in seen: # O(1) lookup
return [seen[complement], i]
seen[n] = i
return []
print(two_sum([2, 7, 11, 15], 9)) # [0, 1]빠른 확인
빠르게 확인해 보겠습니다. 빅오와 복잡도 분류에 대해 무엇을 익혔는지 보여 주세요. 한 문제입니다. 충분히 해낼 수 있습니다. 🎯
학습 내용 복습
복습해 보겠습니다. 빅오는 상수를 제외한 최악의 경우 증가율을 나타냅니다. O(1)부터 O(n!)까지의 분류를 알고 있으며, 독립적인 반복문은 더하고 중첩된 반복문은 곱합니다.
자주 묻는 질문
“Big-O 표기법 기초” 강의는 무료인가요?
네 — “Big-O 표기법 기초” 전체 내용을 이 웹사이트에서 무료로 읽을 수 있습니다. 인터랙티브하게 실습하려면(내장 코드 에디터와 24/7 AI 튜터), CoddyKit PRO로 업그레이드하면 Coding Interview Prep 강의 전체를 잠금 해제할 수 있습니다. Coding Interview Prep 강의에는 총 4개의 강의가 포함되어 있습니다.
“Big-O 표기법 기초”에서 뭘 배우나요?
점근적 증가율을 살펴보는 이유, 상수와 저차항을 생략하는 방법, Big-O를 한눈에 읽는 방법을 이해합니다. 브라우저에서 직접 실행하는 실습 코드로 Coding Interview Prep을(를) 배우며, 24/7 AI 튜터가 강의를 진행하면서 질문에 답변해줍니다.
Coding Interview Prep을(를) 시작하는 데 경험이 필요한가요?
사전 경험은 필요하지 않습니다. CoddyKit의 Coding Interview Prep은(는) 초급자부터 고급 학습자까지를 위해 구성되어 있으므로, 여기서 시작하거나 처음부터 시작할 수 있으며 자신의 속도대로 진행할 수 있습니다. 이것은 4개 중 1번째 강의입니다.
“Big-O 표기법 기초” 강의는 얼마나 걸리나요?
대부분의 CoddyKit 강의는 약 5~10분이 소요됩니다. 각 강의는 간결하고 인터랙티브하여 꾸준한 진행이 가능하며, 웹과 앱에서 중단한 부분부터 바로 시작할 수 있습니다.
이 Coding Interview Prep 강의에서 코드를 작성하고 실행할 수 있나요?
네. 모든 Coding Interview Prep 강의에는 내장 코드 에디터가 포함되어 있으므로, 브라우저에서 바로 실제 코드를 작성하고 실행한 후 즉시 AI 피드백을 받을 수 있습니다 — 로컬 설정이 필요 없습니다.
이 강의의 모든 강의
- Big-O 표기법 기초
- 반복문과 중첩 반복문 분석
- 재귀와 재귀 트리 방법
- 공간 복잡도와 트레이드오프