인사이트 출처 : https://joyhong-91.tistory.com/12#whatBO, https://zoosso.tistory.com/883, https://blog.chulgil.me/algorithm/, https://velog.io/@abc2752/시간-복잡도Time-Complexity
입력값과 연산 수행 시간의 상관관계를 나타내는 척도.
최상은 오메가 표기법, 평균은 세타 표기법, 최악은 빅오 표기법
시간복잡도는 최악을 기준으로 성능을 예측한다.
빅오 표기법에서 시간복잡도는 입력된 N의 크기에 따라 실행되는 조작의 수
공간복잡도는 알고리즘이 실행될 때 사용되는 메모리의 양
시간 복잡도를 잘 이해해야 하는 이유
코딩 테스트를 통과하기 위한 것도 있지만, 개발자라면 여러 코드를 짬에 있어서 코드의 성능을 개선하고, 실행 시간을 단축시킬 수 있다.
단순히 돌아가는 코드를 짤 수 있지만 최적화 적인 측면에서 시간복잡도 개념을 알고 있으면 성능향상을 시도할 수 있는 발판이 된다.
코딩 테스트 시간 복잡도 판단법 (Python 기준)
1초 = 약 1억 번(2 * 10^8) 연산을 할 수 있다.
시간복잡도가 ~이면 최대 연산 횟수 ~ 번 가능
O(logN) → 약 10억번 가능
O(n) → 약 1억번 가능
O(N log N) - 약 100,000번 가능
O(N²) - 약 2,000번 가능
O(N³) → 약 500번 가능

설명
예시
배열(첫, 마지막) 요소 접근, 해시 테이블 키를 통해 조회, 스택의 push, pop
배열의 첫 번째 또는 마지막 요소에 접근하는 경우
def get_first_element(arr):
return arr[0]
해시 테이블에서 키를 사용해 값을 조회하는 경우 (평균적인 경우)
def get_value(hash_table, key):
return hash_table[key]
스택의 push 또는 pop 연산
def push(stack, item):
stack.**append**(item)

설명
왜 입력을 반복적으로 절반으로 나누는 알고리즘에서 나타나는가
지수적 감소
각 단계마다 처리해야 할 데이터의 양이 절반으로 줄어든다.
이는 문제의 크기가 지수적으로 감소함 의미.
로그의 정의
log₂n은 2를 몇 번 곱해야 n이 되는가?를 나타낸다. 이는 n을 몇 번 2로 나누어야 1이 되는가?와 동일한 의미
효율성
입력 크기가 증가해도 처리 시간이 느리게 증가.
예 : 입력이 2배가 되어도 처리 시간은 1만큼만 증가
대표 예시로 이진 탐색
알고리즘 과정
정렬된 배열에서 중간 요소 확인.
찾는 값과 비교하여 왼쪽 또는 오른쪽 절반을 선택.
선택된 절반에서 과정을 반복
단계 수
각 단계마다 검색 범위가 절반 줄어듬.
n개의 요소를 가진 배열은 최대 log₂n 단계 후에 단일 요소로 줄어듬.
// 이진 탐색
def binary_search(arr, target):
left, right = 0, len(arr) - 1
while left <= right:
mid = (left + right) // 2
if arr[mid] == target:
return mid
elif arr[mid] < target:
left = mid + 1# 오른쪽 절반 선택
else:
right = mid - 1# 왼쪽 절반 선택
return -1# 찾지 못한 경우
복잡도 분석
1,000,000개의 요소를 가진 배열에서도 최대 약 20번의 비교만으로 검색 가능.
8개(3), 16개(4), 32개(5), 1024(10)
log₂(1,000,000) ≈ 19.93
이러한 특성 때문에 O(log n) 알고리즘은 대규모 데이터셋에서도 매우 효율적으로 동작한다.

설명
배열의 모든 요소, 선형 검색 알고리즘
배열의 모든 요소를 순회하는 경우
def find_max(arr):
max_val = arr[0]
for num in arr:
if num > max_val:
max_val = num
return max_val
선형 검색 알고리즘
def linear_search(arr, target):
for i, num in enumerate(arr):
if num == target:
return i
return -1

설명
왜 효율적인 정렬 알고리즘에서 나타나는가
분할 정복 전략
많은 효율적인 정렬 알고리즘(예: 병합 정렬, 퀵 정렬)이 분할 정복 전략을 사용. 이 전략은 문제를 더 작은 부분 문제로 나누고, 각 부분을 해결한 뒤 결과를 합치는 방식이다.
로그적 분할
선형적 결합
총 복잡도
예시로 병합정렬
병합 정렬 (Merge Sort)
병합 정렬의 동작 과정 요약
따라서, 병합 정렬의 총 시간 복잡도는 배열을 나누는 log n번의 과정과 각 단계에서 이루어지는 n번의 병합 작업이 결합 O(n log n)
장점
O(n log n)의 시간 복잡도로 일관된 성능을 보장. 특히 최악의 경우에도 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(left, right):
result = []
i, j = 0, 0
while i < len(left) and j < len(right):
if left[i] <= right[j]:
result.append(left[i])
i += 1
else:
result.append(right[j])
j += 1
result.extend(left[i:])
result.extend(right[j:])
return result

설명
예시
버블 정렬(이중 for문)
def bubble_sort(arr):
n = len(arr)
for i in range(n):
for j in range(0, n-i-1):
if arr[j] > arr[j+1]:
arr[j], arr[j+1] = arr[j+1], arr[j]
return arr
선택 정렬
모든 가능한 쌍을 비교하는 알고리즘