Algorithm/06. 정렬

6-7. 병합 정렬(Merge Sort)

찹키리 2021. 5. 3. 17:34

정렬을 마친 배열의 병합

: 정렬을 마친 각 배열에서 선택한 요소의 값을 비교해 작은 값의 요소를 꺼내 새로운 배열에 넣는 작업을 반복하여 정렬

 

 

 

 

 

[그림]

 

  • 배열 a와 b를 앞에서부터 차례로 비교하여 작은 값을 꺼내 비에 넣음

 

 

 

[실습 코드]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
static void merge(int[] a, int na, int[] b, int nb, int[] c) {
        int pa = 0;
        int pb = 0;
        int pc = 0;
        
        while(pa < na && pb < nb)
            c[pc++= (a[pa] <= b[pb]) ? a[pa++] : b[pb++];
 
        while(pa < na)
            c[pc++= a[pa++];
        
        while(pb < nb)
            c[pc++= b[pb++];;
    }

 

  • 3개의 반복문을 늘어놓는 단순한 알고리즘으로 구현
  • 병합에 필요한 시간 복잡도는 O(n)

 

 

 

 

 

병합 정렬

: 정렬을 마친 배열의 병합을 응용하여 분할 정복법에 따라 정렬하는 알고리즘

 

 

 

 

 

[그림]

 

  • 먼저, 배열을 앞부분과 뒷부분으로 분할
  • 두 배열을 각각 정렬하고 병합
  • 앞뒤에 놓인 6개의 요소를 정렬할 때도 다시 병합 정렬을 적용

 

 

  • 병합 정렬 알고리즘
배열의 요소 개수가 2개 이상인 경우
1. 배열의 앞부분을 병합 정렬로 정렬
2. 배열의 뒷부분을 병합 정렬로 정렬
3. 배열의 앞부분과 뒷부분을 병합

 

 

 

 

 

[실습 코드]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
static int[] buff; //작업용 배열
    
static void __mergeSort(int[] a, int left, int right) {
    if(left < right) {
        int i;
        int center = (left + right) / 2;
        int p = 0;
        int j = 0;
        int k = left;
            
        __mergeSort(a, left, center);
        __mergeSort(a, center + 1, right);
        
        for(i = left; i <= center; i++) {
            buff[p++= a[i];
        }
            
        while(i <= right && j < p)
            a[k++= (buff[p] <= a[i]) ? buff[p++] : a[i++];
            
        while(j < p)
            a[k++= buff[j++];
    }
}

 

  • 배열 병합의 시간 복잡도는 O(n), 전체 시간 복잡도는 O(n log n)
  • 서로 떨어져 있는 요소를 교환하는 것이 아니므로 안정적인 정렬 방법

 

 

 

 

 

Array.sort로 퀵 정렬과 병합 정렬하기

  • 03장에서 이진 검색에 사용했던 binarySearch메서드는 java.util.Arrays클래스의 클래스 메서드로 제공
  • 이 클래스는 배열을 정렬하는 클래스 메서드 sort도 제공

 

 

 

기본 자료형 배열의 정렬(퀵 정렬)

[실습 코드]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import java.util.Arrays;
//Arrays.sort 메서드로 정렬(퀵 정렬)
 
System.out.println("요솟수: ");
int num = stdIn.nextInt();
int[] x = new int[num];
        
for(int i = 0; i < num; i++) {
    System.out.println("x[" + i + "]: ");
    x[i] = stdIn.nextInt();
    }
        
Arrays.sort(x);
        
System.out.println("오름차순 정렬");
for(int i = 0; i < num; i++)
    System.out.println("x[" + i + "] = " + x[i]);

 

  • sort 메서드가 사용하는 알고리즘은 퀵 정렬 알고리즘을 개선한 것으로 안정적이지 않음
  • 즉, 배열에 같은 값이 존재하는 경우 같은 값 사이의 순서가 뒤바뀔 수 있음

 

 

 

클래스 객체 배열의 정렬(병합 정렬)

  • 클래스 객체 배열을 정렬하는 메서드는 총 4개이며, 크게 두 종류로 나눌 수 있음
  • 이들 메서드의 알고리즘은 병합 정렬을 개선한 것으로 안정적인 결과 보장

 

 

 

A. 자연 정렬이 필요한 배열

: 요소의 대소 관계를 비교하여 정렬

 

static void sort(Object[] a)
static void sort(Object[] a, int fromIndex, int toIndex)

 

 

 

[실습 코드]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import java.util.GregorianCalendar;
import static java.util.GregorianCalendar.*;
import java.util.Arrays;
//달력의 배열을 정렬
 
GregorianCalendar[] x = {
    new GregorianCalendar(2017, NOVEMBER, 1),
    new GregorianCalendar(1963, OCTOBER, 18),
    new GregorianCalendar(1985, APRIL, 5),
};
        
Arrays.sort(x);
        
for(int i = 0; i < x.length; i++) {
    System.out.printf("%04d년 %02d월 %02d일\n"
        x[i].get(YEAR),
        x[i].get(MONTH) + 1,
        x[i].get(DATE));
}

 

 

 

 

 

B. 자연 정렬이 필요하지 않은 배열

: 요소의 대소 관계를 비교할 때 comparator c를 사용해 정렬

 

static <T> void sort(T[] a, Comparator<? super T> c)
static <T> void sort(T[] a, int fromIndex, int toIndex, Comparator<? super T> c)

 

 

 

'Algorithm > 06. 정렬' 카테고리의 다른 글

6-9. 도수 정렬(Counting Sort)  (0) 2021.06.25
6-8. 힙 정렬(Heap Sort)  (0) 2021.05.05
6-6. 퀵 정렬(Quick Sort)  (0) 2021.03.11
6-5. 셸 정렬(Shell Sort)  (0) 2021.02.28
6-4. 단순 삽입 정렬(Straight Insertion Sort)  (0) 2021.02.28