카테고리 없음

240202 동계 모각코 5차

muk muk 2024. 2. 7. 11:07

병합 정렬 

정렬된 두 배열이 있다고 가정했을 때, 두 배열을 합쳐 하나의 정렬된 배열로 만드는 방법은 두 개의 배열 처음부터 시작하여 두개의 값 중 더 작은 값을 배열에 넣고, 다시 남은 값을 비교하는 방식으로 채워나가는 것이다. 두 배열의 원소를 N개라고 가정한다면, 각 원소는 단 한번만 이동을 하게 되므로 시간복잡도는 O(N) 이 된다. 

그러나 우리가 정렬해야되는 배열은 한개다. 따라서 배열을 두개로 쪼개준다. 언제까지 ? 배열의 원소개 1개가 될 때 까지 재귀적으로 쪼개준다. 쪼갠 후 정렬을 하고, 다시 합치므로 이러한 방식을 divide and conquer 이라고 부른다. 

병합정렬의 시간 복잡도는 O(nlogn) 이다. 쪼개는데에 logn, 배열을 이동하는 데에 n 만큼의 시간이 소요되기 때문이다. 

 

function merge_sort(arr[], low, high)
  if low < high
    set mid = (low + high) / 2
    merge_sort(arr, low, mid)
    merge_sort(arr, mid+1, high)
    merge(arr, low, mid, high)


set merged_arr = []

function merge(arr[], low, mid, high)
  set i = low, j = mid + 1

  set k = low
  while i <= mid && j <= high
    if arr[i] <= arr[j]
      merged_arr[k] = arr[i]
      k += 1; i += 1
    else
      merged_arr[k] = arr[j]
      k += 1; j += 1
  
  while i <= mid
    merged_arr[k] = arr[i]
    k += 1; i += 1

  while j <= high
    merged_arr[k] = arr[j]
    k += 1; j += 1
  
  for k = low ... k <= high
    arr[k] = merged_arr[k]
  
  return arr 

 

파이썬 코드는 다음과 같다. 먼저 원소의 개수가 2개 이상인 경우, 리스트를 반으로 갈라 좌,우 측에 대한 정렬을 재귀적으로 수행한다. 

 

function merge_sort(arr[], low, high)
  if low < high                  // 원소의 개수가 2개 이상인 경우에만 진행
    set mid = (low + high) / 2   // 가운데 원소의 위치
    merge_sort(arr, low, mid)    // 왼쪽 부분에 대해 병합정렬 진행
    merge_sort(arr, mid+1, high) // 우측 부분에 대해 병합정렬 진행
    merge(arr, low, mid, high)   // 정렬된 두 리스트를 하나로 병합

 

이제 정렬된 다음 두 구간 내의 리스트에 대해 합치는 작업을 진행하는데, 두 리스트 ( i, j ) 를 비교하여 더 작은 원소를 합쳐진 배열에 넣어준다. 

 

set merged_arr = []           // 병합 이후의 결과를 담아줍니다.

function merge(arr[], low, mid, high)
  set i = low, j = mid + 1      // 각 리스트 내의 원소 위치를 잡습니다.

  set k = low                   // 병합 시 원소를 담을 위치를 유지합니다.
  while i <= mid && j <= high   // 두 리스트 내의 원소가 아직 남아있다면
    if arr[i] <= arr[j]          // 첫 번째 리스트 내의 원소가 더 작다면
      merged_arr[k] = arr[i]    // 해당 원소를 옮겨줍니다. 
      k += 1; i += 1
    else
      merged_arr[k] = arr[j]    // 그렇지 않다면 두 번째 리스트 내의
      k += 1; j += 1            // 원소를 옮겨줍니다.
  
  while i <= mid                // 아직 첫 번째 리스트 내 원소가 남아있다면
    merged_arr[k] = arr[i]      // 남은 원소들을 전부 옮겨줍니다.
    k += 1; i += 1

  while j <= high               // 아직 두 번째 리스트 내 원소가 남아있다면
    merged_arr[k] = arr[j]      // 남은 원소들을 전부 옮겨줍니다.
    k += 1; j += 1
  
  for k = low ... k <= high     // 병합된 리스트를 다시
    arr[k] = merged_arr[k]      // 원본 리스트에 옮겨줍니다.
  
  return arr 

 

바로 직전학기 알고리즘 수업때 중요하게 다루어졌던 부분이다. 아직도 코드가 수월하게 이해되지는 않지만, 계속 보다보니 어느정도 알 것 같다. 

 

function merge_sort(arr[], low, high)
  if low < high
    set mid = (low + high) / 2
    merge_sort(arr, low, mid)
    merge_sort(arr, mid+1, high)
    merge(arr, low, mid, high)


set merged_arr = []

function merge(arr[], low, mid, high)
  set i = low, j = mid + 1

  set k = low
  while i <= mid && j <= high
    if arr[i] <= arr[j]
      merged_arr[k] = arr[i]
      k += 1; i += 1
    else
      merged_arr[k] = arr[j]
      k += 1; j += 1
  
  while i <= mid
    merged_arr[k] = arr[i]
    k += 1; i += 1

  while j <= high
    merged_arr[k] = arr[j]
    k += 1; j += 1
  
  for k = low ... k <= high
    arr[k] = merged_arr[k]
  
  return arr 

 

문제로 위의 코드에서 arr = [ 17, 21, 15, 16, 30, 20, 35 ] 일때, 함수가 7번 종류된 시점에서의 배열의 상태를 물어봤다. 

이때 진행과정은 다음과 같다. 

 

 

 

그 다음은 2,3 -> 2,2 -> 2,2 -> 3,3 -> 3,3 -> 2,3 ---> 0,3 

 

7번 종류되었을 때 arr는 0,3 구간이 정렬된 상태이므로 , 

15 16 17 21 30 20 35 가 정답이 된다. 쉽지 않은 문제였다.