Java/java 알고리즘

퀵 정렬

쭈녁 2024. 4. 6. 01:07

 

Java의 Arrays.sort는 듀얼피봇 퀵정렬(Dual-Pivot QuickSort)을 사용한다.

듀얼 피봇 퀵정렬은 피봇을 2개를 두고 3개의 구간을 만들어 퀵정렬을 진행한다는 것이다.

이 방식은 하나의 피봇으로 진행하는 퀵 정렬에 비해 좋은 성능을 낸다고 알려진다.

 

그 이유는 퀵 정렬의 경우 최악의 경우의 피봇을 잡았을 때 (오름차순에서 가장 작은 수 or 내림차순에서 가장 높은 수) 버블정렬과 같은 시간 복잡도가 나온다.

때문에 평균은 O(nlog₂n)로 준수한 시간 복잡도를 갖지만 최악의 경우 O(n²)가 나온다. 때문에 두개의 피봇을 두고 퀵 정렬을 한다.

 

그렇기에 그 기본이 되는 퀵 정렬에 대해 공부해 보았다.

 

퀵정렬의 정렬 방식

  • 피벗을 기준으로 피벗보다 작은 요소들은 피벗의 왼쪽에 위치시키고 피벗보다 큰 요소는 오른쪽에 위치시킨다.
  • 피벗을 제외한 왼쪽 리스트와 오른쪽 리스트 각각을 다시 정렬한다.
    • 분할된 서브 리스트에 대해서는 순환 호출을 이용하여 정렬을 반복한다.
    • 서브 리스트에서도 재차 피벗을 정하고 이를 기준으로 2개의 서브 리스트로 다시 나누어 주는 과정을 반복한다.
  • 부분 리스트들이 더 이상 분할이 불가능할 때까지 반복한다.
    • 리스트의 크기가 0이나 1이 될 때까지 반복한다.

 

퀵정렬 애니메이션

 

퀵정렬의 단계

  1. 분할: 배열을 pivot을 기준으로 2개의 서브 리스트 (pivot을 중심으로 왼쪽: pivot보다 작은 요소들, 오른쪽: pivot보다 큰 요소들)로 분할.
  2. 정복: 나누어진 두 개의 공간을 정렬한다. 서브 리스트의 크기가 하나가 남을 때까지 재귀 호출을 이용하여 다시 분할 정복해 나간다.
  3. 결합: 정렬된 부분 배열들을 하나의 배열에 합병한다.

 

아래는 타 블로거의 자료를 참고하여 적어 보았다.(감사합니다)

 

참조 :https://innovation123.tistory.com/84

 

[JAVA] 퀵 정렬 (Quick Sort)

퀵 정렬 (Quick sort) 퀵 정렬은 기준값(pivot)을 선정해 해당 값보다 작은 데이터와 큰 데이터로 분류하는 것을 반복해 정렬하는 알고리즘이다. 시간복잡도가 비교적 빠르기 때문에 코딩테스트에서

innovation123.tistory.com

퀵 정렬 예제

예제 배열


예제 1 : 첫 번째 반복

위와 같은 정렬되지 않은 배열이 있다.

- 중간값을 pivot으로 선정한다. (5)

- start와 end인덱스를 지정한다.


start < pivot의 경우 start를 한 칸씩 오른쪽으로 이동한다. ( pivot보다 큰 값을 찾을 때까지 이동 )

end > pivot의 경우 end를 한 칸씩 왼쪽으로 이동한다. (pivot보다 작은 값을 찾을 때까지 이동 )


start와 end 포인트를 더 이상 움직일 수 없는 경우(원하는 값을 찾은 경우) 두 데이터를 swap 한다.( 2와 9 swap )

swap을 하고 나서 start, end 포인트를 한 칸씩 옮겨준다. 


다시 위의 과정을 반복해, start, end 포인트가 멈출 때까지 이동시킨다. ( start=7, end=1)


찾은 두 값을 swap 하고, 포인트를 한 칸씩 옮겨준다.

여기서 5의 위치는 start 포인트이면서 동시에 pivot 값이다.


start(5)는 5보다 작지 않기 때문에 움직이지 않는다.

0은 5보다 작으므로 움직이지 않는다. ( 0과 5 swap )

0과 5를 swap 하고, 포인트를 각각 움직여준다. 


 

start와 end가 반복문의 조건에 맞지 않게 된다. (start <= end)

따라서 이 루프는 종료하고, start가 가리키고 있는 5를 기준으로 전체 배열을 나눈다. (파티션)

그렇게 되면 5는 오른쪽 파티션의 첫 번째 인덱스가 되고, 배열은 아래와 같이 나뉘게 된다.


이렇게 하나의 루프가 종료되면 pivot 값이었던 5는 전체 배열에서의 알맞은 위치를 찾게 된다.

이후, 두 개로 나뉜 파티션에서 위의 과정을 반복한다.

각각 중간값(pivot), start, end 포인트를 정하는 등..

두 번째 파티션에 대하여 한 번의 루프를 더 돌아보겠다.


예제 2 : 두 번째 반복

이제 start=5, pivot=6end=9로 지정하여 새로 시작한다.


start는 pivot보다 큰 값이 나올 때까지, end는 pivot보다 작은 값이 나올 때까지 이동한다. 

( end > pivot일 때 움직이므로 end포인트는 6에서 멈춘다) 


두 값을 swap 하고, 한 칸씩 움직인다.

움직이면 start <=end 조건을 만족하지 못하기 때문에 해당 루프는 끝난다.

 

import java.util.Arrays;
import java.util.Scanner;

public class QuickSort {
    static int[] arr;
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        arr = new int[n];
        for (int i = 0; i < n; i++) {
            arr[i] = sc.nextInt();
        }
        quickSort(0, n-1);
        System.out.println(Arrays.toString(arr));

    }

    static void quickSort(int start, int end) {
        //배열이 더이상 나누어지지 않으면 재귀를 빠져나간다.
        if(start>= end) return;
        //pivot 의 index를 중간값으로 설정
        int mid = start + (end - start) / 2;
        //pivot 의 값
        int midValue = arr[mid];

        //비교 되는 범위 값
        int left = start;
        int right = end;

        // 왼쪽의 비교값이 오른쪽보다 커지지 않을때까지만 반복문 실행
        while (left <= right) {
            //pivot 을 기준으로 왼쪽과 오른쪽으로 나누어 확인


            // 왼쪽의 값을 pivot 과 비교하여 작으면 그대로 두고 크거나 같을때 까지 왼쪽 index 의 값을 올림
            // (크면 pivot 을 기준으로 오른쪽으로 옮기고 아니면 그대로 두겠다)
            while (arr[left] < midValue) {
                left++;
            }
            // 위와 정 반대
            while (arr[right] > midValue) {
                right--;
            }
            // 왼쪽 인덱스와 오른쪽 인덱스가 pivot 을 기준으로 반대편으로 넘어가지 않았다면 서로 바꾼다
            if (left <= right) {
                swap(left, right);

                //바꾸고 또 비교해야함으로 왼쪽과 오른쪽 인덱스를 반대쪽으로 하나씩 옮겨준다
                left++;
                right--;
            }
        }
        /*
        * 빠져나온 후엔 다시 둘로 쪼개서 탐색 및 정렬
        * pivot , pivot + 1로 넣을 수도 있겠지만 
        * 아래와 같이 하면 pivot 이 정중앙의 숫자라면 재귀에 포함시키기 않는다
        * */
        quickSort(start, right);
        quickSort(left, end);

    }


    static void swap(int left, int right) {
        int temp = arr[right];
        arr[right] = arr[left];
        arr[left] = temp;
    }
}

 

 

'Java > java 알고리즘' 카테고리의 다른 글

메서드 정리  (0) 2022.12.28
스텍, 큐  (0) 2022.12.28
완주하지 못한 선수  (0) 2022.12.28
평균 구하기  (0) 2022.12.28