코딩테스트 구현력 보강: 리스트 컴프리헨션, 딕셔너리, DFS, BFS 정리
오늘은 코딩테스트 문제를 많이 푼 날이라기보다,
문제를 풀기 위해 필요한 파이썬 구현 기본기와 탐색 개념을 같이 정리한 날에 가까웠다.
최근 코테 문제를 풀면서 반복해서 느낀 건 하나였다.
문제를 아예 이해하지 못하는 경우보다,
문제를 이해한 다음 그걸 파이썬 문법으로 구현하는 과정에서 더 자주 막힌다는 점이다.
특히 오늘은 그게 더 분명하게 보였다.
문제 해석은 어느 정도 되는데,
막상 구현에 들어가면 문법 선택이 느려진다.
딕셔너리를 어떻게 세팅할지,
리스트를 쓸지 set을 쓸지,
split()과 map()을 어떻게 연결할지,
컴프리헨션으로 줄일 수 있을지 같은 부분에서 손이 자꾸 멈췄다.
그래서 오늘은 무작정 문제 개수를 늘리기보다,
내가 실제로 막히는 지점을 먼저 보강하는 방향으로 공부했다.
오늘 정리한 흐름은 다음과 같다.
- 신고 결과 문제를 통해 문제 해석 방식 다시 점검
- 리스트 컴프리헨션, 딕셔너리, map(), split() 따로 연습
- DFS / BFS를 배우기 전에 필요한 탐색 개념 정리
- 오늘 공부하면서 느낀 고민과 정리
1. 신고 결과 문제를 다시 보면서 느낀 점
🔗 문제 링크
오늘 신고 결과 문제를 다시 보면서 가장 크게 느낀 건,
문제 설명은 단순한 배경 설명이 아니라는 점이었다.
이런 구현형 문제에서는 오히려
문제 설명 자체가 자료구조 설계 힌트에 가깝다.
오늘 다시 정리하면서 특히 남은 포인트는 이거였다.
- 문제 설명 안에 필요한 변수와 구조 힌트가 많이 들어 있다
- 예외 케이스나 강조 문장은 꼭 체크해야 한다
- 샘플 입력은 손으로라도 따라가 봐야 한다
- 문제를 읽는 동시에 key / value 구조도 같이 떠올려야 한다
정리한 텍스트에서도 문제 설명 과정에 필요한 변수나 세팅 아이디어가 많고, 문제에서 강조하는 부분과 예외 상황을 잘 파악해야 한다는 점이 반복해서 언급돼 있었다. 또 최소한 주어진 샘플은 다 점검해야 놓치는 부분이 줄어든다고 정리돼 있었다.
신고 결과 문제도 머리로는 대충 흐름이 보인다.
- 신고 내역을 정리하고
- 기준 이상 신고당한 사람을 추리고
- 그 사람을 신고한 사람들에게 메일 횟수를 반영한다
그런데 막상 구현으로 옮기면 바로 꼬인다.
- 중복 신고는 어디서 제거할지
- 신고 내역을 어떤 자료형으로 저장할지
- 딕셔너리의 value를 리스트로 둘지 set으로 둘지
- 최종 카운팅은 어느 단계에서 할지
결국 오늘 다시 느낀 건 이거였다.
💡 설명을 빨리 읽는 것보다, 설명에서 구현 힌트를 뽑아내는 게 더 중요하다.
2. 오늘 가장 크게 느낀 부족한 부분은 구현 문법이었다
오늘은 이걸 꽤 솔직하게 느꼈다.
내가 막히는 건 알고리즘 개념 자체보다는,
그걸 파이썬 문법으로 자연스럽게 구현하는 힘 쪽이었다.
문제를 이해하고,
어떤 흐름으로 가야 할지 머리로 정리하는 건 어느 정도 된다.
그런데 실제로 코드를 치기 시작하면 바로 고민이 많아진다.
- 딕셔너리를 어떻게 초기화할까
- append()를 써야 할까, add()를 써야 할까
- for i in range(len(...))로 갈까, 요소 자체를 순회할까
- 컴프리헨션으로 줄일 수 있을까
- split()이랑 map()을 어디서 붙일까
정리 텍스트에서도 파이썬으로 코테를 준비하는 입장에서는 딕셔너리를 자유롭게 쓰는 연습이 중요하고, 문제 이해 → 논리 정리 → 코드화의 세 단계가 다 필요하다고 되어 있었다. 이 부분이 오늘 내가 느낀 상태와 거의 비슷했다.
그래서 오늘은 문제를 계속 붙잡고 있는 대신,
내가 자주 막히는 문법을 따로 연습하는 방향으로 갔다.
이게 오늘은 훨씬 잘 맞았다.
문제를 더 많이 푸는 것보다,
문제를 구현할 수 있게 해주는 문법 감각을 먼저 키우는 게 맞겠다고 느꼈다.
3. 따로 연습한 파이썬 기본기
오늘 따로 연습한 핵심은 다음 네 가지였다.
- 리스트 컴프리헨션
- 딕셔너리 컴프리헨션
- split()
- map()
예전에도 본 적은 있었지만,
오늘은 “이걸 언제 쓰는가” 쪽에 더 집중해서 봤다.
3-1. 리스트 컴프리헨션은 짧게 쓰는 문법이 아니라 변환 방식에 가까웠다
예를 들면 이런 식이다.
numbers = [i for i in range(10)]
squares = [i**2 for i in range(1, 11)]
evens = [i for i in range(21) if i % 2 == 0]
labels = ["even" if i % 2 == 0 else "odd" for i in range(10)]
예전에는 리스트 컴프리헨션을 보면
그냥 “짧게 쓰는 문법” 정도로만 생각했다.
그런데 오늘은 오히려
입력을 어떤 규칙으로 변환할지 한 번에 표현하는 방식처럼 느껴졌다.
문자열이나 리스트를 그대로 순회하는 것도 같이 연습했다.
chars = [ch for ch in "hello"]
lengths = [len(word) for word in ["apple", "banana", "kiwi"]]
연습 파일에도 0부터 9까지 리스트 만들기, 제곱 리스트, 짝수만 남기기, "hello"를 문자 리스트로 바꾸기, 단어 길이 리스트 만들기 같은 예제가 실제로 정리돼 있었다.
이걸 따로 해보니까
range()만 도는 게 아니라 문자열, 리스트, split() 결과도 다 바로 순회할 수 있다는 점이 훨씬 익숙해졌다.
3-2. 딕셔너리 컴프리헨션도 생각보다 자주 쓸 수 있었다
딕셔너리도 오늘 꽤 크게 남았다.
예전에는 딕셔너리를 만들 때
무조건 빈 딕셔너리 선언하고 for문 돌면서 넣는 방식만 먼저 떠올랐다.
그런데 오늘은 이런 구조도 계속 연습했다.
llist = [1, 2, 3, 4]
dicc = {i: i**2 for i in llist}
또 단어와 길이를 매핑하는 형태도 해봤다.
{i: len(i) for i in "apple banana kiwi".split()}
연습 자료에도 리스트를 key로, 길이나 제곱을 value로 두는 딕셔너리 예제와 딕셔너리 컴프리헨션 예제가 함께 정리돼 있었다.
이런 걸 해보니까
딕셔너리 컴프리헨션은 그냥 문법 암기 대상이 아니라,
key-value 관계가 분명한 데이터를 바로 정리하는 도구처럼 느껴졌다.
신고 결과 같은 문제에서도 결국 이런 감각이 중요할 것 같았다.
3-3. split()과 map()은 입력 처리에서 계속 나올 것 같다
사실 split()과 map()은 그동안
볼 때마다 알 것 같으면서도 손에 안 익는 느낌이 있었다.
오늘은 이걸 입력 처리랑 연결해서 연습하니까 훨씬 더 실전적으로 느껴졌다.
예를 들면 이런 형태다.
list(map(int, "1 2 3 4 5".split()))
그리고 여기서 끝나는 게 아니라
다시 컴프리헨션과 합쳐서 이런 식으로도 쓸 수 있었다.
[i**2 for i in list(map(int, "1 2 3 4 5".split()))]
연습 노트에도 "1 2 3 4 5"를 split()으로 나눈 뒤 map(int, ...)로 바꾸는 패턴과, 그 결과를 다시 컴프리헨션으로 가공하는 예제가 포함돼 있었다.
이런 걸 보면서 느낀 건,
입력 처리도 결국 패턴을 많이 보는 문제라는 거였다.
처음에는 낯설지만,
반복해서 보다 보면 점점 손이 빨라질 것 같다.
4. 오늘 제일 오래 걸린 문제와 제일 많이 배운 부분
오늘 스스로 연습한 것 중 가장 오래 걸렸던 건 이 문제였다.
# 입력 "a:1,2 b:3,4"
# 출력 {'a_1':1, 'a_2':2, 'b_3':3, 'b_4':4}
{ k+'_'+nnum : int(nnum)
for sep in ("a:1,2 b:3,4".split())
for k, num in [(sep.split(':'))]
for nnum in (num.split(','))
}
이 문제를 오래 붙잡고 나서 남은 건 단순히 “풀었다”가 아니었다.
오히려 아래 두 가지가 더 크게 남았다.
4-1. 컴프리헨션 안에서도 for문을 여러 번 쓸 수 있다
처음엔 이 구조가 너무 낯설었다.
그런데 결국 보면,
- 공백 기준으로 먼저 나누고
- : 기준으로 또 나누고
- , 기준으로 또 나누는 과정
이걸 한 줄 안에서 순서대로 쓴 구조였다.
즉, 문법이 어려운 게 아니라
문자열 분해 과정을 단계적으로 쓴 것이라고 생각하니까 조금 덜 어렵게 느껴졌다.
연습 파일 마지막에도 이 구조가 그대로 들어 있었고, 위쪽에는 for가 중첩되는 컴프리헨션 예제와 함께 “이중 포문처럼 생각하면 된다”는 메모도 남겨져 있었다.
4-2. unpacking이 조금 더 이해됐다
특히 이 부분이 오늘 제일 크게 남았다.
for k, num in [(sep.split(':'))]
처음엔 이게 너무 낯설었다.
그런데 결국 sep.split(':') 결과를
k, num 두 변수에 나눠 담는 구조라고 생각하니까 훨씬 이해가 됐다.
즉, 복잡한 문법이라기보다
쪼갠 결과를 바로 변수 두 개로 받는 방식이라는 감각이 조금 생긴 셈이다.
오늘은 이게 진짜 컸다.
설명만 들었을 때보다,
한 번 오래 걸려서 직접 풀어본 게 훨씬 많이 남았다.
5. 주차 요금 문제는 구현까지는 못 갔지만, 왜 어려운지는 보였다
🔗 문제 링크
오늘은 주차 요금 문제를 구현까지 하진 않았고,
설명을 따라가면서 구조를 이해하는 데 집중했다.
그런데 설명만 들어도
왜 이 문제가 까다로운지는 바로 느껴졌다.
이 문제는 알고리즘이 엄청 어려운 문제라기보다,
예외 처리와 케이스 분기가 많은 문제에 더 가까웠다.
예를 들면 이런 것들이다.
- 출차 기록이 없는 차량은 23:59 출차로 간주
- 기본 시간 이하 / 초과 여부 판단
- 초과 시간이 단위 시간으로 나눠떨어지는지 여부
- 마지막 출력은 차량 번호 기준 정렬
정리 텍스트에서도 주차 기록을 차량 번호 기준으로 다시 정리해야 하고, 출차 기록 누락을 예외 처리해야 하며, 마지막에는 차량 번호 기준으로 요금을 정렬해 출력해야 한다는 흐름이 길게 설명돼 있었다.
그래서 오늘은 구현보다도
“이 문제는 예외 케이스 정리를 먼저 하고 들어가야 한다”는 걸 확인한 정도로 남았다.
주말에 다시 직접 구현해보면서
오늘 이해한 구조를 실제 코드로 옮겨볼 생각이다.
6. 탐색 들어가기 전에 정리한 개념들
오늘은 DFS / BFS를 배우기 전에
그 바탕이 되는 개념들도 같이 정리했다.
6-1. while문
오늘 다시 느낀 건
for는 개수 중심, while은 조건 중심이라는 점이었다.
for는
“이걸 10번 반복해라”에 가깝고,
while은
“이 조건이 만족될 때까지 계속해라”에 가깝다.
탐색은 몇 번 반복될지 미리 정해져 있지 않은 경우가 많기 때문에,
결국 while이 더 자연스럽게 쓰인다.
정리 텍스트에서도 while문은 조건 중심으로 사용하고, 빠져나갈 조건을 항상 염두에 둬야 한다고 설명돼 있었다.
그래서 탐색 문제에서는
“몇 번 돌까?”보다
“언제 멈출까?”를 먼저 생각하는 게 더 중요하게 느껴졌다.
6-2. stack / queue
탐색을 이해하기 전에
스택과 큐도 같이 정리했다.
- Stack : 나중에 들어간 것이 먼저 나온다 (LIFO)
- Queue : 먼저 들어간 것이 먼저 나온다 (FIFO)
이걸 단순 정의로 외우는 것보다,
“탐색은 결국 방문할 노드를 어떤 순서로 처리할지 정하는 문제”라고 연결하니까 훨씬 잘 들어왔다.
정리 텍스트에는 stack과 queue를 여러 값을 어떻게 스케줄링해서 처리할지 정하는 기본 구조로 설명하고 있었고, 파이썬 리스트에서 append()와 pop()으로 이를 구현하는 흐름도 함께 정리돼 있었다.
6-3. 재귀 함수
재귀 함수도 다시 정리했다.
일반적인 분석 코드에서는 자주 안 써도,
코딩테스트에서는 구조적인 반복을 표현할 때 자주 나오고,
특히 DFS 같은 주제와 연결될 수 있다는 점이 오늘 다시 남았다.
팩토리얼 예제를 보면서 느낀 건,
호출은 먼저 시작되지만 해결은 가장 안쪽 호출부터 이뤄진다는 점이었다.
정리 텍스트에서도 재귀 함수는 자기 자신을 호출하는 구조이고, DFS처럼 구조적인 반복 문제에서 자주 연결된다고 설명돼 있었다.
이 흐름이 stack과 닮아 있어서
DFS를 재귀로 구현할 수 있다는 설명도 조금 더 납득됐다.
7. DFS: 왜 stack인지 이제 조금 이해됐다
오늘 DFS를 보면서 가장 기억에 남은 건
“깊게 들어갔다가 막히면 최근에 갔던 곳부터 다시 본다”는 흐름이었다.
오늘 정리한 그래프는 이런 식이었다.
graph = [
[],
[2, 3, 8],
[1, 7],
[1, 4, 5],
[3, 5],
[3, 4],
[7],
[2, 6, 8],
[1, 7]
]
그리고 DFS 기본 구조는 이렇게 잡았다.
- 방문할 곳 리스트
- 방문한 곳 리스트
- 시작점 추가
- 할 일이 없을 때까지 while
- 하나 꺼내기
- 처음 방문이면 기록하고 인접 노드 추가
코드는 이렇게 정리했다.
def dfs_m1(graph, start):
need_visit = []
visited = []
need_visit.append(start)
while need_visit:
node = need_visit.pop()
if node not in visited:
visited.append(node)
need_visit.extend(graph[node])
return visited
그리고 작은 번호부터 방문하고 싶으면
인접 노드를 역순으로 넣어야 한다는 것도 같이 정리했다.
def dfs_small_first(graph, start):
need_visit = []
visited = []
need_visit.append(start)
while need_visit:
node = need_visit.pop()
if node not in visited:
visited.append(node)
temp = list(reversed(graph[node]))
need_visit.extend(temp)
return visited
정리 자료에서도 DFS는 stack 기반으로 동작하고, 깊이 들어간 다음 막히면 최근에 쌓인 노드부터 다시 처리한다고 설명돼 있었다.
오늘 DFS에서 남은 핵심은 이것 같다.
💡 DFS는 “깊이 우선”이라는 정의보다,
막히면 최근 것부터 다시 꺼내는 stack 흐름으로 이해하는 게 훨씬 잘 들어온다.
8. BFS: 가까운 곳부터 확장하는 방식
BFS는 DFS와 반대로
가까운 노드부터 차근차근 확장해 나가는 방식으로 이해됐다.
그래서 BFS는 자연스럽게 queue와 연결된다.
내가 정리한 BFS 코드는 이쪽이었다.
def BFS(graph, start):
todo = []
did = []
todo.append(start)
while todo:
node = todo.pop(0)
if node not in did:
did.append(node)
todo.extend(graph[node])
return did
이 코드는 개념 이해용으로는 괜찮았지만,
pop(0)은 비효율적일 수 있기 때문에
나중에는 deque까지 같이 정리해서 다시 보는 게 좋겠다는 생각도 들었다.
정리 텍스트에서도 BFS는 queue 기반이고, 동일 가중치 상황에서는 최단거리 문제와 연결된다고 설명돼 있었다. 또 queue 구현에서는 deque를 자주 쓰게 된다는 흐름도 같이 나왔다.
오늘 BFS에서 남은 핵심은 이 다섯 줄로 정리된다.
- DFS는 깊게
- BFS는 가까운 곳부터
- DFS는 stack
- BFS는 queue
- BFS는 최단거리와 연결된다
9. 오늘 남은 고민
오늘 공부하면서 남은 고민도 꽤 분명했다.
9-1. 문제를 이해했다고 해서 구현까지 되는 건 아니다
이건 오늘 제일 크게 느낀 부분이다.
문제를 읽고 흐름을 이해하는 것과,
그걸 실제 코드로 쓰는 건 생각보다 다른 단계였다.
9-2. 문제를 많이 푸는 것보다, 약한 문법을 따로 연습하는 날도 필요하다
오늘은 이 선택이 잘 맞았다.
계속 문제만 붙잡고 있었으면
막히는 부분이 반복됐을 것 같은데,
부족한 문법을 따로 연습하니까 다시 연결되는 느낌이 있었다.
9-3. 탐색은 정의보다 흐름으로 이해하는 게 낫다
DFS / BFS도 이름만 외우면 계속 헷갈리는데,
stack / queue 흐름으로 연결하니까 훨씬 이해가 잘 됐다.
10. 오늘 느낀 점
오늘은 문제를 많이 푼 날은 아니었다.
그런데 오히려 그래서 더 의미가 있었다.
지금 내게 필요한 건 무조건 문제 수를 늘리는 게 아니라,
문제를 구현할 수 있게 해주는 기본기를 더 익히는 것이었다.
오늘 정리한 걸 다시 보면 결국 다 연결된다.
- 신고 결과 문제에서는 문제 설명을 끝까지 읽는 힘이 중요했고
- 파이썬 기본기 연습에서는 구현 문법을 익히는 게 중요했고
- DFS / BFS에서는 자료구조 흐름을 이해하는 게 중요했다
결국 코딩테스트는
- 문제를 이해하고
- 규칙을 세우고
- 그 규칙을 코드로 옮기는 과정
이 세 단계가 다 맞물려야 풀린다.
오늘은 그중에서도
특히 2번과 3번을 더 단단하게 만든 날이었다.
'[SK플래닛] ASAC 빅데이터전문가 11기 > 학습기록' 카테고리의 다른 글
| [SK플래닛] ASAC 빅데이터전문가 11기 | 09일차 (2) | 2026.04.23 |
|---|---|
| [SK플래닛] ASAC 빅데이터전문가 11기 | 08일차 (0) | 2026.04.22 |
| [SK플래닛] ASAC 빅데이터전문가 11기 | 06일차 (0) | 2026.04.20 |
| [SK플래닛] ASAC 빅데이터전문가 11기 | 05일차 (2) | 2026.04.17 |
| [SK플래닛] ASAC 빅데이터전문가 11기 | 04일차 (1) | 2026.04.16 |
