코딩 테스트 합격자 되기 | 배열 학습 기록
오늘 읽은 배열 파트를 바탕으로, PDF에서 주고받은 문제 풀이와 문법 질문을 한 글로 다시 정리했다. 한 문제 안에서는 내가 작성한 풀이와 다른 풀이를 함께 비교한다.
1. 모의고사: 반복 패턴을 배열로 만들기
내 풀이
def solution(answers):
patterns = [[1, 2, 3, 4, 5],
[2, 1, 2, 3, 2, 4, 2, 5],
[3, 3, 1, 1, 2, 2, 4, 4, 5, 5]]
scores = [0] * 3
for i, answer in enumerate(answers):
for j, pattern in enumerate(patterns):
if answer == pattern[i % len(pattern)]:
scores[j] += 1
max_score = max(scores)
result = []
for i, score in enumerate(scores):
if score == max_score:
result.append(i + 1)
return result
각 사람의 패턴을 2차원 리스트에 넣으면 사람 수가 늘어도 패턴만 추가하면 된다. i % len(pattern)은 패턴 끝에서 처음으로 돌아온다. enumerate가 꺼낸 answer를 쓰면 answers[i]를 다시 찾지 않아도 된다.
2. 행렬의 곱셈: 세 가지 풀이
arr1의 한 행과 arr2의 한 열을 잡고, 같은 위치의 값끼리 곱해 더한다. arr1이 A x B, arr2가 B x C이면 결과는 A x C다.
내 풀이: 결과 배열을 먼저 만들기
def solution(arr1, arr2):
r1, c1 = len(arr1), len(arr1[0])
r2, c2 = len(arr2), len(arr2[0])
result = [[0] * c2 for _ in range(r1)]
for i in range(r1):
for j in range(c2):
for k in range(c1):
result[i][j] += arr1[i][k] * arr2[k][j]
return result
i는 arr1의 행, j는 arr2의 열, k는 곱할 위치다. [[0] * c2 for _ in range(r1)]는 각 행을 독립적으로 만든다. [[0] * c2] * r1은 행을 공유할 수 있어 사용하지 않는다.
다른 풀이: append()로 한 행씩 완성하기
def solution(arr1, arr2):
answer = []
for i in range(len(arr1)):
row = []
for j in range(len(arr2[0])):
total = 0
for k in range(len(arr1[0])):
total += arr1[i][k] * arr2[k][j]
row.append(total)
answer.append(row)
return answer
answer는 전체 결과, row는 결과 한 행, total은 결과 한 칸의 계산기다. 예전 코드의 productMatrix(A, B)도 같은 로직이지만, 현재 제출은 문제에서 준 solution 함수명을 쓴다.
짧은 풀이와 풀어 쓴 버전
def solution(arr1, arr2):
return [[sum(a * b for a, b in zip(row, col))
for col in zip(*arr2)]
for row in arr1]
def solution(arr1, arr2):
answer = []
for row in arr1:
new_row = []
for col in zip(*arr2):
temp_sum = 0
for a, b in zip(row, col):
temp_sum += a * b
new_row.append(temp_sum)
answer.append(new_row)
return answer
zip(*arr2)는 arr2의 열을 꺼낸다. zip(row, col)은 행과 열의 같은 위치 값을 짝짓고, sum()은 곱한 결과를 더한다. 짧은 코드는 바로 아래의 세 겹 반복문을 압축한 것이다.
3. 실패율: count()와 빈도수 배열 비교
처음 떠올리기 쉬운 방식
fail_count = stages.count(stage)
count()는 이해하기 쉽지만 스테이지마다 stages 전체를 다시 확인한다.
완성 풀이: 빈도수 배열
def solution(N, stages):
challenger = [0] * (N + 2)
for stage in stages:
challenger[stage] += 1
fails = {}
total = len(stages)
for stage in range(1, N + 1):
if total == 0:
fails[stage] = 0
else:
fails[stage] = challenger[stage] / total
total -= challenger[stage]
return sorted(
fails,
key=lambda stage: fails[stage],
reverse=True
)
N+1은 모든 스테이지를 통과한 사람의 위치다. 리스트 인덱스는 0부터 시작하므로 N+1을 안전하게 세려면 [0] * (N + 2)가 필요하다. 빈도수 배열은 참가자 목록을 한 번만 세므로 큰 입력에서 유리하다.
lambda를 정석 함수로 풀기
def get_fail_rate(stage):
return fails[stage]
result = sorted(fails, key=get_fail_rate, reverse=True)
lambda stage: fails[stage]는 위 함수와 같다. 초안에서는 else 들여쓰기, total을 다음 스테이지 전에 빼는 위치, 빈 answer가 아니라 result를 반환하는 점을 수정했다.
4. 방문 길이: set과 함수 분리
인라인 이동 풀이
def solution(dirs):
x, y = 0, 0
visited = set()
moves = {
"U": (0, 1),
"D": (0, -1),
"R": (1, 0),
"L": (-1, 0)
}
for direction in dirs:
dx, dy = moves[direction]
nx, ny = x + dx, y + dy
if -5 <= nx <= 5 and -5 <= ny <= 5:
visited.add((x, y, nx, ny))
visited.add((nx, ny, x, y))
x, y = nx, ny
return len(visited) // 2
기능을 나눈 풀이
def is_valid_move(nx, ny):
return -5 <= nx <= 5 and -5 <= ny <= 5
def update_location(x, y, direction):
moves = {
"U": (0, 1),
"D": (0, -1),
"L": (-1, 0),
"R": (1, 0)
}
dx, dy = moves[direction]
return x + dx, y + dy
def solution(dirs):
x, y = 0, 0
visited = set()
for direction in dirs:
nx, ny = update_location(x, y, direction)
if not is_valid_move(nx, ny):
continue
visited.add((x, y, nx, ny))
visited.add((nx, ny, x, y))
x, y = nx, ny
return len(visited) // 2
이 문제는 방문한 점이 아니라 지나간 선을 센다. A에서 B로 간 길과 B에서 A로 돌아온 길은 같으므로 양방향 경로를 set에 넣고 2로 나눈다. add와 좌표 갱신은 반드시 for문 안에 있어야 한다.
문제 밖 질문: add(), append(), 문자열, 좌표계
리스트에는 append(), 문자열에는 + 또는 +=, 집합에는 add(), 딕셔너리에는 dict[key] = value를 쓴다. add()는 문자열 메서드가 아니다.
좌표를 0부터 10으로 옮기는 방식도 가능하지만, 시작점은 (5, 5), 범위는 0부터 10으로 끝까지 일관되어야 한다. 이 글은 원문과 같은 시작점 (0, 0), 범위 -5부터 5를 사용했다. 결과는 실수가 되는 /가 아니라 정수 나눗셈 //로 반환한다.
오늘의 배열 체크
한 문제를 풀 때는 정답 하나만 적지 않고, 내 풀이와 다른 풀이를 같은 자리에서 비교한다. 특히 짧은 풀이를 쓸 때는 반드시 같은 동작의 for문 버전도 함께 확인해 원리를 놓치지 않는다.