본문 바로가기
Develop/백준 (python)

[백준] 24060번: 알고리즘 수업 - 병합 정렬 1 (python)

by Tarra 2022. 3. 31.

24060번: 알고리즘 수업 - 병합 정렬 1


문제 )

오늘도 서준이는 병합 정렬 수업 조교를 하고 있다. 아빠가 수업한 내용을 학생들이 잘 이해했는지 문제를 통해서 확인해보자.

N개의 서로 다른 양의 정수가 저장된 배열 A가 있다. 병합 정렬로 배열 A를 오름차순 정렬할 경우 배열 A에 K 번째 저장되는 수를 구해서 우리 서준이를 도와주자.

크기가 N인 배열에 대한 병합 정렬 의사 코드는 다음과 같다.

 

merge_sort(A[p..r]) { # A[p..r]을 오름차순 정렬한다.
    if (p < r) then {
        q <- ⌊(p + r) / 2⌋;       # q는 p, r의 중간 지점
        merge_sort(A, p, q);      # 전반부 정렬
        merge_sort(A, q + 1, r);  # 후반부 정렬
        merge(A, p, q, r);        # 병합
    }
}

# A[p..q]와 A[q+1..r]을 병합하여 A[p..r]을 오름차순 정렬된 상태로 만든다.
# A[p..q]와 A[q+1..r]은 이미 오름차순으로 정렬되어 있다.
merge(A[], p, q, r) {
    i <- p; j <- q + 1; t <- 1;
    while (i ≤ q and j ≤ r) {
        if (A[i] ≤ A[j])
        then tmp[t++] <- A[i++]; # tmp[t] <- A[i]; t++; i++;
        else tmp[t++] <- A[j++]; # tmp[t] <- A[j]; t++; j++;
    }
    while (i ≤ q)  # 왼쪽 배열 부분이 남은 경우
        tmp[t++] <- A[i++];
    while (j ≤ r)  # 오른쪽 배열 부분이 남은 경우
        tmp[t++] <- A[j++];
    i <- p; t <- 1;
    while (i ≤ r)  # 결과를 A[p..r]에 저장
        A[i++] <- tmp[t++]; 
}

 

 

 

입력 :

첫째 줄에 배열 A의 크기 N(5 ≤ N ≤ 500,000), 저장 횟수 K(1 ≤ K ≤ 10^8)가 주어진다.
다음 줄에 서로 다른 배열 A의 원소 A1, A2, ..., AN이 주어진다. (1 ≤ Ai ≤ 10^9)

 

 

출력 :

배열 A에 K 번째 저장 되는 수를 출력한다. 저장 횟수가 K 보다 작으면 -1을 출력한다.

 

 

풀이)

병합 정렬을 배우고, 병합 정렬에 익숙해지고자 만만하게 덤빈 문제,

분명 정렬을 제대로 짠 것 같은데 이상하게 답이 틀려서 한참을 해멨다.

 

답이 틀린 이유는 이거였다.

예제 1번을 예시로 설명해보면 

처음에 내가 짠 코드는 4 5 // 1 3 2 의 방식으로 나누어 병합 정렬을 진행했지만,

문제에서는 4 5 1 // 3 2 와 같이 나누어 병합 정렬을 진행했기 때문에 답이 다르게 나온 것이었다.

이를 해결하기 위해 math 라이브러리를 불러 2로 나누었을 때 반올림을 해 해결하였다.

 

 

1. deque를 이용한 병합 정렬

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
from collections import deque
import math
 
# 하나씩 정렬 할때마다 카운트를 올려준다.
def check():
    global cnt , ans, a
    cnt += 1
    # 카운트가 k에 도달하였을 때 정답을 갱신해준다.
    if cnt == k:
        ans = a
 
def merge(left, right):
    global cnt, a
    
    # 정렬하여 넣어 줄 결과 리스트
    result = deque()
 
    # left와 right가 모두 빌때까지 반복한다.
    while len(left) or len(right):
        # 크기를 비교하여 result에 넣어주는 과정.
        # 수를 넣어줄 때마다 check를 해준다.
        if len(left) and len(right):
            if left[0<= right[0]:
                a = left[0]
                check()
                result.append(left.popleft())
            else:
                a = right[0]
                check()
                result.append(right.popleft())
        elif len(left) and not len(right):
            a = left[0]
            check()
            result.append(left.popleft())
        elif len(right) and not len(left):
            a = right[0]
            check()
            result.append(right.popleft())
    return result
 
 
# 병합 정렬을 위해 반으로 나누고, 이후 병합을 해준다.
def merge_sort(li):
 
    if len(li) == 1:
        return li
 
    mid = math.ceil(len(li)/2)
    left = deque()
    right = deque()
 
    for i in range(mid):
        left.append(li[i])
    for i in range(mid, len(li)):
        right.append(li[i])
 
    left = merge_sort(left)
    right = merge_sort(right)
 
    return merge(left, right)
 
 
n, k = map(int, input().split())
li = list(map(int, input().split()))
cnt = 0
ans = -1
merge_sort(li)
print(ans)
cs

 

 

2. 인덱스를 이용한 병합 정렬

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
import math
 
def check():
    global cnt, ans
    cnt += 1
 
    if cnt == k:
        ans = a
 
def merge_sort(li):
 
    if len(li) == 1:
        return li
 
    mid = math.ceil(len(li)/2)
 
    left = merge_sort(li[:mid])
    right = merge_sort(li[mid:])
 
    return merge(left, right)
 
def merge(left, right):
    global a
 
    result = []
    left_idx = 0
    right_idx = 0
 
    while left_idx < len(left) and right_idx < len(right):
        if left[left_idx] < right[right_idx]:
            a = left[left_idx]
            check()
            result.append(left[left_idx])
            left_idx += 1
        else:
            a = right[right_idx]
            check()
            result.append(right[right_idx])
            right_idx += 1
 
    if left_idx == len(left):
        while right_idx < len(right):
            a = right[right_idx]
            check()
            result.append(right[right_idx])
            right_idx += 1
 
    if right_idx == len(right):
        while left_idx < len(left):
            a = left[left_idx]
            check()
            result.append(left[left_idx])
            left_idx += 1
 
    return result
 
n, k = map(int, input().split())
li = list(map(int, input().split()))
ans = -1
cnt = 0
merge_sort(li)
print(ans)
 
cs

 

 

출처 : https://www.acmicpc.net/problem/24060

 

24060번: 알고리즘 수업 - 병합 정렬 1

첫째 줄에 배열 A의 크기 N(5 ≤ N ≤ 500,000), 저장 횟수 K(1 ≤ K ≤ 108)가 주어진다. 다음 줄에 서로 다른 배열 A의 원소 A1, A2, ..., AN이 주어진다. (1 ≤ Ai ≤ 109)

www.acmicpc.net