알고리즘 Study/코딩테스트5일벼락치기

CHAPTER 1. 실전 대비 & 필수 문법 (전략 & 기본 도구)

쿠리유짱 2026. 8. 30. 00:39

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. 핵심 요약

  1. lambda는 한 줄짜리 일회용 미니 함수다.
  2. key=lambda x: x[1] 은 "1번 인덱스 요소(나이, 두 번째 값 등)를 기준으로 정렬하라"는 뜻이다.
  3. map(lambda x: ..., nums) 은 "nums에서 원소를 하나씩 꺼내 x에 대입하며 계산을 수행하라"는 뜻이다.
반응형