빅오 표기법은 알고리즘이 입력 데이터(n)가 커질 때, 실행 시간이나 메모리 사용량이 얼마나 빨리 증가하는지를 수학적으로 간단하게 표현하는 방법입니다. 즉, "데이터가 많아질수록 내 코드가 얼마나 느려질까?"를 한눈에 보여주는 지표입니다.
상한선(최악의 경우): 빅오는 "최악의 경우 이 정도 시간/공간이 걸린다"는 상한선을 나타냅니다.
상수와 영향력 없는 항 무시: 데이터가 아주 많아지면, 작은 수치(상수)나 영향력이 적은 항은 무시합니다.
시간 복잡도와 공간 복잡도: 주로 시간(실행 속도)에 쓰지만, 메모리 사용량(공간 복잡도)에도 적용할 수 있습니다.
| 표기법 | 예시 코드/알고리즘 | 설명 및 일상 비유 |
|---|---|---|
| O(1) | 배열에서 인덱스로 값 찾기 | "냉장고에서 딱 정해진 위치에 있는 물건 꺼내기"처럼, 데이터 양과 상관없이 항상 같은 시간에 끝남 |
| O(log n) | 이진 탐색 | "책에서 원하는 페이지를 반씩 접으면서 찾기"처럼, 데이터가 두 배로 늘어도 몇 번만 더 찾으면 됨 |
| O(n) | for문 한 번 돌기 | "줄 선 사람 한 명씩 체크하기"처럼, 데이터가 두 배면 시간도 두 배 걸림 |
| O(n log n) | 퀵정렬, 병합정렬 | "여러 번 반씩 나누면서 전체를 훑어야 하는 상황" |
| O(n^2) | 이중 for문(버블정렬 등) | "모든 사람끼리 악수하기"처럼, 데이터가 두 배면 시간은 네 배 걸림 |
| O(2^n) | 피보나치 재귀, 모든 조합 탐색 | "모든 경우의 수를 다 해보기"처럼, 데이터가 조금만 늘어도 시간이 폭발적으로 증가 |

O(1) < O(log n) < O(n) < O(n log n) < O(n²) < O(2ⁿ) < O(n!) (오른쪽으로 갈수록 성능 나쁨)
리스트
* 특징
*주요 연산 및 시간 복잡도 (동적 배열 구현 기준)
인덱싱: my_list[i] → O(1) (매우 빠름). 🔵 연속된 메모리 구조 덕분에 특정 위치의 주소를 바로 계산하여 접근할 수 있습니다.
슬라이싱: my_list[i:j] → O(k) (k는 슬라이스 길이). 슬라이스 길이만큼의 새로운 리스트를 생성하고 요소를 복사합니다.
길이 확인: len(my_list) → O(1). 🔵 리스트 객체 내부에 저장된 길이 정보(ob_size)를 바로 반환합니다.
요소 포함 확인: item in my_list → O(n) (선형 탐색). 🔴 처음부터 끝까지 요소를 하나씩 비교하며 찾아야 합니다.
맨 뒤에 추가: my_list.append(item) → 평균 O(1), 최악 O(n). 🔵 대부분의 경우, 미리 할당된 메모리 공간(allocated)의 끝에 요소를 추가하고 길이(ob_size)만 늘리면 되므로 O(1)입니다. 하지만, 공간이 꽉 차면 더 큰 메모리 공간을 새로 할당하고 기존 요소들을 모두 복사해야 하므로 드물게 O(N) (최악) 시간이 걸립니다. (이러한 메모리 재할당(resize) 전략 덕분에 평균적으로 O(1) 성능을 보입니다.)
특정 위치에 삽입: my_list.insert(i, item) → O(n). 🔴 지정된 위치(i) 이후의 모든 요소들을 메모리상에서 한 칸씩 뒤로 밀어야 삽입할 공간을 확보할 수 있습니다.
맨 뒤 요소 삭제: my_list.pop() → O(1). 🔵 맨 뒤 요소를 제거하고 길이(ob_size)만 줄이면 됩니다. (메모리가 너무 많이 남으면 축소(resize)될 수 있음)
특정 위치 요소 삭제: my_list.pop(i), del my_list[i] → O(n). 🔴 지정된 위치(i)의 요소를 삭제하고, 그 뒤의 모든 요소들을 한 칸씩 앞으로 당겨 빈 공간을 메워야 합니다.
특정 값 삭제: my_list.remove(value) → O(n). 🔴 값을 찾기 위해 선형 탐색(O(N))을 하고, 찾은 후에는 해당 요소를 삭제하고 뒤 요소들을 이동(O(N))시켜야 합니다.
정렬: my_list.sort() → O(n log n). Timsort라는 효율적인 알고리즘을 사용합니다.
뒤집기: my_list.reverse() → O(n). 리스트의 절반만큼 요소 위치를 맞바꿉니다.
슬라이스 대입/삭제: my_list[i:j] = iterable, del my_list[i:j] → O(N+k) (N은 리스트 길이, k는 iterable/슬라이스 길이). 관련된 요소들을 이동시키거나 복사해야 할 수 있습니다.
튜플
* 특징
(소괄호)를 사용하여 생성 (소괄호 생략 가능).
한번 생성되면 요소의 추가, 삭제, 수정이 불가능.
리스트보다 약간 더 메모리 효율적이고 빠름 (고정 크기).
딕셔너리의 키(key) 로 사용될 수 있음 (리스트는 불가).
함수의 다중 반환값 처리 등에 유용.
리스트와 유사하게 인덱싱, 슬라이싱, len(), in, +, * 연산 가능.
변경 관련 연산 (append, insert, pop, remove, sort 등)은 사용 불가!
집합
* 특징:
* 주요 연산 및 시간 복잡도
딕셔너리
* 특징:
(중괄호) 안에 key: value 형태로 작성하여 생성.
키(Key):
a.고유(Unique) 해야 함. 중복된 키 사용 시 마지막 값으로 덮어써짐.
b.변경 불가능(Immutable) 하고 해시 가능(Hashable) 해야 함. (숫자, 문자열, 튜플 등 사용 가능. 리스트, 딕셔너리, 집합은 키로 사용 불가)
값(Value):
a.어떤 데이터 타입이든 가능 (숫자, 문자열, 리스트, 다른 딕셔너리 등).
b.중복 가능.
순서:
Python 3.6 이하: 순서 없음 (unordered). 출력 시 순서 보장 안 됨.
Python 3.7 이상: 입력된 순서가 유지됨 (ordered). (CPython 구현 기준)
* 주요 연산 및 시간 복잡도