목차16개 항목
출처와 참고
반례 찾기와 파이썬 표준 라이브러리 팁 일부는 팔만코딩경(42Seoul 커뮤니티 위키)의 「[Python&C] 맞왜틀 이제 그만, 반례 생성기!」, 「코딩테스트를 위한 python 문법」을 바탕으로 보강했다. 자바스크립트 실전 팁은 「Frontend_TIL: 알고리즘」의 문제 풀이에서 반복되는 패턴만 뽑아 보강했다. 「막혔을 때 바꿔 보는 관점」은 jbee의 「Interview Question for Beginner」 알고리즘 문서의 코딩 테스트 팁과 전략적 접근 절을, 「풀이 최적화」와 자료구조 신호는 Tech Interview Handbook의 「Coding Interview Techniques」를 바탕으로 보강했다. 문제 풀이 순서의 조건 확정, 「자주 하는 선택」 일부와 「놓치기 쉬운 경계 사례」는 Tech Interview Handbook의 「Algorithms study cheatsheets」와 주제별 치트시트(배열·행렬·연결 리스트·트리·그래프·구간·수학 등)의 코너 케이스 절을 바탕으로 보강했다. 「자기기만 피하기」는 Codeforces 블로그의 「Self-deception: maybe why you're still grey after practicing every day」(2022.01)와 글쓴이가 단 보충 댓글을 바탕으로 보강했다.
코딩 테스트에서 시간이 부족해지는 이유는 대개 설계 없이 코드부터 쓰다가 중간에 방향을 잃기 때문이다. 아래는 문제를 풀 때 따르는 순서와 자주 쓰는 실전 팁이다.
문제 풀이 순서
- 비어 있는 조건을 확정한다. 문제는 일부러 덜 정해져 있는 경우가 많다. 중복 값이 있는가, 입력이 늘 유효한가, 문자 집합과 대소문자 구분은 어떤가, 끝점이 맞닿은 구간은 겹친 것인가, 시간·공간 제약이 있는가를 묻거나 가정으로 적어 둔다.
- 주석으로 먼저 설계한다. 풀이 단계를 한국어 주석으로 적고, 그 아래에 코드를 채운다.
- 떠오르는 테스트 케이스(TC)를 적어 둔다. 특히 경계 조건(최솟값, 최댓값, 빈 입력, 정답이 없는 경우)을 미리 적는다. 입력 유형별 목록은 아래 「놓치기 쉬운 경계 사례」에 있다.
- 주석을 읽으며 코딩한다. 설계와 구현을 분리하면 실수가 줄고 막혔을 때 어디서 막혔는지 바로 보인다.
- 감정도 적는다. "여기 헷갈림", "시간 초과 걱정" 같은 메모가 나중에 복습할 때 약점을 알려 준다.
- 다 풀면 정리한다. 불필요한 디버깅 코드는 지우고 TC와 핵심 아이디어만 남긴다. 면접처럼 시간이 정해진 자리에서 일부러 단순하게 처리한 부분(정규식 대신
split등)이 있으면, 제약이 없을 때 어떻게 바꿀지 말로 덧붙인다.
# 1. 입력: N(≤ 100,000), 수열 A
# 2. 아이디어: 정렬 후 투 포인터 → O(N log N)
# 3. TC: N=1, 모두 같은 값, 정답이 없는 경우
# 4. 출력: 조건을 만족하는 쌍의 개수출제 유형과 우선순위
기업 코딩 테스트에 자주 나오는 알고리즘·자료구조를 출제 빈도 순으로 묶었다. 「반드시」부터 개념을 익힌 뒤 유형별 문제로 연습하고 범위를 넓힌다.
| 우선순위 | 유형 | 핵심·쓰는 곳 | 노트 |
|---|---|---|---|
| 반드시 | 정렬 | 순서대로 놓아야 탐색·비교가 효율적이다 | 기본 정렬, 병합·퀵 정렬 |
| 반드시 | 완전 탐색·백트래킹 | 가능한 경우를 모두 보고 가지치기한다. 재귀·순열로 변형된다 | 완전 탐색과 백트래킹 |
| 반드시 | BFS / DFS | 그래프·격자 전체 훑기. 미로 탈출, 영역 세기, 가중치 없는 최단 거리 | BFS, DFS |
| 반드시 | 스택·큐·덱·해시 | LIFO·FIFO, 평균 O(1) 조회. 자료구조 선택이 곧 풀이인 문제 | 스택, 큐와 덱 |
| 반드시 | 우선순위 큐 / 힙 | 넣은 순서와 상관없이 우선순위가 높은 값이 먼저 나온다 | 힙과 우선순위 큐 |
| 반드시 | 동적 계획법 | 반복되는 부분 답을 저장해 재사용한다 | 동적 계획법 |
| 반드시 | 투 포인터, 누적합 | 구간 탐색, 구간 합을 O(1)에 | 투 포인터, 누적합 |
| 반드시 | 이분 탐색 | 정렬된 범위나 「답의 범위」를 절반씩 버린다 | 이분 탐색 |
| 반드시 | 구현·시뮬레이션, 문자열 | 요구 사항을 그대로 옮기는 문제 | |
| 반드시 | 에라토스테네스의 체, 유클리드 호제법 | 소수 판별, 최대공약수·최소공배수 | |
| 자주 | 다익스트라, 플로이드-워셜 | 한 점에서 모든 점, 모든 점 사이 최단 거리 | 다익스트라 |
| 자주 | 유니온 파인드, 크루스칼·프림 | 같은 집합 판별, 최소 신장 트리(크루스칼은 간선을 정렬해 고르고, 프림은 정점에서 뻗어 나간다) | 서로소 집합 |
| 자주 | LIS, LCS, 진법 변환 | 대표 DP 유형과 수 표현 | LIS |
| 여유 있으면 | 위상 정렬, 트라이, KMP·라빈-카프·보이어-무어, 비트마스킹, 세그먼트 트리 | 선후 관계, 문자열 검색, 상태 압축, 구간 질의 | 위상 정렬, KMP, 비트 연산 |
연습 방법
- 쉬운 단계(프로그래머스 1레벨)로 언어 문법부터 손에 붙인다. 문법이 막히면 알고리즘을 떠올려도 못 쓴다.
- 유형별 문제집(프로그래머스 고득점 Kit, 백준 단계별·solved.ac 태그)으로 위 표를 차례로 훑는다.
- 문제를 읽으면 제한 조건의 입력 크기로 허용 복잡도를 먼저 계산해 후보 알고리즘을 좁힌다(아래 표).
- 맞혀도 다른 사람의 풀이를 반드시 본다. 더 짧고 빠른 방법을 배운다.
막혔을 때 풀이를 볼 시점을 미리 정해 두면 시간을 버리지 않는다.
| 상황 | 기준 | 다음 행동 |
|---|---|---|
| 손도 못 댐 | 30분 | 아직 준비가 안 된 유형이다. 개념을 공부하거나 풀이를 본다 |
| 풀었는데 시간 초과 | 1시간 | 다른 알고리즘·자료구조를 검토한다. 떠오르지 않으면 풀이를 본다 |
| 일부 케이스만 틀림 | 2시간 | 엣지 케이스를 못 찾으면 처음부터 다시 풀거나 풀이를 본다 |
| 풀이를 보고 풂 | 1주 뒤 | 같은 문제를 다시 혼자 푼다 |
자기기만 피하기
위 기준표에는 함정이 있다. 「n분 동안 풀어 보고 안 되면 풀이를 본다」가 실제로는 「n분 동안 푸는 척하다가 풀이를 본다」가 되기 쉽다. 어차피 곧 풀이를 볼 테니 흔한 접근 몇 개를 건드려 볼 뿐 문제에 온전히 몰두하지 않는다. 겉으로는 매일 꾸준히 연습하는 것과 구별되지 않아서 남도, 나 자신도 알아채기 어렵다. 실력이 늘지 않는 가장 큰 원인이 이런 자기기만(self-deception)인 경우가 많다.
| 자기기만 | 모습 | 막는 법 |
|---|---|---|
| 풀이를 전제로 한 연습 | 정해 둔 시간이 지나기만 기다린다 | 「언제 볼까」가 아니라 「볼 것인가」를 묻는다. 풀이를 볼 생각 없이 끝까지 붙잡고, 풀이는 비상시에만 본다. 풀이가 아예 없는 문제집(USACO Training Pages)이 이 효과를 강제한다 |
| 「풀이를 봐야 새 기법을 배운다」 | 풀이에 나온 한 줄짜리 성질(예: x mod y는 x/2보다 작거나 x 그대로다)을 외울 기법으로 여긴다 | 그런 성질은 외우는 것이 아니라 대회 중에 스스로 떠올려야 하는 것이다. 처음에 스스로 떠올린 사실은 다음에도 남을 확률이 훨씬 높다. 입문 단계를 벗어나는 데 필요한 지식은 DFS, 이분 탐색, 중학교 수학 정도다 |
| 편향된 문제 선택 | 같은 난이도 목록에서 쉽게 풀리는 것만 풀고 막히는 것은 넘긴다. 추천기를 여러 번 다시 돌려 아는 유형이 나올 때까지 고른다 | 한 번에 몇 문제만 보여 주고 모두 풀어야 다음으로 넘어가는 방식(USACO Training, 예전 A2OJ의 단일 문제 사다리)을 쓴다. 「1600 난이도 100문제」가 정말 무작위 표본인지 의심한다 |
| 푼 문제 수를 목표로 삼기 | 개수를 늘리려고 가장 쉬운 방식으로 많이 푼다 | 푼 문제 수는 노력의 지표로는 괜찮지만 목표가 되면 연습의 목적을 무너뜨린다(굿하트의 법칙) |
| 엉뚱한 데서 원인 찾기 | 「하루 5시간으로 안 되니 8시간」, 「FFT·HLD 같은 고급 알고리즘을 몰라서」, 「개인 코치나 합숙이 없어서」 | 늘지 않는다면 양이 아니라 방식이 틀렸다는 신호다. 높은 단계 전까지는 취미 수준으로 해도 된다. 고급 알고리즘이 발목을 잡는 일은 드물고, 코치·합숙은 이미 높은 수준인 사람에게 주로 도움이 된다. 실력의 대부분은 스스로의 연습에서 나온다 |
| 낮은 점수 합리화 | 「실력은 더 높은데 레이팅이 낮다」 | 레이팅은 실제 대회 성적에서 계산되므로 꽤 객관적이다. 잘된 몇 번(최고의 경우)을 남의 평균과 비교하거나, 대회 형식이 달라서라고 둘러대지 않는다. 오래 쉬었거나 가끔만 나가는 경우처럼 예외는 있지만 드물다 |
좋은 연습법은 자기를 속일 수 없게 만드는 합리적인 방법이면 무엇이든 된다. 문제 자체에 대한 흥미가 깊으면 거의 어떤 방법도 이 조건을 만족하고, 흥미 없이 취업 같은 외부 동기만 있으면 거의 어떤 방법도 만족하지 못한다. 풀이를 보는 것 자체가 나쁜 것은 아니다. 다만 한동안 풀이 없이 끝까지 풀어 보면 아이디어를 떠올리는 일이 생각보다 가능하다는 것을 알게 된다.
목표 난이도는 회사마다 다르다. 대체로 대형 IT 기업은 프로그래머스 3레벨, 대기업 SI는 2레벨 수준을 기준으로 잡는다. 연습 사이트는 국내 기업 대비로 백준·프로그래머스, 삼성 대비로 SW Expert Academy, 해외 기업 대비로 LeetCode·Codeforces가 대표적이다.
코딩 테스트는 입구일 뿐이다. 프로젝트와 CS 기초 공부를 함께 가져간다.
반례 찾기: 랜덤 비교 테스트
예제도 통과하고 알려진 반례도 통과하는데 틀렸다면, 작은 입력을 무작위로 만들어 확실히 맞는 풀이와 결과를 비교한다. 스트레스 테스트(stress test)라고도 부른다.
| 구성 요소 | 역할 |
|---|---|
| 입력 생성기 | 문제 조건을 지키는 입력을 random으로 만든다. 범위는 아주 작게(N ≤ 10, 값 ≤ 20) 잡아야 찾은 반례를 손으로 따라갈 수 있다 |
| 정답 풀이 | 느려도 확실한 풀이. 완전 탐색이 가장 좋다. 남의 정답 코드를 써도 된다 |
| 검사 풀이 | 제출해서 틀린 내 풀이 |
| 비교 루프 | 두 결과가 다를 때까지 반복하고, 다르면 입력과 두 답을 출력하고 멈춘다 |
from random import randint
def gen():
k = randint(1, 5)
return [randint(1, 20) for _ in range(k)], randint(1, 10)
for _ in range(10_000): # 재귀 대신 반복문: 깊이 제한에 걸리지 않는다
lines, n = gen()
expected, got = brute(lines, n), solve(lines, n)
if expected != got:
print(lines, n, "정답:", expected, "내 답:", got)
break- 반례가 나오면 그 입력을 고정해 두고 디버깅한다. 이분 탐색의 경계(
>vs>=, 시작값 0 vs 1), 0으로 나누기, 정수 오버플로 같은 실수가 주로 걸린다. - C·C++ 풀이도 같은 방식으로 비교할 수 있다. 셸 스크립트로 생성기·두 실행 파일을 돌려 출력 파일을
diff하면 언어와 무관하게 쓸 수 있다. - 시간 초과는 이 방법으로 못 찾는다. 최대 크기 입력을 따로 만들어 실행 시간을 잰다.
입력 크기로 풀이 방향 정하기
문제를 읽으면 먼저 N의 범위를 보고 허용되는 시간 복잡도를 정한다. 1초에 C/C++은 약 10⁸번, 파이썬은 약 10⁷번의 단순 연산을 한다고 어림잡는다(경험칙).
| N | 목표 복잡도 | 떠올릴 기법 |
|---|---|---|
| ~10 | O(N!) | 순열 완전 탐색 |
| ~20 | O(2ᴺ) | 부분집합, 비트마스크, 백트래킹 |
| ~500 | O(N³) | 삼중 반복문, 플로이드-워셜 |
| ~5,000 | O(N²) | 이중 반복문, 2차원 DP |
| ~1,000,000 | O(N log N) | 정렬, 이분 탐색, 힙, 다익스트라 |
| 그 이상 | O(N), O(log N) | 투 포인터, 누적 합, 수학 |
자주 하는 선택
- 최단 거리 / 최소 횟수, 가중치 없음 → BFS
- 최단 거리, 가중치 있음(음수 없음) → 다익스트라
- 모든 경우를 봐야 함 → DFS / 백트래킹, 경우의 수부터 계산
- 최적값 + 같은 하위 문제가 반복 → DP
- 정렬 후 매 순간 최선의 선택이 정당함 → 그리디
- 정렬된 데이터에서 찾기, "최댓값/최솟값을 구하라" → 이분 탐색, 파라메트릭 서치
- 구간 합 쿼리가 많음 → 누적 합
- 조회가 느림 → 해시 맵. 면접 문제에서 가장 자주 쓰는 자료구조다(해시 테이블)
- 개체 사이의 관계로 주어진 데이터 → 그래프로 모델링(그래프). 선후 관계면 위상 정렬(위상 정렬)
- 괄호·수식처럼 중첩된 문자열 파싱 → 스택
- 우선순위에 따른 스케줄링, 상위·하위 K개, 중앙값 → 힙
- 문자열 집합의 접두사 검색 → 트라이(기초 자료구조)
- 연속 구간, 정렬된 배열의 쌍 → 투 포인터·슬라이딩 윈도우(투 포인터와 슬라이딩 윈도우), 뒤에서부터 순회
- 거듭제곱·제곱근·나눗셈을 O(n)보다 빠르게 → 지수를 반씩 줄이는 반복 제곱, 답을 반씩 좁히는 이분 탐색
- 같은 함수가 여러 번 호출됨(서버 API 등) → 입력을 한 번 전처리(정렬, 해시, 트라이, 접두사 합)해 두고 호출마다 재사용
- 연산마다 O(1)이 필요함 → 자료구조를 조합한다. LRU 캐시는 해시 맵 + 이중 연결 리스트로 조회·갱신이 모두 O(1)이다
놓치기 쉬운 경계 사례
정답 코드가 틀리는 입력은 대개 정해져 있다. 입력 유형을 보고 아래 경우를 TC에 넣는다.
| 입력 | 확인할 경우 |
|---|---|
| 배열·문자열 | 빈 입력, 원소 1개와 2개, 모두 같은 값, 중복 값, 모두 다른 값. 인덱스가 범위를 벗어나지 않는가(off-by-one) |
| 행렬 | 빈 행렬(행이 0개, 또는 첫 행의 길이가 0), 1×1, 한 행 또는 한 열뿐인 행렬 |
| 연결 리스트 | head가 null, 노드 1개와 2개, 사이클(있을 수 있는지 먼저 묻는다) |
| 트리 | 빈 트리, 노드 1개, 한쪽으로만 뻗은 편향 트리, 음수 값 노드(경로 합 문제) |
| 그래프 | 빈 그래프, 정점 1~2개, 연결되지 않은 그래프, 사이클 |
| 구간 | 구간 0개·1개, 겹치지 않는 구간, 다른 구간에 완전히 포함된 구간, 똑같은 구간, 끝점이 맞닿은 [1, 2], [2, 3] |
| 스택·큐·힙 | 비어 있을 때 꺼내기, 원소 1~2개 |
| 수 | 0으로 나누기·나머지, 정수 오버플로·언더플로, 음수, 실수 오차, 0과 1 같은 항등원 |
| 재귀 | n = 0, n = 1. 재귀 호출이 f(n-2)까지 내려가면 기저 사례도 두 개 필요하다 |
고정 폭 정수 언어(C·C++·Java)에서는 오버플로를 처리하거나 적어도 가능성을 언급한다. 정수 문자열·리스트를 이어 붙이는 코드는 타입이 섞이지 않았는지도 본다.
막혔을 때 바꿔 보는 관점
풀이가 떠오르지 않으면 문제를 다른 모양으로 바꿔 본다.
| 관점 | 해 보는 것 |
|---|---|
| 예제를 그린다 | 너무 작지 않은 중간 크기 예제를 손으로 풀어 본다. 작은 예제의 답은 일반화되지 않을 때가 많다. 트리·그래프·행렬·연결 리스트는 단계별 상태를 그림으로 그린다(행렬 나선형 순회는 방향을 그려 보면 규칙이 보인다). 예제를 여러 개 만들어 두면 끝난 뒤 테스트 케이스로 쓴다 |
| 손으로 푼다 | 코드 없이 사람이 푸는 절차를 말로 적는다. 그 절차를 모든 예제에 통하는 규칙으로 다듬으면 그대로 코드가 된다. 이진 탐색 트리 검증은 「왼쪽 서브트리는 모두 루트보다 작고 오른쪽은 모두 크다를 노드마다 확인」이라는 손 풀이에서 출발한다 |
| 쪼갠다 | 큰 문제는 상위 함수를 먼저 쓰고 독립된 하위 함수로 나눈다. Group Anagrams는 「문자열을 해시하기」와 「해시로 묶기」 두 함수로 나뉜다. 다 못 짜도 접근이 있다는 것은 보인다. 최적 풀이는 이 추상화를 깨고 한 번에 처리해야 할 때도 있다 |
| 기법을 대입한다(하향식) | 정렬·이분 탐색, 분할 정복, DP, 그리디, 재귀, 특정 자료구조(배열, 스택·큐, 해시, 트리, 힙, 그래프)를 하나씩 대입해 맞는지 본다 |
| 비슷한 문제를 떠올린다 | 겉모양보다 핵심 연산이 같은 예전 문제를 찾고 그 풀이를 고친다 |
| 작게·특수하게 줄인다 | 경계값, 특수한 경우, 단순화한 버전부터 풀고 범위를 넓힌다 |
| 단순한 풀이부터 | 완전 탐색으로 맞는 답을 먼저 만들고 중복 계산을 찾아 줄인다 |
| 식으로 쓴다 | 조건을 수식·점화식으로 옮기면 DP나 수학 풀이가 보인다 |
| 순서를 강제한다 | 순서가 없는 선택을 정렬된 순서로 고정하면 경우의 수가 줄어든다 |
| 뒤에서부터 본다 | 마지막 상태에서 거꾸로 오면 선택지가 줄어드는 문제가 있다 |
| 되돌아간다 | 몇 분을 써도 진전이 없는 접근은 버리고 다른 방법으로 간다 |
풀이 최적화: 더 빠르게, 더 작게
동작하는 풀이를 찾은 뒤에는 「더 나은 방법이 있나」를 따진다. 면접관이 「더 잘할 수 있을까요?」라고 묻는 지점이다.
이론상 최선 복잡도(BTTC)부터 정한다
이론상 최선 시간 복잡도(Best Theoretical Time Complexity, BTTC)는 어떤 풀이로도 넘을 수 없는 하한이다. 대개 「입력을 적어도 한 번은 봐야 한다」에서 나온다.
| 문제 | BTTC | 이유 |
|---|---|---|
| 배열의 합 | O(n) | 모든 값을 한 번은 본다 |
| 애너그램 묶기(단어 n개, 최대 길이 k) | O(nk) | 모든 단어의 모든 글자를 본다 |
| 행렬의 섬 개수(n×m) | O(nm) | 모든 칸을 본다 |
| 정렬된 배열에서 값 찾기 / 최댓값 | O(log n) / O(1) | 정렬이라는 조건이 하한을 바꾼다. 이진 탐색, 마지막 원소 |
- 최적 풀이는 지금 풀이와 BTTC 사이에 있다. BTTC보다 빠른 풀이를 찾느라 시간을 쓰지 않는다. BTTC를 면접관에게 말하는 것도 좋은 신호다.
- 문제의 조건(정렬됨, 범위 제한)을 놓치면 BTTC를 잘못 잡는다.
- 이미 BTTC인데 더 최적화하라고 하면, 대개 같은 복잡도에서 일을 덜 하기(두 번 훑던 것을 한 번에)나 공간 줄이기를 묻는 것이다.
시간 줄이기
| 기법 | 내용 | 예 |
|---|---|---|
| 겹치는 계산 재사용 | 완전 탐색은 같은 연산을 반복한다. 앞의 결과를 저장해 쓴다. DP가 대표지만 전처리로 푸는 문제도 있다 | Product of Array Except Self: result[i]와 result[i+1]의 곱이 대부분 겹친다. 앞쪽·뒤쪽 누적 곱 배열로 O(n²) → O(n) (누적합) |
| 자료구조 바꾸기 | 조회가 병목이면 해시 테이블로 O(1)에 | K Closest Points: 전부 정렬하면 O(n log n), 크기 K로 제한한 힙을 쓰면 O(n log k) |
| 불필요한 작업 걷어내기 | 복잡도는 같아도 코드 품질로 평가된다 | 아래 목록 |
불필요한 작업의 흔한 모양은 다음과 같다.
- 중복 조건:
not arr and len(arr) == 0,x < 5 and x < 10은 뒤 조건이 필요 없다. - 조건 순서:
or는 빠르거나 참일 가능성이 높은 쪽을,and는 거짓일 가능성이 높은 쪽을 앞에 둔다(단락 평가). - 반복 호출 캐싱: 함수 안에서 바뀌지 않는
len(array)같은 값은 변수에 한 번 담는다. - 조기 종료: 답이 정해지면 바로
return한다. 긴장하면 가장 자주 잊는다. - 반복문 안 작업 줄이기: 반복마다 같은 값을 다시 계산하지 않는다(루프 밖으로 꺼낸다).
- 지연 평가: 필요할 때까지 계산·초기화를 미룬다. 입력이 비었으면 해시 테이블도 만들지 않는다.
def contains_string(search_term, strings):
if not strings: # 지연: 빈 입력이면 아무것도 계산하지 않는다
return False
target = search_term.lower() # 반복문 밖으로: 매번 lower()하지 않는다
for s in strings:
if s.lower() == target:
return True # 조기 종료
return False공간 줄이기
보통 시간이 공간보다 중요하다. 최적 시간에 도달한 뒤 추가 공간을 줄이라는 요청이 온다.
| 기법 | 내용 | 예 |
|---|---|---|
| 입력을 제자리에서 바꾸기 | 새 배열 대신 입력 배열을 덮어써 O(1) 추가 공간으로 | Sort Colors(네덜란드 국기 문제): 새 배열이면 공간 O(n), 제자리 정렬이면 O(1) |
| 입력 배열을 해시 테이블처럼 쓰기 | 값의 존재를 인덱스 위치의 부호로 표시한다 | First Missing Positive: 4가 있으면 nums[4]를 음수로 바꾼다 |
| 자료구조 바꾸기 | 같은 정보를 더 촘촘히 저장하는 구조를 고른다 | 접두사로 시작하는 문자열 개수 세기는 트라이가 저장과 질의 모두 효율적이다 |
입력을 바꾸는 기법은 면접용이다. 실무에서 입력 변경은 코드를 읽고 유지하기 어렵게 만든다. 뒤에서 원래 입력이 필요하면 되돌릴 수 없게 망가뜨리지 않는다. 면접에서도 입력을 바꿔도 되는지 먼저 묻는다.
파이썬 실전 팁
import sys
from collections import deque
import heapq
input = sys.stdin.readline # 입력이 많을 때 input()보다 훨씬 빠르다
sys.setrecursionlimit(10 ** 6) # 재귀 DFS 사용 시 기본 한도(약 1000) 늘리기
n = int(input())
arr = list(map(int, input().split()))
s = input().rstrip() # readline은 줄바꿈 문자를 포함하므로 제거- 큐는
list.pop(0)(O(n)) 대신deque.popleft()(O(1)) - 우선순위 큐는
heapq(최소 힙). 최대 힙은 음수로 넣기 - 원소 존재 여부를 자주 확인하면 리스트(O(n)) 대신
set/dict(평균 O(1)) - 순열·조합은
itertools의permutations,combinations,product(중복 순열),combinations_with_replacement(중복 조합) - 개수 세기는
collections.Counter, 정렬된 리스트의 위치 찾기는bisect_left·bisect_right - 실수 계산은 오차가 있다(
0.3 + 0.6 == 0.8999999999999999). 비교는abs(a - b) < 1e-9나math.isclose로 하고, 가능하면 정수로 바꿔 계산한다. - 2차원 리스트는
[[0] * m for _ in range(n)]으로 만든다.[[0] * m] * n은 같은 행을 공유하는 버그가 생긴다. - 문자열을 반복해서 이어 붙일 때는 리스트에 모은 뒤
''.join() - 출력이 많으면
print('\n'.join(map(str, result)))로 한 번에 출력 - 리스트를 공백으로 구분해 한 줄에 출력할 때는
print(*arr).*는 iterable을 개별 인자로 풀어 넘기는 언패킹 연산자다. 딕셔너리는**로 키워드 인자로 풀 수 있다('{y}-{m}'.format(**info)). 이때 이미 넘긴 인자와 같은 키가 겹치면TypeError가 난다.
자바스크립트 실전 팁
// Node.js에서 표준 입력 전체 읽기
const input = require('fs').readFileSync(0, 'utf8').trim().split('\n')
const [n, m] = input[0].split(' ').map(Number)| 상황 | 쓰는 것 | 함정 |
|---|---|---|
| 숫자 정렬 | arr.sort((a, b) => a - b) | 비교 함수 없는 sort()는 문자열로 비교한다([10, 9, 1] → [1, 10, 9]) |
| 원본을 지키며 정렬 | arr.toSorted(cmp)(ES2023) 또는 [...arr].sort(cmp) | sort는 원본을 바꾼다 |
| 여러 기준 정렬 | (a, b) => a[n].localeCompare(b[n]) || a.localeCompare(b) | 첫 기준이 같으면 0이 나와 || 뒤로 넘어간다 |
| 개수 세기 | const cnt = new Map(); for (const x of arr) cnt.set(x, (cnt.get(x) ?? 0) + 1) | 「참가자 중 완주 못 한 한 명」처럼 두 목록의 차이는 한쪽을 세고 다른 쪽으로 빼면 O(n). 둘 다 정렬해 비교하면 O(n log n) |
| 중복 제거 | [...new Set(arr)] | 매번 includes로 확인하면 O(n²) |
| 진법 변환 | n.toString(3), parseInt('1021', 3) | 자릿수 뒤집기도 문자열로 한다: parseInt([...n.toString(3)].reverse().join(''), 3) |
| 연속 정수의 합 | (a + b) * (Math.abs(a - b) + 1) / 2 | 반복문 없이 O(1). 큰 수는 Number.MAX_SAFE_INTEGER를 넘는지 보고 BigInt |
| 문자 개수 | str.split('8').length - 1, [...str].filter((c) => c === '8').length | 정규식 match는 결과가 없으면 null을 돌려준다 |
| 요일·날짜 | 월별 일수 배열을 누적해 % 7, 또는 new Date(y, m - 1, d).getDay() | Date의 월은 0부터 센다 |
| 문자열 이어 붙이기 | 배열에 모아 join('') | 반복문 안의 +=도 엔진이 최적화하지만 출력이 크면 join이 안전하다 |
핵심 정리
- 주석 설계 → TC 작성 → 구현 → 정리 순서로 푼다.
- N의 범위에서 허용 복잡도를 역산해 알고리즘을 고른다.
- 최적화는 이론상 최선 복잡도(BTTC)를 하한으로 잡고, 겹치는 계산 재사용·자료구조 교체·불필요한 작업 제거 순으로 본다.
- 막히면 30분·1시간·2시간 기준으로 풀이를 보고, 1주 뒤 다시 푼다. 다만 「푸는 척하다 풀이 보기」와 쉬운 문제만 고르기가 되지 않게, 때로는 풀이 없이 끝까지 푼다.
sys.stdin.readline,deque,heapq,setrecursionlimit은 기본 장비다.
관련 노트: 빅오 표기법, 기술 면접 준비, 프론트엔드 면접 유형과 준비법, 투 포인터와 슬라이딩 윈도우, BFS, 다익스트라, 동적 계획법, 그리디, 이분 탐색, 접두사 합, 면접 잘 보는 법, 초보 웹 개발자의 공부 방향과 차별화, KMP 문자열 검색 알고리즘, 컴퓨팅 사고와 개발 실력 늘리는 법
원본 기록17
- 원문 (새 창) riverkim.kr/46
- 원문 (새 창) riverkim.kr/49
- 원문 (새 창) mungto.tistory.com/516
- Notion 원문 (새 창) 80000coding.notion.site/bfd36598b2024bd09ff4528db740e054
- Notion 원문 (새 창) 80000coding.notion.site/1dbfa6dd227e4749b83ae2cefccdda9f
- GitHub 원문 (새 창) github.com/AI-Dev-Lex-Kim/Frontend_TIL/blob/master/알고리즘/완주하지 못한 선수.md
- GitHub 원문 (새 창) github.com/AI-Dev-Lex-Kim/Frontend_TIL/blob/master/알고리즘/문자열 내맘대로 정렬하기.md
- GitHub 원문 (새 창) github.com/AI-Dev-Lex-Kim/Frontend_TIL/blob/master/알고리즘/3진법 뒤집기.md
- GitHub 원문 (새 창) github.com/AI-Dev-Lex-Kim/Frontend_TIL/blob/master/알고리즘/두 정수사이의 합.md
- GitHub 원문 (새 창) github.com/AI-Dev-Lex-Kim/Frontend_TIL/blob/master/알고리즘/2016년.md
- GitHub 원문 (새 창) github.com/AI-Dev-Lex-Kim/Frontend_TIL/blob/master/알고리즘/두 개 뽑아서 더하기.md
- GitHub 원문 (새 창) github.com/jbee37142/Interview_Question_for_Beginner/blob/main/Algorithm/README.md
- 원문 (새 창) www.techinterviewhandbook.org/coding-interview-techniques/
- 원문 (새 창) www.techinterviewhandbook.org/algorithms/study-cheatsheet/
- 원문 (새 창) www.techinterviewhandbook.org/algorithms/math/
- 원문 (새 창) www.techinterviewhandbook.org/algorithms/interval/
- 원문 (새 창) codeforces.com/blog/entry/98621