본문 바로가기
알고리즘

[알고리즘] 퀵 정렬 (Quick Sort)

by Hongwoo 2022. 7. 25.
반응형

목차

 

퀵 정렬이란?

퀵 정렬은 분할 정복 (Divide and Conquer)과 재귀 방식을 이용한 정렬 알고리즘이다. 분할 정복이란 문제를 작은 2개의 문제로 분리하고 각각을 해결한 다음, 결과를 모아서 원래의 문제를 해결하는 방식이다. 

 

퀵 정렬에서는 pivot이라는 기준을 하나 정하고, 왼쪽은 기준보다 작은 값, 그리고 더 큰 값은 기준 오른쪽으로 옮기면서 배열을 둘로 분할한 후 정렬한다. 보통 pivot은 맨 앞이나 맨 뒤, 혹은 중간에 위치한 값을 선택한다.

 

퀵 정렬 과정

  1. 배열 맨 앞에 있는 수를 pivot으로 설정한다. 그리고 배열의 길이가 n일 때, 배열의 시작점은 0이 되고 도착점은 n - 1이 된다.
  2. pivot 값을 기준으로 시작점에서부터 검사해서 큰 값을, 도착점에서부터 작은 값을 찾는다.
  3. 두 값을 바꾸고, 같은 과정을 수행한다
  4. 큰 값의 index와 작은 값의 index가 교차하는 경우, 작은 값과 pivot 값을 바꾼다. 이렇게 하면 pivot 값을 기준으로 왼쪽은 pivot보다 작은 값들, 오른쪽은 큰 값들로 분할된다
  5. 분할된 영역에서 맨 앞에 있는 수를 pivot으로 설정하고 같은 과정을 수행한다

 

이제 퀵 정렬 예시를 한번 살펴보겠다.

 

 

 

소스 코드

 

import java.io.*;
import java.util.*;
public class Main {
   static int number = 10;  //데이터의 개수
   static int[] arr = {1, 10, 5, 8, 7, 6, 4, 3, 2, 9};  //정렬할 배열

   public static void main(String[] args) throws IOException {
       quickSort(arr, 0, number - 1);
   }

   static void quickSort(int[] arr, int start, int end) {
       if (start >= end) {
           return;  //원소가 1개인 경우
       }

       int pivot = start;  //pivot값은 첫번째 원소
       int i = start + 1;  //시작점 - 왼쪽부터 큰 값을 찾을 때 시작하는 인덱스
       int j = end;  //도착점 - 오른쪽부터 작은 값을 찾을 때 시작하는 인덱스
       int temp;  //수를 바꿀 때 사용하는 임시 변수

       while (i <= j) { // 엇갈릴 때까지 반복
           while (arr[i] <= arr[pivot]) { //pivot보다 더 큰 값을 만날 때까지
               i++;
           }
           while (arr[j] >= arr[pivot] && j > start) { //pivot보다 더 작은 값을 만날 때까지
               j--;
           }
           if (i > j) { //현재 엇갈린 상태면 pivot값과 교체
               temp = arr[j];
               arr[j] = arr[pivot];
               arr[pivot] = temp;
           } else { //엇갈리지 않았다면 i와 j를 교체
               temp = arr[i];
               arr[i] = arr[j];
               arr[j] = temp;
           }
      }

       //분할된 왼쪽과 오른쪽 둘다 퀵 정렬 수행
       quickSort(arr, start, j - 1);
       quickSort(arr, j + 1, end);
   }
}

 

만약에 내림차순으로 정렬을 한다고 할 때는 다음만 바꿔주기만 하면 된다.

 

while (arr[i] >= arr[pivot]) { //pivot보다 더 큰 값을 만날 때까지
    i++;
}
while (arr[j] <= arr[pivot] && j > start) { //pivot보다 더 작은 값을 만날 때까지
    j--;
}

 

퀵 정렬 (내림차순)

 

import java.io.*;
import java.util.*;
public class Main {
    static int number = 10;  //데이터의 개수
    static int[] arr = {1, 10, 5, 8, 7, 6, 4, 3, 2, 9};  //정렬할 배열

    public static void main(String[] args) throws IOException {
        quickSort(arr, 0, number - 1);
    }

    static void quickSort(int[] arr, int start, int end) {
        if (start >= end) {
            return;  //원소가 1개인 경우
        }

        int pivot = start;  //pivot값은 첫번째 원소
        int i = start + 1;  //시작점 - 왼쪽부터 큰 값을 찾을 때 시작하는 인덱스
        int j = end;  //도착점 - 오른쪽부터 작은 값을 찾을 때 시작하는 인덱스
        int temp;  //수를 바꿀 때 임시 변수

        while (i <= j) { // 엇갈릴 때까지 반복
            while (arr[i] >= arr[pivot]) { //pivot보다 더 큰 값을 만날 때까지
                i++;
            }
            while (arr[j] <= arr[pivot] && j > start) { //pivot보다 더 작은 값을 만날 때까지
                j--;
            }
            if (i > j) { //현재 엇갈린 상태면 pivot값과 교체
                temp = arr[j];
                arr[j] = arr[pivot];
                arr[pivot] = temp;
            } else { //엇갈리지 않았다면 i와 j를 교체
                temp = arr[i];
                arr[i] = arr[j];
                arr[j] = temp;
            }
       }

        //분할된 왼쪽과 오른쪽 둘다 퀵 정렬 수행
        quickSort(arr, start, j - 1);
        quickSort(arr, j + 1, end);
    }
}

 

 

퀵 정렬 시간 복잡도

 

퀵 정렬의 성능은 어떻게 pivot 값을 선택하느냐에 따라 크게 달라질 수 있다. pivot 값을 기준으로 동일한 개수의 작은 값들과 큰 값들이 분할되면 O(NlogN)의 시간 복잡도를 가지게 된다.

 

하지만, pivot 값을 기준으로 분할했을 때 값들이 한 편으로 크게 치우치게 되면, 퀵 정렬의 성능은 저하되고, 한편으로만 모든 값이 몰리면 O(N^2)의 시간 복잡도를 가지게 된다. 추가로 이미 배열이 정렬되어 있고 pivot이 항상 그 배열의 최솟값 혹은 최댓값인 경우에는 최악의 시간 복잡도를 가진다. 이때, 분할이 N만큼 진행되다 보니 최악의 시간 복잡도는 O(N^2)이 된다.

 

 

이 그래프에서 볼 수 있듯이 자료의 개수 N이 많아질수록 O(N log N)의 시간 복잡도를 가지고 있는 퀵 정렬은 다른 정렬 알고리즘보다 훨씬 더 효율적이다.

 

 

 

퀵 정렬의 장단점

장점

  • O(NlogN)의 시간 복잡도를 가지므로 다른 정렬 알고리즘보다 빠르다
  • 정렬하고자 하는 배열 안에서 교환하는 방식(in-place)이므로, 다른 메모리 공간을 필요로 하지 않는다

 

단점

  • 불안정 정렬(Unstable Sort)이다
  • 정렬된 배열에 대해서는 Quick sort의 불균형 분할에 의해 오히려 수행 시간이 더 많이 걸린다 

 

 

반응형

댓글