[묘공단] 코딩테스트 스터디 10주차
이 글은 골든래빗 코딩 테스트 합격자 되기 파이썬 편의 12장 문제풀이입니다
정렬 개념
정렬이란 사용자가 정의한 순서로 데이터를 나열하는 것
- 오름차순
- 내림차순
- 임의의 조건
정렬이 된 데이터에서는 원하는 데이터를 쉽게 찾을 수 있기 때문에 효율적
병합 정렬
전형적인 분할-정복 방식의 정렬 알고리즘
매 과정에서 반씩 분할 후, 분할된 데이터의 크기가 1일 때부터 정렬된 순서로 합병하는 방식
힙 정렬
힙의 특징을 이용해서 최솟값 또는 최댓값을 반복적으로 pop하면 결국 정렬된 데이터를 얻을 수 있게됨
파이썬에서는 heapq의 여러가지 메서드를 활용해서 우선순위 큐 연산들을 지원할 수 있다
위상 정렬
방향이 있고 cycle이 없는 graph (DAG)에서 태스크의 순서를 정렬하는 알고리즘
각 노드로 들어오는 input stream의 차수를 기준으로 초기화를 하고, 큐를 이용해서 집어넣은 후
차수가 0인 노드를 팝하면서 downstream으로 연결된 노드의 차수를 -1씩 감소하면서 반복
계수 정렬
카운팅 정렬이라고도 불리며, 몇 가지 한계 케이스 (음수 또는 sparse 배열, 너무 큰 range)가 아닌 경우
효율적으로 정렬이 가능함 (비교 정렬과 다르게 선형시간에 정렬 가능)
몸풀기 문제
계수 정렬 구현하기
문제 조건에서 알파벳 소문자라는 조건이 있으므로, 작은 범위만 볼 수 있으므로 계수 정렬을 적용할 수 있는 좋은 예제
Python의 ord나 chr 함수를 알아야 쉽게 풀이 가능
def solution(s: str) -> str:
counts = [0] * 26
for char in s:
counts[ord(char) - ord("a")] += 1
answer = []
for index, count in enumerate(counts):
for _ in range(count):
answer.append(chr(index + ord("a")))
return "".join(answer)
assert solution("hello") == "ehllo"
assert solution("algorithm") == "aghilmort"
정렬이 완료된 두 배열 합치기
교재 풀이와 거의 유사함, 다만 arr1 또는 arr2 중 하나가 끝났다면, 간단하게 extend를 활용해서 붙이는 부분만 조금 차이
from typing import Sequence, List
def solution(arr1: Sequence[int], arr2: Sequence[int]) -> List[int]:
answer = []
ptr1 = 0
ptr2 = 0
while ptr1 < len(arr1) and ptr2 < len(arr2):
if arr1[ptr1] <= arr2[ptr2]:
answer.append(arr1[ptr1])
ptr1 += 1
else:
answer.append(arr2[ptr2])
ptr2 += 1
if ptr1 == len(arr1):
answer.extend(arr2[ptr2:])
else:
answer.extend(arr1[ptr1:])
return answer
assert solution([1, 3, 5], [2, 4, 6]) == [1, 2, 3, 4, 5, 6]
assert solution([1, 2, 3], [4, 5, 6]) == [1, 2, 3, 4, 5, 6]
실전 문제
문자열 내 마음대로 정렬하기
문제 조건대로 정렬 비교 함수를 구현하면 쉽게 풀 수 있는 문제
인덱스 n의 문자가 같은 경우 사전순으로 앞선 문자열이 앞쪽에 위치한다는 부분을 놓치지 말 것
from typing import Sequence, List
def solution(strings: Sequence[str], n: int) -> List[str]:
return sorted(strings, key=lambda s: (s[n], s))
assert solution(["sun", "bed", "car"], 1) == ["car", "bed", "sun"]
assert solution(["abce", "abcd", "cdx"], 2) == ["abcd", "abce", "cdx"]
정수 내림차순으로 배치하기
우선 입력으로 들어온 n을 string으로 변환 후 내림차순으로 정렬
정렬된 결과를 join으로 묶어서 string으로 만든 후 최종적으로는 int로 변환해서 반환
def solution(n: int) -> int:
return int("".join(sorted(str(n), reverse=True)))
assert solution(118372) == 873211
K번째수
slicing 연산으로 배열을 잘라내고, 정렬 후 인덱스 결과만 저장하면 쉽게 풀이 가능한 문제
인덱스 시작이 0이 아니라 1이므로 off-by-one error만 주의
from typing import Sequence, List
def solution(array: Sequence[int], commands: Sequence[Sequence[int]]) -> List[int]:
answer = []
for i, j, k in commands:
sorted_sub_array = sorted(array[i - 1:j])
answer.append(sorted_sub_array[k - 1])
return answer
가장 큰 수
파이썬에서 제공하는 cmp_to_key 를 활용해서 문제 조건에 맞게 사용자 정의 정렬을 하는 연습 문제
a가 b보다 선이라면 음수 값을, 동등하다면 0을, a가 b보다 후라면 양수 값을 반환하는 식으로 cmp 함수를 작성하면 된다
reference: https://docs.python.org/ko/3/library/functools.html#functools.cmp_to_key
A comparison function is any callable that accepts two arguments, compares them,
and returns a negative number for less-than, zero for equality, or a positive number for greater-than.
A key function is a callable that accepts one argument and returns another value to be used as the sort key.
from functools import cmp_to_key
from typing import Sequence
def solution(numbers: Sequence[int]) -> str:
def _compare(a: int, b: int) -> int:
c1 = f"{a}{b}"
c2 = f"{b}{a}"
return int(c1) - int(c2)
sorted_numbers = sorted(numbers, key=cmp_to_key(_compare), reverse=True)
answer = "".join(str(x) for x in sorted_numbers)
return "0" if int(answer) == 0 else answer
assert solution([6, 10, 2]) == "6210"
assert solution([3, 30, 34, 5, 9]) == "9534330"
assert solution([0, 0, 0]) == "0"
튜플
문자열을 스택 자료구조를 이용해서 파싱하는 식으로 진행했는데 조금 더 간결하게 작성가능할 것 같다
from collections import deque
from typing import List
def solution(s: str) -> List[int]:
stack = []
candidates = []
temp_container = []
for token in s[1:-1]:
if token == "{":
stack.append(token)
elif token == "}":
stack.append("".join(temp_container))
temp_container.clear()
queue = deque()
while (element := stack.pop()) != "{":
queue.appendleft(int(element))
candidates.append(set(queue))
elif token == ",":
stack.append("".join(temp_container))
temp_container.clear()
elif token.isdigit():
temp_container.append(token)
else:
continue
candidates.sort(key=lambda c: len(c))
previous_set = candidates[0]
answer = [min(previous_set)]
for candidate in candidates[1:]:
answer.append(min(candidate.difference(previous_set)))
previous_set = candidate
return answer
assert solution("{{2},{2,1},{2,1,3},{2,1,3,4}}") == [2, 1, 3, 4]
assert solution("{{1,2,3},{2,1},{1,2,4,3},{2}}") == [2, 1, 3, 4]
assert solution("{{20,111},{111}}") == [111, 20]
assert solution("{{123}}") == [123]
assert solution("{{4,2,3},{3},{2,3,4,1},{2,3}}") == [3, 2, 4, 1]
문자열 처리를 이용해서 좀 더 간결하게 풀이한 코드
from typing import List
def solution(s: str) -> List[int]:
prepared_str = s.lstrip("{").rstrip("}")
tuple_set = [set(term.split(",")) for term in prepared_str.split("},{")]
tuple_set.sort(key=lambda _tuple: len(_tuple))
answer = [min(tuple_set[0])]
prev = tuple_set[0]
for x in tuple_set[1:]:
answer.append(min(x.difference(prev)))
prev = x
return [int(x) for x in answer]
지형 이동
최소 비용, 그리고 인접한 노드끼리 이동 가능하다는 점에서 BFS를 떠올리고,
이동 비용이 작은 순으로 방문 처리를 하므로 우선순위 큐를 착안할 수 있어야한다
원점부터 시작하여 상, 하, 좌, 우 인접한 칸의 cost와 좌표를 우선순위 큐에 넣고
우선순위 큐에서 cost가 작은 순으로 뽑아 내고 또 상, 하, 좌, 우 탐색
방문처리 하는 시점은 일반적인 BFS와 좀 다르므로 유의할 것
import heapq
from typing import Sequence
def solution(land: Sequence[Sequence[int]], height: int) -> int:
dimension = len(land)
visited = [[False] * dimension for _ in range(dimension)]
start = (0, 0, 0) # cost, row, col
queue = [start]
heapq.heapify(queue)
answer = 0
while queue:
cost, row, col = heapq.heappop(queue)
if visited[row][col]:
continue
answer += cost
visited[row][col] = True
for drow, dcol in [(0, 1), (0, -1), (1, 0), (-1, 0)]:
new_row, new_col = row + drow, col + dcol
if (
0 <= new_row < dimension
and 0 <= new_col < dimension
and not visited[new_row][new_col]
):
if (diff := abs(land[row][col] - land[new_row][new_col])) <= height:
heapq.heappush(queue, (0, new_row, new_col))
else:
heapq.heappush(queue, (diff, new_row, new_col))
return answer
assert (
solution([[1, 4, 8, 10], [5, 5, 5, 5], [10, 10, 10, 10], [10, 10, 10, 20]], 3) == 15
)
assert (
solution([[10, 11, 10, 11], [2, 21, 20, 10], [1, 20, 21, 11], [2, 1, 2, 1]], 1)
== 18
)
추가 문제
파일명 정렬
문제 요구사항대로 구현, TAIL이 빈 문자일 수 있으므로 해당 부분을 주의해야 함
from typing import Sequence, List
def solution(files: Sequence[str]) -> List[str]:
custom_files = []
for original_order, file_name in enumerate(files):
head = []
index = 0
while not file_name[index].isdigit():
head.append(file_name[index])
index += 1
head = "".join(head)
number = []
while index < len(file_name) and file_name[index].isdigit():
number.append(file_name[index])
index += 1
number = "".join(number)
tail = file_name[index:]
custom_files.append((head, number, tail, original_order))
custom_files.sort(key=lambda file: (file[0].lower(), int(file[1]), file[3]))
return [f"{head}{number}{tail}" for head, number, tail, _ in custom_files]
H-Index
from collections import Counter
from typing import Sequence
def solution(citations: Sequence[int]) -> int:
citation_counter = Counter(citations)
sorted_citations = sorted(citations, reverse=True)
h_index = {}
accumulative_sum = 0
answer = 0
for citation in sorted_citations:
if citation in h_index:
continue
accumulative_sum += citation_counter[citation]
h_index[citation] = accumulative_sum
if citation >= accumulative_sum:
answer = max(answer, accumulative_sum)
return answer
assert solution([3, 0, 6, 1, 5]) == 3
assert solution([0, 0, 0, 0, 0]) == 0
assert solution([0, 0, 0, 0, 1]) == 1
assert solution([9, 9, 9, 12]) == 4
댓글
이 글 공유하기
다른 글
-
[묘공단] 코딩테스트 스터디 12주차
[묘공단] 코딩테스트 스터디 12주차
2024.02.24 -
[묘공단] 코딩테스트 스터디 11주차
[묘공단] 코딩테스트 스터디 11주차
2024.02.07 -
[묘공단] 코딩테스트 스터디 9주차
[묘공단] 코딩테스트 스터디 9주차
2024.01.27 -
[묘공단] 코딩테스트 스터디 8주차
[묘공단] 코딩테스트 스터디 8주차
2024.01.19