코딩테스트 자료구조 파이썬 완전 정복 – 빈출 10선과 실전 코드


코딩테스트 자료구조 파이썬 조합은 국내 대기업·스타트업 기술 면접의 공통 핵심 키워드입니다. 어떤 문제가 나와도 “이 문제는 어떤 자료구조로 풀어야 하는가?”를 1분 안에 판단하는 능력이 합격을 가릅니다. 이 글에서는 카카오·네이버·삼성·라인 등 주요 기업 코딩테스트에서 실제로 자주 등장하는 자료구조 10가지를 파이썬 코드, 시간복잡도, 실전 활용 패턴과 함께 완전히 정리합니다.


목차

  1. 코딩테스트와 자료구조 – 왜 이 10가지인가?
  2. 선형 자료구조 4선 – 리스트·스택·큐·덱
  3. 해시 기반 자료구조 2선 – 딕셔너리·집합
  4. 트리 기반 자료구조 2선 – 힙·트라이
  5. 그래프와 고급 자료구조 2선 – 그래프·유니온 파인드
  6. 자료구조 선택 전략과 실전 문제 패턴 매핑

1. 코딩테스트와 자료구조 – 왜 이 10가지인가?

코딩테스트에서 자료구조는 문제 풀이의 ‘도구 선택’ 단계에 해당합니다. 아무리 알고리즘 로직이 정확해도, 잘못된 자료구조를 선택하면 시간 초과(TLE)나 메모리 초과(MLE)로 탈락합니다.

빈출 자료구조가 결정되는 이유

코딩테스트 출제자들은 다음 기준으로 문제를 설계합니다.

  • 시간복잡도 함정: O(n²)으로 풀면 TLE가 나고, 올바른 자료구조로 O(n log n) 이하로 풀어야 통과
  • 공간·탐색 트레이드오프: 메모리를 더 쓰되 속도를 올리는 선택이 필요한 상황
  • 구현 난이도: 라이브러리로 빠르게 쓸 수 있는 자료구조 vs 직접 구현해야 하는 자료구조

파이썬은 collections, heapq, bisect 등 강력한 표준 라이브러리를 제공하기 때문에, 이를 얼마나 잘 활용하는지가 파이썬 코딩테스트의 실질적 변별력이 됩니다.

자료구조별 시간복잡도 한눈에 보기

자료구조삽입삭제탐색주요 용도
리스트(List)O(1)~O(n)O(n)O(n)순서 있는 데이터
스택(Stack)O(1)O(1)O(n)DFS, 괄호 검사
큐(Queue/deque)O(1)O(1)O(n)BFS, 순서 처리
덱(Deque)O(1)O(1)O(n)양방향 처리
딕셔너리(dict)O(1)O(1)O(1)빈도수, 매핑
집합(set)O(1)O(1)O(1)중복 제거, 교집합
힙(heapq)O(log n)O(log n)O(1)우선순위 큐
트라이(Trie)O(m)O(m)O(m)문자열 검색
그래프(Graph)O(1)O(1)O(V+E)경로 탐색
유니온 파인드O(α(n))O(α(n))연결 요소 판별

m = 문자열 길이, V = 정점 수, E = 간선 수, α = 역아커만 함수(사실상 O(1))


2. 선형 자료구조 4선 – 리스트·스택·큐·덱

선형 자료구조는 데이터가 일렬로 나열된 구조입니다. 코딩테스트에서 가장 기본이 되며, 파이썬에서는 모두 간결하게 구현할 수 있습니다.

① 리스트 (List)

파이썬 리스트는 동적 배열로, 인덱스 기반 접근이 O(1)입니다. 단, 중간 삽입·삭제는 O(n)임을 반드시 기억해야 합니다.

python

# 리스트 핵심 패턴 – 코딩테스트 실전
arr = [3, 1, 4, 1, 5, 9, 2, 6]

# 정렬 (Timsort, O(n log n))
arr.sort()                        # 오름차순 정렬 (in-place)
arr.sort(reverse=True)            # 내림차순 정렬
sorted_arr = sorted(arr, key=lambda x: -x)  # key 활용

# 슬라이싱 – O(k), k = 슬라이스 길이
sub = arr[2:5]

# 이진 탐색 – bisect 활용 (정렬된 배열에서 O(log n))
import bisect
pos = bisect.bisect_left(arr, 4)  # 4가 삽입될 위치 반환

# 2차원 배열 초기화 – 주의: [[0]*n]*m 은 얕은 복사!
matrix = [[0] * 4 for _ in range(3)]  # 3×4 행렬 안전 초기화

실전 주의점: arr.insert(0, x)는 O(n)입니다. 앞쪽 삽입이 잦다면 deque로 교체하세요.


② 스택 (Stack)

후입선출(LIFO) 구조. 파이썬 리스트의 append()·pop()이 모두 O(1)이므로 별도 라이브러리 없이 구현합니다.

python

# 스택 – 괄호 유효성 검사 (카카오 빈출 유형)
def is_valid(s: str) -> bool:
    stack = []
    mapping = {')': '(', '}': '{', ']': '['}

    for char in s:
        if char in mapping:                   # 닫는 괄호
            top = stack.pop() if stack else '#'
            if mapping[char] != top:
                return False
        else:                                 # 여는 괄호
            stack.append(char)

    return not stack                          # 스택이 비어있어야 유효

print(is_valid("({[]})"))   # True
print(is_valid("({[})"))    # False

빈출 패턴: 괄호 검사, 히스토그램 최대 직사각형, 함수 호출 스택 시뮬레이션, DFS 반복 구현.


③ 큐 (Queue)

선입선출(FIFO) 구조. 리스트의 pop(0)은 O(n)이므로 절대 사용 금지. 반드시 collections.deque를 사용합니다.

python

from collections import deque

# BFS – 최단 경로 탐색 (가장 빈출 큐 활용)
def bfs(graph: dict, start: int) -> list:
    visited = set([start])
    queue = deque([start])
    order = []

    while queue:
        node = queue.popleft()        # O(1) – 리스트 pop(0) 대신!
        order.append(node)

        for neighbor in graph[node]:
            if neighbor not in visited:
                visited.add(neighbor)
                queue.append(neighbor)

    return order

graph = {1: [2, 3], 2: [4], 3: [4, 5], 4: [], 5: []}
print(bfs(graph, 1))   # [1, 2, 3, 4, 5]

④ 덱 (Deque)

양쪽 끝에서 O(1) 삽입·삭제가 가능한 구조입니다. 슬라이딩 윈도우 문제의 핵심 도구입니다.

python

from collections import deque

# 슬라이딩 윈도우 최대값 – 단조 덱(Monotonic Deque) 패턴
def sliding_window_max(nums: list, k: int) -> list:
    dq = deque()   # 인덱스 저장, 덱 내 값은 항상 단조 감소
    result = []

    for i, num in enumerate(nums):
        # 윈도우 범위를 벗어난 인덱스 제거
        while dq and dq[0] < i - k + 1:
            dq.popleft()

        # 현재 값보다 작은 이전 값 제거 (단조 감소 유지)
        while dq and nums[dq[-1]] < num:
            dq.pop()

        dq.append(i)

        if i >= k - 1:
            result.append(nums[dq[0]])  # 덱 앞이 항상 최대값

    return result

print(sliding_window_max([1,3,-1,-3,5,3,6,7], 3))
# [3, 3, 5, 5, 6, 7]

3. 해시 기반 자료구조 2선 – 딕셔너리·집합

해시 기반 자료구조는 평균 O(1) 탐색이라는 압도적 장점으로, 빈도수 계산·중복 처리·빠른 조회 문제에 핵심 도구가 됩니다.

⑤ 딕셔너리 (Dictionary / HashMap)

파이썬 dict는 해시테이블 기반으로, 삽입·삭제·탐색 모두 평균 O(1)입니다.

python

from collections import defaultdict, Counter

# --- 패턴 1: defaultdict – KeyError 없이 안전하게 ---
graph = defaultdict(list)     # 그래프 인접 리스트
graph[1].append(2)            # 키 없어도 자동 초기화

freq = defaultdict(int)       # 빈도수 카운터
for char in "abracadabra":
    freq[char] += 1

# --- 패턴 2: Counter – 빈도수 특화 ---
counter = Counter("abracadabra")
print(counter.most_common(2)) # [('a', 5), ('b', 2)]

# 두 Counter 연산
c1 = Counter("aab")
c2 = Counter("abb")
print(c1 + c2)   # Counter({'a': 3, 'b': 3})
print(c1 & c2)   # Counter({'a': 1, 'b': 1}) – 교집합(최솟값)

# --- 패턴 3: 애너그램 판별 ---
def is_anagram(s: str, t: str) -> bool:
    return Counter(s) == Counter(t)

print(is_anagram("anagram", "nagaram"))  # True

⑥ 집합 (Set)

중복을 허용하지 않는 해시 기반 구조. 합집합·교집합·차집합 연산이 내장되어 있어 집합 연산 문제에 강력합니다.

python

# 집합 핵심 연산
a = {1, 2, 3, 4, 5}
b = {3, 4, 5, 6, 7}

print(a | b)   # 합집합: {1, 2, 3, 4, 5, 6, 7}
print(a & b)   # 교집합: {3, 4, 5}
print(a - b)   # 차집합: {1, 2}
print(a ^ b)   # 대칭차: {1, 2, 6, 7}

# 방문 체크 – BFS/DFS에서 set이 list보다 O(1) vs O(n)
visited = set()
visited.add(node)
if node not in visited:   # O(1) 탐색
    pass

# 중복 제거 후 정렬
nums = [3, 1, 4, 1, 5, 9, 2, 6, 5]
unique_sorted = sorted(set(nums))  # [1, 2, 3, 4, 5, 6, 9]

실전 주의점: set은 순서를 보장하지 않습니다. 삽입 순서가 필요하면 Python 3.7+ dict를 활용하세요.


4. 트리 기반 자료구조 2선 – 힙·트라이

트리 기반 자료구조는 정렬된 순서 유지 또는 구조적 탐색이 필요한 문제에서 핵심 역할을 합니다.

⑦ 힙 (Heap / 우선순위 큐)

파이썬 heapq최소 힙(Min-Heap) 만 기본 제공합니다. 최대 힙이 필요하면 값에 -1을 곱해 넣습니다.

python

import heapq

# 최소 힙 기본
heap = []
heapq.heappush(heap, 3)
heapq.heappush(heap, 1)
heapq.heappush(heap, 5)
print(heapq.heappop(heap))   # 1 (최솟값 반환)

# 최대 힙 – 음수 변환 트릭
max_heap = []
for num in [3, 1, 5, 2]:
    heapq.heappush(max_heap, -num)
print(-heapq.heappop(max_heap))  # 5 (최댓값 반환)

# K번째 최솟값 – O(n log k) 패턴
def kth_smallest(nums: list, k: int) -> int:
    heapq.heapify(nums)          # 리스트를 힙으로 O(n)
    for _ in range(k - 1):
        heapq.heappop(nums)
    return heapq.heappop(nums)

# 다익스트라 핵심 패턴 – (거리, 노드) 튜플로 관리
def dijkstra(graph: dict, start: int) -> dict:
    dist = defaultdict(lambda: float('inf'))
    dist[start] = 0
    pq = [(0, start)]           # (거리, 노드)

    while pq:
        d, node = heapq.heappop(pq)
        if d > dist[node]:
            continue            # 이미 더 짧은 경로 존재
        for neighbor, weight in graph[node]:
            new_dist = d + weight
            if new_dist < dist[neighbor]:
                dist[neighbor] = new_dist
                heapq.heappush(pq, (new_dist, neighbor))
    return dist

⑧ 트라이 (Trie / 접두사 트리)

문자열 검색·자동완성·공통 접두사 문제에 특화된 트리입니다. 파이썬에서는 dict 중첩으로 간결하게 구현합니다.

python

# 트라이 – 딕셔너리 기반 구현
class Trie:
    def __init__(self):
        self.root = {}

    def insert(self, word: str) -> None:
        node = self.root
        for char in word:
            # 해당 문자 키가 없으면 새 노드 생성
            node = node.setdefault(char, {})
        node['#'] = True          # 단어 종료 마커

    def search(self, word: str) -> bool:
        node = self.root
        for char in word:
            if char not in node:
                return False
            node = node[char]
        return '#' in node        # 종료 마커 확인

    def starts_with(self, prefix: str) -> bool:
        node = self.root
        for char in prefix:
            if char not in node:
                return False
            node = node[char]
        return True               # 접두사만 존재해도 True

# 사용 예시
trie = Trie()
for word in ["apple", "app", "application"]:
    trie.insert(word)

print(trie.search("app"))          # True
print(trie.search("ap"))           # False
print(trie.starts_with("appl"))    # True

빈출 패턴: 전화번호 접두사 판별(프로그래머스), 자동완성 구현, 단어 검색 II(LeetCode).


5. 그래프와 고급 자료구조 2선 – 그래프·유니온 파인드

그래프는 현실 문제를 모델링하는 가장 강력한 자료구조입니다. 지도, 소셜 네트워크, 의존성 관계 모두 그래프로 표현됩니다.

⑨ 그래프 (Graph) – BFS / DFS

python

from collections import deque

# 인접 리스트 표현 – 공간 효율적
graph = defaultdict(list)
edges = [(1,2),(1,3),(2,4),(3,4),(3,5)]
for u, v in edges:
    graph[u].append(v)
    graph[v].append(u)   # 무방향 그래프

# DFS – 재귀 구현
def dfs(graph, node, visited=None):
    if visited is None:
        visited = set()
    visited.add(node)
    print(node, end=' ')
    for neighbor in graph[node]:
        if neighbor not in visited:
            dfs(graph, neighbor, visited)
    return visited

# DFS – 스택 반복 구현 (재귀 깊이 제한 우회)
def dfs_iterative(graph, start):
    visited = set()
    stack = [start]
    result = []
    while stack:
        node = stack.pop()
        if node not in visited:
            visited.add(node)
            result.append(node)
            # 방문 순서 유지를 위해 역순 삽입
            for neighbor in reversed(graph[node]):
                if neighbor not in visited:
                    stack.append(neighbor)
    return result

# BFS – 최단 거리 계산
def bfs_distance(graph, start):
    dist = {start: 0}
    queue = deque([start])
    while queue:
        node = queue.popleft()
        for neighbor in graph[node]:
            if neighbor not in dist:
                dist[neighbor] = dist[node] + 1
                queue.append(neighbor)
    return dist

빈출 패턴: 섬의 개수(연결 요소), 최단 경로, 위상 정렬(indegree), 사이클 탐지.


⑩ 유니온 파인드 (Union-Find / Disjoint Set)

두 노드가 같은 집합(연결 요소)에 속하는지를 거의 O(1) 에 판별합니다. 경로 압축(Path Compression)과 랭크 기반 합치기(Union by Rank)를 반드시 함께 구현해야 합니다.

python

class UnionFind:
    def __init__(self, n: int):
        self.parent = list(range(n))  # 각 노드가 자기 자신의 부모
        self.rank = [0] * n           # 트리 높이 관리

    def find(self, x: int) -> int:
        # 경로 압축 – find 호출 시 부모를 루트로 직접 연결
        if self.parent[x] != x:
            self.parent[x] = self.find(self.parent[x])
        return self.parent[x]

    def union(self, x: int, y: int) -> bool:
        rx, ry = self.find(x), self.find(y)
        if rx == ry:
            return False             # 이미 같은 집합 (사이클 감지)
        # 랭크 기반 합치기 – 높이가 낮은 트리를 높은 쪽에 붙임
        if self.rank[rx] < self.rank[ry]:
            rx, ry = ry, rx
        self.parent[ry] = rx
        if self.rank[rx] == self.rank[ry]:
            self.rank[rx] += 1
        return True

    def connected(self, x: int, y: int) -> bool:
        return self.find(x) == self.find(y)

# 크루스칼 알고리즘 – MST (최소 신장 트리) 핵심 패턴
def kruskal(n: int, edges: list) -> int:
    uf = UnionFind(n)
    edges.sort(key=lambda x: x[2])  # 가중치 기준 정렬
    total = 0
    for u, v, weight in edges:
        if uf.union(u, v):           # 사이클 없을 때만 추가
            total += weight
    return total

빈출 패턴: 네트워크 연결 요소 수, 사이클 감지, 크루스칼 MST, 친구 관계 판별(프로그래머스).


6. 자료구조 선택 전략과 실전 문제 패턴 매핑

자료구조를 외우는 것만큼 중요한 것은 문제를 읽는 순간 올바른 자료구조를 선택하는 직관입니다.

문제 유형별 자료구조 선택 지도

문제에서 이런 키워드가 보이면선택할 자료구조대표 알고리즘
최단 경로, 레벨 탐색큐(deque)BFS
모든 경로, 백트래킹스택 / 재귀DFS
K번째 최소/최대, 실시간 정렬힙(heapq)우선순위 큐
빈도수, 개수 세기딕셔너리(Counter)해시맵
중복 제거, 포함 여부집합(set)해시셋
접두사, 자동완성트라이(Trie)문자열 탐색
연결 여부, 사이클 감지유니온 파인드크루스칼
양방향 삽입/삭제덱(deque)슬라이딩 윈도우
구간 최솟값/최댓값세그먼트 트리구간 쿼리
이전 상태 복원, LIFO스택(list)히스토리 관리

자료구조 학습 순서 로드맵

[입문]
리스트 → 딕셔너리 → 집합
    ↓
[중급]
스택 → 큐(deque) → 힙(heapq)
    ↓
[고급]
그래프(BFS/DFS) → 유니온 파인드 → 트라이
    ↓
[심화]
세그먼트 트리 → 펜윅 트리 → 스패닝 트리

파이썬 코딩테스트 실전 팁 5가지

① sys.setrecursionlimit 설정 DFS 재귀 구현 시 파이썬 기본 재귀 한도(1,000)를 반드시 늘려야 합니다.

python

import sys
sys.setrecursionlimit(10**6)

② 입력 속도 개선 대용량 입력에서 input() 대신 sys.stdin.readline()을 사용하면 10배 이상 빠릅니다.

python

import sys
input = sys.stdin.readline

③ 리스트 컴프리헨션 우선 반복문보다 리스트 컴프리헨션이 C로 구현되어 실질적으로 빠릅니다.

python

# 느림
result = []
for i in range(n):
    result.append(i * 2)

# 빠름
result = [i * 2 for i in range(n)]

④ enumerate와 zip 활용 인덱스와 값을 동시에 다룰 때 enumerate, 두 리스트를 병렬 처리할 때 zip을 사용하면 코드가 간결하고 오류가 줄어듭니다.

⑤ 2차원 배열 회전·전치 패턴 암기 행렬 회전 문제는 코딩테스트 단골입니다.

python

matrix = [[1,2,3],[4,5,6],[7,8,9]]

# 전치(Transpose)
transposed = list(map(list, zip(*matrix)))

# 90도 시계 방향 회전
rotated = list(map(list, zip(*matrix[::-1])))

결론

코딩테스트 자료구조 파이썬 학습의 핵심은 ‘무엇을 아는가’가 아니라 ‘언제 무엇을 쓰는가’를 체화하는 것입니다. 리스트·스택·큐·덱·딕셔너리·집합·힙·트라이·그래프·유니온 파인드, 이 10가지를 코드로 직접 짜고, 각각의 시간복잡도를 외우고, 문제 유형과 매핑하는 연습을 반복하면 어떤 테스트도 당황하지 않는 근육이 만들어집니다. 오늘 이 글의 코드를 한 줄씩 직접 실행하며 익혀보세요.


답글 남기기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다