← Blog
면접 준비

파이썬 코딩테스트 유틸 총정리

각 유틸의 사용법과 예시를 빠짐없이 정리한 레퍼런스.


1. 내장 함수 (Built-in)

zip

여러 iterable을 병렬로 묶음. 가장 짧은 것 기준으로 잘림.

a = [1, 2, 3]
b = ['x', 'y', 'z']
list(zip(a, b))          # [(1, 'x'), (2, 'y'), (3, 'z')]

# 언패킹 (unzip)
pairs = [(1, 'x'), (2, 'y')]
nums, chars = zip(*pairs)   # (1, 2), ('x', 'y')

# 행렬 전치
matrix = [[1, 2, 3], [4, 5, 6]]
list(zip(*matrix))       # [(1, 4), (2, 5), (3, 6)]

# 동시 순회
for x, y in zip(a, b):
    print(x, y)

enumerate

인덱스와 값을 동시에.

for i, v in enumerate(['a', 'b', 'c']):
    print(i, v)          # 0 a / 1 b / 2 c

for i, v in enumerate(['a', 'b'], start=1):
    print(i, v)          # 1 a / 2 b

map

각 원소에 함수 적용.

list(map(int, ['1', '2', '3']))       # [1, 2, 3]
list(map(str, [1, 2, 3]))             # ['1', '2', '3']
n, m = map(int, input().split())      # 입력 여러 개 받기
list(map(lambda x: x**2, [1, 2, 3]))  # [1, 4, 9]
list(map(lambda x, y: x+y, [1,2], [3,4]))  # [4, 6]

filter

조건 만족 원소만 남김.

list(filter(lambda x: x % 2 == 0, range(10)))   # [0, 2, 4, 6, 8]
list(filter(None, [0, 1, '', 'a', None]))       # [1, 'a'] (falsy 제거)

sorted

정렬된 새 리스트 반환. (리스트의 .sort()는 in-place)

sorted([3, 1, 2])                     # [1, 2, 3]
sorted([3, 1, 2], reverse=True)       # [3, 2, 1]
sorted(['bb', 'a', 'ccc'], key=len)   # ['a', 'bb', 'ccc']

# 다중 기준: 첫째 오름차순, 둘째 내림차순
pts = [(1, 2), (1, 5), (2, 1)]
sorted(pts, key=lambda p: (p[0], -p[1]))   # [(1, 5), (1, 2), (2, 1)]

# 딕셔너리 값 기준
d = {'a': 3, 'b': 1}
sorted(d.items(), key=lambda x: x[1])      # [('b', 1), ('a', 3)]

reversed

역순 이터레이터.

list(reversed([1, 2, 3]))    # [3, 2, 1]
list(reversed(range(5)))     # [4, 3, 2, 1, 0]

sum / min / max

sum([1, 2, 3])               # 6
sum([1, 2, 3], 10)           # 16 (시작값)
min([3, 1, 2])               # 1
max([3, 1, 2])               # 3

# key 인자
pts = [(1, 5), (3, 2)]
max(pts, key=lambda p: p[1]) # (1, 5)
min(pts, key=lambda p: p[0]) # (1, 5)

# 여러 인자
max(3, 7, 1)                 # 7

any / all

any([False, True, False])    # True (하나라도 참)
all([True, True, False])     # False (모두 참이어야)
all(x > 0 for x in [1, 2, 3])   # True
any(x > 5 for x in [1, 2, 3])   # False

divmod

몫과 나머지 동시.

divmod(17, 5)                # (3, 2)
q, r = divmod(17, 5)

pow

pow(2, 10)                   # 1024
pow(2, 10, 1000)             # 24  (2^10 % 1000, 모듈러 거듭제곱 - 매우 빠름)

진법 / 문자 변환

ord('A')                     # 65
chr(65)                      # 'A'
ord('a') - ord('A')          # 32

bin(10)                      # '0b1010'
oct(10)                      # '0o12'
hex(255)                     # '0xff'
int('1010', 2)               # 10  (2진 -> 10진)
int('ff', 16)                # 255
format(10, 'b')              # '1010' (0b 없이)

eval

문자열 수식 계산 (신뢰된 입력만).

eval('3 + 4 * 2')            # 11
eval('[1, 2, 3]')            # [1, 2, 3]

2. collections

from collections import deque, Counter, defaultdict, OrderedDict

deque

양쪽 끝 O(1). BFS 필수.

dq = deque([1, 2, 3])
dq.append(4)          # 오른쪽 추가   [1,2,3,4]
dq.appendleft(0)      # 왼쪽 추가      [0,1,2,3,4]
dq.pop()              # 오른쪽 제거 -> 4
dq.popleft()          # 왼쪽 제거   -> 0
dq.extend([5, 6])     # 오른쪽 여러 개
dq.extendleft([1, 0]) # 왼쪽 (역순으로 들어감)
dq.rotate(1)          # 오른쪽으로 회전
dq.rotate(-1)         # 왼쪽으로 회전

# 슬라이딩 윈도우 (최대 길이 고정)
window = deque(maxlen=3)
for x in [1, 2, 3, 4, 5]:
    window.append(x)  # 초과 시 반대쪽 자동 제거
# window -> deque([3, 4, 5], maxlen=3)

# BFS 예시
from collections import deque
def bfs(graph, start):
    visited = {start}
    q = deque([start])
    while q:
        node = q.popleft()
        for nxt in graph[node]:
            if nxt not in visited:
                visited.add(nxt)
                q.append(nxt)

Counter

개수 세기.

c = Counter('aabbbc')          # Counter({'b': 3, 'a': 2, 'c': 1})
c = Counter([1, 1, 2, 3, 3, 3])
c[3]                           # 3
c[99]                          # 0 (없는 키는 0, 에러 안 남)
c.most_common(2)               # [(3, 3), (1, 2)] 상위 2개
c.most_common()                # 전체 (많은 순)
list(c.elements())             # 원소 펼치기 [1,1,2,3,3,3]

# Counter 연산
a = Counter('aab')
b = Counter('abc')
a + b                          # 더하기 Counter({'a':3,'b':2,'c':1})
a - b                          # 빼기 (음수 제거)
a & b                          # 교집합 (min)
a | b                          # 합집합 (max)

c.update('aaa')                # 개수 추가

defaultdict

없는 키 접근 시 기본값 자동 생성.

d = defaultdict(int)           # 기본값 0
d['x'] += 1                    # KeyError 없이 동작

d = defaultdict(list)          # 기본값 []
d['a'].append(1)               # {'a': [1]}

d = defaultdict(set)           # 기본값 set()
d['a'].add(1)

# 그래프 인접리스트
graph = defaultdict(list)
for u, v in edges:
    graph[u].append(v)
    graph[v].append(u)

# 2중 defaultdict
grid = defaultdict(lambda: defaultdict(int))
grid[1][2] += 1

OrderedDict

삽입 순서 유지 (Python 3.7+ 일반 dict도 순서 유지되지만 특수 메서드 필요 시).

od = OrderedDict()
od['a'] = 1
od.move_to_end('a')            # 맨 뒤로
od.move_to_end('a', last=False)  # 맨 앞으로
od.popitem(last=False)         # 맨 앞 제거 (FIFO)
# LRU 캐시 구현 시 유용

3. heapq (우선순위 큐)

기본 min-heap. 리스트를 힙으로 사용.

import heapq

h = [3, 1, 4, 1, 5]
heapq.heapify(h)               # 리스트를 힙으로 (in-place, O(n))
heapq.heappush(h, 2)           # 추가
heapq.heappop(h)               # 최솟값 꺼내기 -> 1
h[0]                           # 최솟값 확인 (제거 X)

heapq.heappushpop(h, 6)        # push 후 pop (효율적)
heapq.heapreplace(h, 6)        # pop 후 push

# 상위/하위 k개
heapq.nlargest(3, [1, 5, 2, 8, 3])    # [8, 5, 3]
heapq.nsmallest(3, [1, 5, 2, 8, 3])   # [1, 2, 3]
heapq.nlargest(2, data, key=lambda x: x[1])

# max-heap: 부호 반전
h = []
heapq.heappush(h, -5)
heapq.heappush(h, -1)
-heapq.heappop(h)              # 5 (최댓값)

# 튜플: 첫 원소 기준 (다익스트라)
import heapq
def dijkstra(graph, start, n):
    dist = [float('inf')] * n
    dist[start] = 0
    pq = [(0, start)]          # (거리, 노드)
    while pq:
        d, u = heapq.heappop(pq)
        if d > dist[u]:
            continue
        for v, w in graph[u]:
            if dist[u] + w < dist[v]:
                dist[v] = dist[u] + w
                heapq.heappush(pq, (dist[v], v))
    return dist

4. itertools

from itertools import (permutations, combinations, product,
                       combinations_with_replacement, accumulate,
                       groupby, chain, count, cycle, repeat, compress,
                       starmap, takewhile, dropwhile, islice)

permutations / combinations

list(permutations([1, 2, 3], 2))
# [(1,2),(1,3),(2,1),(2,3),(3,1),(3,2)]  순서 O

list(combinations([1, 2, 3], 2))
# [(1,2),(1,3),(2,3)]  순서 X

list(combinations_with_replacement([1, 2, 3], 2))
# [(1,1),(1,2),(1,3),(2,2),(2,3),(3,3)]  중복 허용

product

데카르트 곱 (중첩 for 대체).

list(product([1, 2], ['a', 'b']))
# [(1,'a'),(1,'b'),(2,'a'),(2,'b')]

list(product([0, 1], repeat=3))
# [(0,0,0),(0,0,1),...,(1,1,1)]  8개 (비트마스크 조합)

# 중첩 반복 대체
for i, j in product(range(3), range(3)):
    ...

accumulate

누적 계산.

list(accumulate([1, 2, 3, 4]))          # [1, 3, 6, 10] 누적합
list(accumulate([1, 2, 3, 4], initial=0))  # [0, 1, 3, 6, 10]

import operator
list(accumulate([1, 2, 3, 4], operator.mul))  # [1, 2, 6, 24] 누적곱
list(accumulate([3, 1, 4, 1], max))     # [3, 3, 4, 4] 누적 최댓값

# prefix sum으로 구간합 O(1)
arr = [1, 2, 3, 4, 5]
prefix = [0] + list(accumulate(arr))    # [0,1,3,6,10,15]
# 구간 [l, r) 합 = prefix[r] - prefix[l]

chain

여러 iterable 이어붙이기.

list(chain([1, 2], [3, 4], [5]))        # [1, 2, 3, 4, 5]
list(chain.from_iterable([[1,2],[3,4]])) # [1, 2, 3, 4] (2D 평탄화)

groupby

연속된 같은 값 그룹핑 (정렬 먼저 해야 함).

data = [1, 1, 2, 3, 3, 3]
for key, grp in groupby(data):
    print(key, list(grp))
# 1 [1, 1] / 2 [2] / 3 [3, 3, 3]

# 문자열 압축
s = "aaabbc"
result = [(k, len(list(g))) for k, g in groupby(s)]
# [('a', 3), ('b', 2), ('c', 1)]

# key 함수
words = ['apple', 'ant', 'bee', 'bird']
words.sort(key=lambda x: x[0])
for k, g in groupby(words, key=lambda x: x[0]):
    print(k, list(g))

기타

list(islice(count(10), 5))       # [10,11,12,13,14] 무한 count 자르기
list(islice(cycle([1,2]), 5))    # [1,2,1,2,1]
list(repeat(3, 4))               # [3,3,3,3]
list(compress('abcd', [1,0,1,0]))    # ['a', 'c'] 마스크 선택
list(takewhile(lambda x: x<3, [1,2,3,1]))  # [1, 2]
list(dropwhile(lambda x: x<3, [1,2,3,1]))  # [3, 1]
list(starmap(pow, [(2,3),(2,4)]))    # [8, 16]

5. functools

from functools import lru_cache, cache, reduce, cmp_to_key, partial

lru_cache / cache

메모이제이션 자동. DP/재귀 필수.

@lru_cache(maxsize=None)         # 캐시 크기 무제한
def fib(n):
    if n < 2:
        return n
    return fib(n-1) + fib(n-2)

@cache                           # Python 3.9+, lru_cache(maxsize=None)와 동일
def dp(i, j):
    ...

# 주의: 인자는 hashable이어야 함 (list 불가, tuple 사용)
fib.cache_clear()                # 캐시 초기화

reduce

누적 연산으로 하나의 값 도출.

reduce(lambda a, b: a+b, [1, 2, 3, 4])       # 10
reduce(lambda a, b: a*b, [1, 2, 3, 4])       # 24
reduce(lambda a, b: a*b, [1, 2, 3, 4], 10)   # 240 (초기값)

import math
reduce(math.gcd, [12, 18, 24])               # 6 (여러 수 gcd)

cmp_to_key

커스텀 비교 함수를 key로. (a가 앞: 음수, 뒤: 양수)

def cmp(a, b):
    # 이어붙여 큰 수 만들기 (예: "3","30" -> "330")
    if a + b > b + a:
        return -1
    return 1

nums = ['3', '30', '34', '5', '9']
sorted(nums, key=cmp_to_key(cmp))    # ['9','5','34','3','30']

partial

인자 일부 고정.

from functools import partial
int2 = partial(int, base=2)
int2('1010')                     # 10

6. bisect (이진 탐색)

정렬된 배열 대상.

import bisect

arr = [1, 3, 3, 5, 7]
bisect.bisect_left(arr, 3)       # 1 (3이 들어갈 가장 왼쪽)
bisect.bisect_right(arr, 3)      # 3 (3이 들어갈 가장 오른쪽)
bisect.bisect(arr, 3)            # 3 (bisect_right와 동일)

# 값의 개수
bisect.bisect_right(arr, 3) - bisect.bisect_left(arr, 3)   # 2

# 정렬 유지하며 삽입 (O(n) - 삽입 자체는 느림)
bisect.insort(arr, 4)            # [1,3,3,4,5,7]
bisect.insort_left(arr, 4)

# x 이상인 첫 원소 인덱스
i = bisect.bisect_left(arr, x)

# LIS (최장 증가 부분수열) O(n log n)
def lis(nums):
    tails = []
    for x in nums:
        i = bisect.bisect_left(tails, x)
        if i == len(tails):
            tails.append(x)
        else:
            tails[i] = x
    return len(tails)

# 좌표 압축, key 지원 (Python 3.10+)
bisect.bisect_left(data, x, key=lambda d: d[0])

7. math

import math

math.gcd(12, 18)                 # 6
math.gcd(12, 18, 24)             # 6 (3.9+ 여러 인자)
math.lcm(4, 6)                   # 12 (3.9+)
math.factorial(5)                # 120
math.comb(5, 2)                  # 10 (nCr)
math.perm(5, 2)                  # 20 (nPr)

math.ceil(3.2)                   # 4
math.floor(3.8)                  # 3
math.trunc(-3.8)                 # -3 (0방향 버림)
math.sqrt(16)                    # 4.0
math.isqrt(17)                   # 4 (정수 제곱근, 오차 없음)
math.pow(2, 3)                   # 8.0 (float)

math.log(8, 2)                   # 3.0
math.log2(8)                     # 3.0
math.log10(100)                  # 2.0

math.inf                         # 무한대 (초기값)
-math.inf
math.isinf(x)
math.isnan(x)

math.dist([0,0], [3,4])          # 5.0 (유클리드 거리)
math.hypot(3, 4)                 # 5.0

math.pi, math.e

8. 문자열 (str)

s = "Hello World"

# 분리 / 결합
s.split()                        # ['Hello', 'World'] (공백 기준)
"a,b,c".split(',')               # ['a', 'b', 'c']
"a  b".split()                   # ['a', 'b'] (연속 공백 처리)
"a b c".split(' ', 1)            # ['a', 'b c'] (횟수 제한)
"line1\nline2".splitlines()      # ['line1', 'line2']
",".join(['a', 'b', 'c'])        # 'a,b,c'
"".join(['a', 'b'])              # 'ab'

# 검사
"123".isdigit()                  # True
"abc".isalpha()                  # True
"abc123".isalnum()               # True
"   ".isspace()                  # True
"Hello".islower()                # False
s.startswith("He")               # True
s.endswith("ld")                 # True

# 변환
s.lower() / s.upper()
s.swapcase()                     # 대소문자 반전
s.capitalize()                   # 첫 글자만 대문자
s.title()                        # 각 단어 첫 글자 대문자
"  hi  ".strip()                 # 'hi'
"xxhixx".strip('x')              # 'hi'
"  hi".lstrip() / "hi  ".rstrip()

# 찾기 / 바꾸기
s.find("o")                      # 4 (없으면 -1)
s.rfind("o")                     # 7 (뒤에서)
s.index("o")                     # 4 (없으면 에러)
s.count("l")                     # 3
s.replace("l", "L")              # 'HeLLo WorLd'
s.replace("l", "L", 1)           # 첫 1개만

# 정렬/채우기
"5".zfill(3)                     # '005'
"hi".ljust(5, '*')               # 'hi***'
"hi".rjust(5, '*')               # '***hi'
"hi".center(6, '-')              # '--hi--'

# 슬라이싱
s[::-1]                          # 뒤집기
s[1:4]                           # 'ell'
s[::2]                           # 'HloWrd'

9. set / frozenset

s = {1, 2, 3}
s = set([1, 2, 2, 3])            # {1, 2, 3} 중복 제거

s.add(4)
s.remove(4)                      # 없으면 에러
s.discard(4)                     # 없어도 에러 X
s.pop()                          # 임의 원소 제거

a = {1, 2, 3}
b = {2, 3, 4}
a & b                            # {2, 3} 교집합 (a.intersection(b))
a | b                            # {1,2,3,4} 합집합 (a.union(b))
a - b                            # {1} 차집합 (a.difference(b))
a ^ b                            # {1, 4} 대칭차 (symmetric_difference)

a <= b                           # 부분집합 여부 (issubset)
a >= b                           # 상위집합 여부 (issuperset)
a.isdisjoint(b)                  # 교집합 없으면 True

# frozenset: 불변, hashable (dict 키/set 원소로 사용)
fs = frozenset([1, 2, 3])
seen = {frozenset([1, 2]), frozenset([3, 4])}

10. 자주 쓰는 팁 / 패턴

입출력 속도

import sys
input = sys.stdin.readline       # 빠른 입력 (개행 포함 주의, strip 필요할 때)
n = int(input())
arr = list(map(int, input().split()))

# 여러 줄 한 번에
data = sys.stdin.read().split()

# 빠른 출력
print('\n'.join(map(str, results)))
sys.stdout.write(...)

재귀 깊이

import sys
sys.setrecursionlimit(10**6)     # DFS 깊을 때 (기본 1000)

2D 배열 초기화

n, m = 3, 4
grid = [[0] * m for _ in range(n)]   # 올바름
# grid = [[0]*m]*n  <- 잘못됨! 모든 행이 같은 리스트 참조

# 방문 배열
visited = [[False] * m for _ in range(n)]

방향 벡터 (상하좌우)

dx = [-1, 1, 0, 0]
dy = [0, 0, -1, 1]
for d in range(4):
    nx, ny = x + dx[d], y + dy[d]
    if 0 <= nx < n and 0 <= ny < m:
        ...
# 8방향
dirs = [(-1,-1),(-1,0),(-1,1),(0,-1),(0,1),(1,-1),(1,0),(1,1)]

리스트 언패킹

a, *b, c = [1, 2, 3, 4, 5]       # a=1, b=[2,3,4], c=5
first, *rest = [1, 2, 3]         # first=1, rest=[2,3]

컴프리헨션

[x**2 for x in range(5)]                 # [0,1,4,9,16]
[x for x in range(10) if x % 2 == 0]     # [0,2,4,6,8]
{x: x**2 for x in range(3)}              # {0:0, 1:1, 2:2}
{x % 3 for x in range(10)}               # set
[[0]*3 for _ in range(3)]                # 2D
[y for row in matrix for y in row]       # 평탄화

무한대 초기값

INF = float('inf')
INF = math.inf
min_val = float('inf')
max_val = float('-inf')

삼항 / 기타

x = a if cond else b
result = "짝" if n % 2 == 0 else "홀"

# 조건부 카운트
count = sum(1 for x in arr if x > 0)

# dict get 기본값
d.get('key', 0)
d.setdefault('key', []).append(1)

# 최댓값 인덱스
arr.index(max(arr))

유니온 파인드 (Union-Find) 템플릿

parent = list(range(n))
def find(x):
    while parent[x] != x:
        parent[x] = parent[parent[x]]   # 경로 압축
        x = parent[x]
    return x
def union(a, b):
    ra, rb = find(a), find(b)
    if ra != rb:
        parent[ra] = rb

순열/조합 직접 없이 비트마스크

for mask in range(1 << n):        # 모든 부분집합
    subset = [arr[i] for i in range(n) if mask & (1 << i)]