면접 준비
파이썬 코딩테스트 유틸 총정리
각 유틸의 사용법과 예시를 빠짐없이 정리한 레퍼런스.
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)]