SKT 개발직군에 지원하게 되면서 이제 진짜 코딩테스트를 준비해야할 때가 왔다.
그러나 사실상 노베이스나 마찬가지라, 상당히 시간이 제한적인 상태이다. 지금 본 게시글을 올리는 시점에서 약 5일정도밖에 시간이 주어지지 않은 상황이나 마찬가지다.
그래서 최소한의 시간투자로 최대한의 결과물을 얻어보고자 아주 필수적인 코딩테스트 기초부터 진행해보고자 한다.
우선 챕터는 4가지로 나눠보며, 이 4개 챕터동안 코딩테스트의 아주 기초를 잡고자 한다. 단, 어느정도 파이썬을 썼던 사람이라고 가정하고 4개 챕터로 나누었다.
이후에는 프로그래머스로 해당 챕터에서 다루었던 기초를 실제 적용해보는 시간을 가져보고자 한다.
본 게시글은 챕터 1을 다루고 있으며, 아래 목차 순서대로 포스팅을 이어나갈 예정이다.
- 목차 개요
- CHAPTER 1. 실전 대비 & 필수 문법 (전략 & 기본 도구)
- CHAPTER 2. 탐색 & 탐색 알고리즘 (격자, BFS, DFS)
- CHAPTER 3. 핵심 선형 자료구조 (스택, 큐, 해시)
- CHAPTER 4. 효율적인 탐색 & 우선순위 (이진 탐색, 힙)
CHAPTER 1. 실전 대비 & 필수 문법
1-1. 실전 디버깅 & 예외 케이스 처리
- print() 디버깅: 머리로 추적하지 않고 f-string으로 터진 위치 직접 확인
- 3대 극단 예외 케이스:
- 빈 배열 처리: if not arr:
- 원소가 1개일 때: if len(arr) == 1:
- 0으로 나누기 방지: if divisor == 0:
- 시간 초과 진단 3요소: list 내 in 연산, list.pop(0), while 무한 루프 체크
실전 디버깅 & 예외 케이스 처리 팁
1. 무조건 print() 찍어보기 (뇌피셜 금지!)
코드실행이 안되거나 에러가 날때 간단한 코드야 머릿속으로 코드를 추적할 수 있어도, 조금이라도 복잡한 경우에는 쉽지 않을것이다. 이때 사용하는것이 "f-string 찍기 테크닉"
# 반복문 내부나 핵심 변수 뒤에 print를 찍어줌
for i in range(len(arr)):
print(f"--- [현재 i: {i}] arr[i]: {arr[i]}, 현재 stack: {stack} ---")
- 어디서 터졌나 확인: print() 출력이 어디서 끊겼는지 보고, 예를들어 "3번째 반복문 진입할 때 에러가 났구나!"하고 터진 위치를 바로 알 수 있음.
- 단, 제출전에는 주석처리하거나 다시 삭제해야함.
2. 코테 감점 방지 3대 예외 케이스 (if문 하나로 점수 얻기)
알고리즘 로직을 맞게 짜도 특이 조건 하나 때문에 테스트케이스 2~3개에서 실패가 나는 경우가 있음. 코드 맨 위에 3가지 예외 처리를 먼저 깔고 시작하자.
① 입력 배열이 비어있는 경우 (if not arr:)
빈 배열이 들어왔는데 arr[0] 같은 걸 참조하면 프로그램이 바로 터짐. 따라서 예외 처리를 해줘야함.
if not arr:
return 0 # 혹은 빈 리스트 [] 반환
② 데이터가 딱 1개만 들어온 경우 (N = 1)
데이터가 여러 개일 거라 생각하고 i+1 인덱스를 접근하거나 두 번째 값부터 비교하는 로직을 짰다면 N=1일 때 에러남.
if len(arr) == 1:
return arr[0] # 예외적으로 1개일 때의 답을 바로 반환
③ 값이 0이거나 음수인 경우
나누기 연산(/, %)을 할 때 분모가 0이 되는 순간 ZeroDivisionError가 발생함.
if divisor == 0:
continue # 0으로 나누는 케이스 건너뛰기
3. 시간 초과가 날 때의 3초 진단법
잘 짠거 같은데 시간 초과가 뜬다면 100% 다음 3가지 중 하나임.
- list에서 in을 썼는가?
- if target in my_list:는 O(N)이라 느림. set이나 dict로 바꿔주면 즉시 O(1)이 됨
- list.pop(0)을 썼는가?
- 앞쪽 원소를 빼는 건 O(N)이나, from collections import deque 불러와서 popleft()로 바꾸면 O(1)이 됨
- 무한 루프에 빠졌는가?
- while문 안에서 포인터 증가(start += 1)나 큐 집어넣기 조건이 빠져서 계속 돌고 있는지 확인함
1-2. 문자열 다루기 & 정렬 (구현)
- 문자열 조작:
- 쪼개고 합치기: split(), "구분자".join(arr) 결합 원리
- 치환 & 슬라이싱: replace(), 문자열 뒤집기 s[::-1]
- 정렬 (Sorting):
- sort() vs sorted() 차이
- key=lambda를 활용한 커스텀 다중 조건 정렬
문자열 다루기 핵심 문법
1. split()과 join() - 쪼개고 합치기
s = "apple,banana,orange"
# 쉼표를 기준으로 쪼개서 리스트 생성
arr = s.split(",") # ['apple', 'banana', 'orange']
# "구분자".join(리스트) -> 구분자를 풀(Glue)처럼 사이에 붙여서 문자열로 합침!
result1 = ", ".join(arr) # "apple, banana, orange"
result2 = "-".join(arr) # "apple-banana-orange"
result3 = "".join(arr) # "applebananaorange" (공백 없이 완전 결합)
2. replace() - 특정 문자 치환하기
date_str = "2026-08-30"
clean_date = date_str.replace("-", ".") # "2026.08.30"
3. 슬라이싱 & 유용한 메서드
text = "aBc123"
# 문자열 뒤집기 (파이썬 치트키)
reversed_text = text[::-1] # "321cBa"
# 대소문자 변환 & 숫자 체크
upper_text = text.upper() # "ABC123"
lower_text = text.lower() # "abc123"
is_num = "123".isdigit() # True
정렬 (Sorting) & key=lambda 활용
1. sort() vs sorted() 차이
nums = [5, 2, 8, 1]
# sort(): 원본 자체를 정렬 (반환값 없음)
nums.sort() # [1, 2, 5, 8] (오름차순)
nums.sort(reverse=True) # [8, 5, 2, 1] (내림차순)
# sorted(): 원본은 유지하고 정렬된 새 리스트 반환
original = [3, 1, 4]
new_list = sorted(original) # original은 [3, 1, 4] 유지, new_list는 [1, 3, 4]
2. key=lambda 커스텀 정렬 (코테 필수!)
# 예시 1: 튜플의 특정 인덱스(나이) 기준 정렬
users = [("김철수", 30), ("이영희", 20), ("박민수", 25)]
users.sort(key=lambda x: x[1])
# 결과: [('이영희', 20), ('박민수', 25), ('김철수', 30)]
# 예시 2: 단어 길이 기준 정렬
words = ["apple", "cat", "banana"]
words.sort(key=lambda x: len(x))
# 결과: ['cat', 'apple', 'banana']
* 번외편
람다식(lambda)이란?
코딩테스트 문제를 풀거나 정렬 코드를 작성하다 보면 항상 마주치는 요상한 구문이 있다.
users = [("김철수", 30), ("이영희", 20), ("박민수", 25)]
users.sort(key=lambda x: x[1])
도대체 key는 뭐고, lambda x: x[1]은 무엇을 의미하는 걸까? 람다식의 정체부터 자주 쓰이는 패턴까지 직관적으로 정리해 보자.
1. key와 lambda x: x[1]의 정체
① key : "무엇을 기준으로 정렬할 것인가?" (손잡이)
- 기본 sort()는 숫자는 크기순, 문자열은 알파벳순으로 정렬해 줌.
- 하지만 ("김철수", 30)처럼 원소가 여러 개 들어있는 튜플/리스트가 들어오면, 파이썬은 "이름(0번)으로 정렬해? 나이(1번)로 정렬해?" 하고 판단을 내리지 못함.
- 이때 key는 파이썬에게 "이 값을 기준으로 정렬해라!" 하고 기준(손잡이)을 지정해 주는 역할을 함.
② lambda : "한 줄짜리 일회용 미니 함수"
- sort()의 key 속성은 "리스트 알맹이를 받아 정렬 기준값을 꺼내줄 '함수'를 달라"고 요구함.
- 정석대로 쓰려면 아래처럼 def 함수를 따로 만들어야 함.
# 정석적인 함수 정의
def get_age(x):
return x[1] # 1번 인덱스(나이)를 반환
users.sort(key=get_age)
- 하지만 겨우 정렬 기준 하나 정하자고 함수를 일일이 만드는 건 귀찮고 코드도 길어짐.
- 그래서 get_age 함수를 한 줄로 줄여 쓴 치트키가 바로 lambda x: x[1] 임.
③ lambda x: x[1] 내부 동작 원리
"리스트에서 튜플을 하나씩 꺼내서 x라고 부를게. 그 x에서 x[1](나이)만 쏙 뽑아서 정렬 기준으로 써라!"
1. ("김철수", 30) 꺼냄 -> x[1] 은 30
2. ("이영희", 20) 꺼냄 -> x[1] 은 20
3. ("박민수", 25) 꺼냄 -> x[1] 은 25
-> 추출된 기준값 [30, 20, 25] 를 바탕으로 [이영희(20) -> 박민수(25) -> 김철수(30)] 순으로 정렬!
2. list(map(lambda x: x * 2, nums)) 해석하기
람다식은 정렬뿐만 아니라 map()이나 filter() 같은 함수와도 자주 조합된다.
nums = [1, 2, 3, 4, 5]
result = list(map(lambda x: x * 2, nums))
# 결과: [2, 4, 6, 8, 10]
Q. 여기서 x는 왜 써주는 걸까요?
- nums 리스트에는 숫자가 여러 개 들어있다.
- map은 리스트에서 숫자를 한 번에 하나씩 쏙쏙 꺼내서 람다 식에 던져주는 역할이다.
- 이때 "꺼내온 그 숫자 하나를 람다 식 안에서 뭐라고 부를 것인가?" 하고 임시로 이름표를 달아주는 것이 바로 x이다.
(x 대신 n이나 num을 써도 똑같이 동작함.)
Q. x * 2, nums 구문의 의미는?
- map(함수, 리스트)는 nums에 있는 원소들을 하나씩 꺼내서 앞쪽 람다식의 x 자리에 차례대로 대입해 주는 구조임.
- 일반 for문과 비교해 보면 원리가 바로 이해가 가능하다.
# 1) 일반 for문
result = []
for x in nums: # nums에서 하나씩 꺼내 x라고 부름
result.append(x * 2)
# 2) map + lambda (완전히 동일함)
result = list(map(lambda x: x * 2, nums))
- for x in nums:의 x와 lambda x:의 x는 완전히 동일한 역할!
3. 핵심 요약
- lambda는 한 줄짜리 일회용 미니 함수다.
- key=lambda x: x[1] 은 "1번 인덱스 요소(나이, 두 번째 값 등)를 기준으로 정렬하라"는 뜻이다.
- map(lambda x: ..., nums) 은 "nums에서 원소를 하나씩 꺼내 x에 대입하며 계산을 수행하라"는 뜻이다.
'알고리즘 Study > 코딩테스트5일벼락치기' 카테고리의 다른 글
| CHAPTER 4. 효율적인 탐색 & 우선순위 (우선순위 큐, 이진 탐색) (0) | 2026.08.30 |
|---|---|
| CHAPTER 3. 핵심 선형 자료구조 (큐, 스택, 해시) (0) | 2026.08.30 |
| CHAPTER 2. 탐색 & 탐색 알고리즘 (격자, BFS, DFS) (0) | 2026.08.30 |