파이썬 딕셔너리와 코딩 테스트 활용
파이썬 딕셔너리의 기본 사용법과 빠른 조회 특성을 코딩 테스트 문제에 활용하는 방법을 정리한다.
딕셔너리(Dictionary)
딕셔너리는 키(key)로 값(value)을 빠르게 찾기 위한 자료구조다. 파이썬에서는 중괄호 {}로 만들며, 일반적으로 조회, 추가, 수정이 평균 O(1)에 가능하다.
rank = {
"mumu": 0,
"soe": 1,
"poe": 2,
}
print(rank["soe"]) # 1
rank["soe"] = 0 # 값 수정
코딩 테스트에서 딕셔너리는 “이 값이 몇 번째지?”, “이 이름의 정보는 무엇이지?”처럼 값을 기준으로 빠르게 위치나 정보를 찾아야 할 때 특히 유용하다.
예제: 달리기 경주에서 추월 처리하기
선수들의 현재 순위가 players에 들어 있고, 해설진이 추월한 선수의 이름을 callings로 부른다. 이름이 불린 선수는 자기 바로 앞 선수를 추월한다.
players = ["mumu", "soe", "poe", "kai", "mine"]
callings = ["kai", "kai", "mine", "mine"]
첫 번째 calling이 "kai"라면 현재 순위는 다음과 같다.
[mumu, soe, poe, kai, mine]
↑
kai
kai는 바로 앞의 poe를 추월한다.
[mumu, soe, kai, poe, mine]
처음 떠올리기 쉬운 방법: 리스트의 index
이름으로 현재 등수를 찾기 위해 다음처럼 작성할 수 있다.
position = players.index("kai")
하지만 list.index는 앞에서부터 하나씩 비교하므로 O(N)이 걸린다. callings가 M개라면 최악의 경우 O(N x M)이 되어, 입력이 크면 시간 초과가 날 수 있다.
선수의 순위는 자주 바뀌지만, “이름 -> 현재 인덱스”를 딕셔너리로 저장하면 해당 선수를 평균 O(1)에 찾을 수 있다.
풀이: 이름 -> 현재 등수를 딕셔너리로 저장하기
def solution(players, callings):
# 선수 이름: 현재 인덱스
rank = {player: i for i, player in enumerate(players)}
for player in callings:
current_rank = rank[player] # 호출된 선수의 현재 등수
front_player = players[current_rank - 1] # 바로 앞 선수의 이름
# players 리스트에서 두 선수의 위치를 교환
players[current_rank - 1], players[current_rank] = (
players[current_rank],
players[current_rank - 1],
)
# 리스트가 바뀌었으므로 딕셔너리의 등수도 함께 갱신
rank[player] = current_rank - 1
rank[front_player] = current_rank
return players
print(solution(
["mumu", "soe", "poe", "kai", "mine"],
["kai", "kai", "mine", "mine"],
))
# ['mumu', 'kai', 'mine', 'soe', 'poe']
동작 과정
| 호출된 선수 | 추월 전 | 교환한 선수 | 추월 후 |
|---|---|---|---|
| kai | mumu, soe, poe, kai, mine | poe | mumu, soe, kai, poe, mine |
| kai | mumu, soe, kai, poe, mine | soe | mumu, kai, soe, poe, mine |
| mine | mumu, kai, soe, poe, mine | poe | mumu, kai, soe, mine, poe |
| mine | mumu, kai, soe, mine, poe | soe | mumu, kai, mine, soe, poe |
이 풀이에서 중요한 점은 리스트와 딕셔너리를 둘 다 갱신한다는 것이다.
- players: “현재 i등 선수는 누구인가?”를 찾는다.
- rank: “이 선수는 현재 몇 등인가?”를 찾는다.
한쪽만 바꾸면 두 자료구조의 정보가 달라져 다음 호출에서 잘못된 선수를 교환하게 된다.
시간, 메모리 복잡도
- 초기 rank 생성: O(N)
- 각 추월 처리: 딕셔너리 조회, 리스트 두 칸 교환, 딕셔너리 갱신 모두 평균 O(1)
- 전체 시간: O(N + M)
- 추가 메모리: rank 딕셔너리 O(N)
N은 선수 수, M은 호출 횟수다.
이 풀이에서 사용한 딕셔너리 문법
1. 딕셔너리 만들기
rank = {"mumu": 0, "soe": 1}
2. 키로 값 조회하기
current_rank = rank["mumu"]
키가 없을 가능성이 있다면 get을 쓴다. 키가 없을 때 오류 대신 기본값을 돌려준다.
current_rank = rank.get("mumu", -1)
3. 값 추가 또는 수정하기
없는 키라면 추가하고, 이미 있는 키라면 값을 수정한다.
rank["kai"] = 3
rank["kai"] = 2
4. 리스트를 순회하며 인덱스도 함께 얻기
enumerate는 순서가 있는 자료를 돌면서 인덱스와 값을 함께 준다.
players = ["mumu", "soe", "poe"]
for i, player in enumerate(players):
print(i, player)
# 0 mumu
# 1 soe
# 2 poe
이를 한 줄로 쓴 것이 딕셔너리 컴프리헨션이다.
rank = {player: i for i, player in enumerate(players)}
# {"mumu": 0, "soe": 1, "poe": 2}
5. 두 값 교환하기
파이썬에서는 임시 변수를 만들지 않고 두 값을 바꿀 수 있다.
a, b = b, a
players[current_rank - 1], players[current_rank] = (
players[current_rank],
players[current_rank - 1],
)
딕셔너리는 언제 사용할까?
다음 단서가 보이면 딕셔너리를 먼저 떠올려 볼 수 있다.
| 문제의 단서 | 딕셔너리 활용 방식 | 예시 |
|---|---|---|
| 이름, 번호, ID로 정보를 찾음 | 이름/ID -> 정보 저장 | 회원 ID -> 점수 |
| 특정 값의 위치를 자주 찾음 | 값 -> 인덱스 저장 | 선수 이름 -> 현재 등수 |
| 등장 횟수를 세야 함 | 값 -> 개수 저장 | 단어 빈도, 의상 종류 |
| 중복 여부를 빠르게 확인 | 키 존재 여부 확인 | 이미 방문한 정점 |
| 두 데이터를 대응시켜야 함 | 한 값 -> 관련 값 저장 | 알파벳 -> 숫자, 도시 -> 거리 |
| 같은 분류끼리 모아야 함 | 키 -> 리스트 저장 | 과목 -> 수강생 목록 |
대표 패턴
빈도 세기:
count = {}
for number in numbers:
count[number] = count.get(number, 0) + 1
존재 여부 확인:
if name in rank:
print("등록된 선수입니다.")
키와 값을 함께 순회하기:
for player, position in rank.items():
print(player, position)
딕셔너리의 키는 변하지 않는 값이어야 한다. 문자열, 정수, 튜플은 키가 될 수 있지만 리스트나 딕셔너리는 키가 될 수 없다.
접근 방법은 알겠는데 문법이 막힐 때
“이름 -> 순위로 빠르게 찾아야 하니 딕셔너리가 필요하다”까지 생각했다면 알고리즘 방향은 이미 맞았다. 이때는 문제 전체를 멈추기보다 필요한 문법을 작은 단위로 확인하면 된다.
- 필요한 데이터 관계를 한국어로 쓴다: “선수 이름을 넣으면 현재 인덱스가 나와야 한다.”
- 자료구조를 한 줄로 만든다: rank = {player: i for i, player in enumerate(players)}
- 작은 예제로 조회, 수정만 따로 해 본다.
- 그 뒤 반복문 안에 넣는다.
players = ["a", "b", "c"]
rank = {player: i for i, player in enumerate(players)}
print(rank["b"]) # 1
rank["b"] = 0
print(rank) # {'a': 0, 'b': 0, 'c': 2}
코딩 테스트에서는 자주 쓰는 아래 세 문법만 먼저 익혀도 많은 딕셔너리 문제를 풀 수 있다.
value = dictionary[key] # 조회
dictionary[key] = value # 추가 또는 수정
dictionary[key] = dictionary.get(key, 0) + 1 # 기본값을 이용한 누적
COMMENTS
GitHub 계정으로 로그인하여 댓글을 남길 수 있습니다. 댓글은 GitHub Discussions에 공개 저장되며, 작성 내용과 GitHub 프로필 정보가 다른 방문자에게 보일 수 있습니다.