3 minute read

  1. 분할정복 알고리즘이란?

    사전적 의미

  2. 병합정렬
    2.1 정의
    2.2 코드

  3. 퀵정렬
    3.1 정의
    3.2 코드

  4. 이진탐색
    4.1 정의
    4.2 코드

  5. 알고리즘 비교

1. 분할정복 알고리즘(Divide and conquer algorithm)

  • 분할정복 알고리즘 (Divide and conquer algorithm)은 그대로 해결할 수 없는 문제를 작은 문제로 분할하여 문제를 해결하는 방법이다.

  • 예시로는 병합정렬, 퀵정렬, 이진탐색이 있으며 후술할 예정이다.



2. 합병정렬(Merge sort)

  • 합병정렬이란

하나의 리스트를 두 개의 균등한 크기로 분할하고 분할된 부분 리스트를 정렬한 다음, 두 개의 정렬된 부분 리스트를 합하여 전체가 정렬된 리스트가 되게 하는 방법이다.



  • 동작과정

  • 분할정복 알고리즘의 단계는 크게 Devide(나누기), Conquer(재귀적으로 문제 해결), Combine(통합) 의 단계로 이루어진다.

1) Divide

  → 입력 배열을 같은 크기의 2개의 부분 배열로 분할한다.

2) Conquer

  → 부분 배열을 정렬한다. 부분 배열의 크기가 충분히 작지 않으면 순환 호출을 이용하여 다시 분할 정복 방법을 적용한다.

3) Combine

  → 정렬된 부분 배열들을 하나의 배열에 합병한다.
  • 예시코드
     public class DivideAndConquer {
      private boolean isSomething(int start, int end) {
          return start >= end;
      }
    
      public int[] mergeSort(int[] arr, int start, int end) {
          if (isSomething(start, end)) {
              return null;
          }
    
          int mid = (start + end) / 2;
    
          mergeSort(arr, 0, mid);
          mergeSort(arr, mid+1, end);
    
          merge(arr, start, mid, end);
    
          return null;
      }
    
      private int[] merge(int[] arr, int start, int mid, int end) {
          int p = start;
          int q = mid + 1;
          int idx = p;
    
          int[] tmp = new int[arr.length];
    
          while(p <= mid || q <= end) {
              if (q > end || (p <= mid && arr[p] < arr[q])) {
                  tmp[idx++] = arr[p++];
              } else {
                  tmp[idx++] = arr[q++];
              }
          }
    
          for (int i=start; i<=end; i++) {
              arr[i] = tmp[i];
          }
    
          return null;
      }
    
      public static void main(String[] args) {
          int arr[] = {7, 6, 5, 4, 3, 2, 1, 0};
          for (int i=0; i<arr.length; i++) {
              System.out.printf("%d ", arr[i]);
          }
          System.out.println();
    
          DivideAndConquer dac = new DivideAndConquer();
          dac.mergeSort(arr, 0, arr.length-1);
    
          for (int i=0; i<arr.length; i++) {
              System.out.printf("%d ", arr[i]);
          }
          System.out.println();
      }
    } 
    

3.퀵 정렬(Quick sort)

  • 퀵 정렬이란

특정 원소 피봇(pivot)을 기준으로 주어진 배열을 두 부분 배열로 분할하고 각 부분 배열에 대해 퀵 정렬을 순환적으로 적용하는 방식이다.



  • 동작과정

1) Divide

  → 피봇 하나를 선택하여 피봇을 기준으로 2개의 부분 배열로 분할한다.

2) Conquer

  → 피봇을 기준으로 피봇보다 큰 값, 혹은 작은 값을 찾는다. 왼쪽에서 부터 피봇보다 큰 값을 찾고 오른쪽에서 부터는 피봇보다 작은 값을 찾아서 두 원소를 교환한다. 분할된 부분 배열의 크기가 0이나 1일 될때까지 반복한다.


  • 예시 코드
public class Main {
	
	static int[] arr;
	public static void main(String[] args) throws IOException{
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
		StringBuilder sb = new StringBuilder();
		int n = Integer.parseInt(br.readLine());
		arr = new int[n];
		for(int i=0; i<n; i++) {
			arr[i] = Integer.parseInt(br.readLine());
		}
		quickSort(0,n-1);
		
		for(int num : arr) {
			sb.append(num+"\n");
		}
		System.out.println(sb.toString());
		
	}
	
	static void quickSort(int left, int right) {
		if(left>= right) {
			return;
		}
		
		int pivot = partition(left, right);
		quickSort(left,pivot-1);
		quickSort(pivot+1,right);
	}
	
	static int partition(int left, int right) {
		int pivot = arr[left];
		int i = left;
		int j = right;
		while(i < j) {
			while(arr[j] > pivot&& i<j) {
				j--;
			}
			while(arr[i] <= pivot && i<j) {
				i++;
			}
			swap(i,j);
		}
		swap(left,i);
		return i;
	}
	
	static void swap(int i, int j) {
		int tmp = arr[i];
		arr[i] = arr[j];
		arr[j] = tmp;
	}

}
  • 단점

    퀵 정렬의 경우 피봇의 위치나 배열의 상태에 따라 최악의 경우 O($n^2$)이 나올 수도 있다.

4. 이진탐색(Binary search)

  • 이진탐색이란

정렬된 데이터를 효과적으로 탐색할 수 있는 방법이다.



  • 동작과정
    1) Divide

    → 배열의 가운데 원소를 기준으로 왼쪽, 오른쪽 부분배열로 분할한다. 탐색키와 가운데 원소가 같으면 분할을 종료한다.
    

2) Conquer

  → 탐색키가 가운데 원소보다 작으면 왼쪽 부분배열을 대상으로 이진 탐색을 순환 호출하고, 크면 오른쪽 부분배열을 대상으로 이진 탐색을 호출한다.  
  <br>
  • 예시코드
    static int binarySearch(int[] arr, int left, int right, int key) {
      int mid =0;
    	
      while(left<= right) {
          mid = (left+right)/2;
            
          if(key == arr[mid]) return mid;
            
          if(arr[mid] < key) {
              left = mid+1;
          }else {
              right = mid;
          }
      }
      return -1; // 찾지못했을 경우
    }
    

5. 알고리즘과 비교

정렬 알고리즘 최대 실행시간 최소 실행시간 평균 실행시간
병합 정렬 O(log n) O(log n) O(log n)
퀵 정렬 O(n^2) O(log n) O(log n)

Updated:

Leave a comment