Home 정렬 알고리즘(퀵 정렬)
Post
Cancel

정렬 알고리즘(퀵 정렬)

퀵 정렬(Quick Sort)

개yo


앞선 포스팅에서는 선택정렬 , 버블정렬 , 삽입정렬에 대해 알아보았다. 3개의, 정렬 알고리즘의 시간 복잡도는 주어진 데이터에 따라 달라지지만 보통 O(N^2) 의 형태를 띄고 있다. 하지만 이번 포스팅에서 설명할 퀵 정렬 의 시간복잡도는 O(N^2)가 아닌 O(N * log N) 를 가지고 있다. 이런 특징 때문에 여러 언어의 정렬 알고리즘의 기반이 되는 알고리즘으로 쓰이고 있다. ex) C++의 sort( ) 등등

문제


문제 : 다음 숫자들을 오름차순으로 정렬하는 프로그램을 작성 하여라

[ 7, 5, 9, 0, 3, 1, 6, 2, 4, 8 ]

SOL

문제를 해결하기 위해 퀵정렬을 쓴다면 “특정한 값을 기준으로 큰 숫자와 작은 숫자를 나누어 배치하기” 를 생각해 볼 수 있다.

위와 같은 방식을 퀵정렬 이라고 한다.

특징

  1. 평균 시간 복잡도는 O(N * log N)이다.
  2. 최악(데이터가 거의 정렬되어 있는)의 경우의 시간 복잡도는 O(N^2)이다.
  3. 효율성이 좋아 각 언어의 sort lib의 기반이 되는 알고리즘으로 사용된다.

구현


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
#include <iostream>

using namespace std; 

void QuickSort(int *arr, int begin, int end)
{
    if(begin >= end) return; // 원소가 1개인 경우 종료
    int pivot = begin; // 기준값(pivot)은 첫번째 인덱스 
    int left = begin + 1;
    int right = end;
    while (left <= right)
    {
        // Left값이 Pivot값 보다 작은경우 반복
        while (left <= end && arr[left] <= arr[pivot]) left++;
        // Right값이 Pivot값 보다 큰경우 반복
        while (right > begin && arr[right] >= arr[pivot]) right--; 
        // Left, Right의 인덱스 즉 순서가 엇갈리면, Pivot, Right swap
        if(left > right) swap(arr[pivot], arr[right]);
        // Pivot값보다 L, R의 조건이 모두 맞지 않는 경우 swap(L, R)
        else swap(arr[left], arr[right]);
    }
    // 재귀적 호출 (log N)번 실행
    // pivot 보다 작은 값들의 리스트 (pivot 포함 O)
    QuickSort(arr, begin, right - 1); 
    // pivot 보다 큰 값들의 리스트   (picot 포함 X)
    QuickSort(arr, right + 1, end);
}

int main()
{
    int arr[10] = {7, 5, 9, 0, 3, 1, 6, 2, 4, 8};
    int n = 10;
    QuickSort(arr, 0, n-1);
    for(auto el : arr)
        cout << el << " ";
    cout << endl;
    return 0;
}

출력

1
0 1 2 3 4 5 6 7 8 9 

해설


위 코드를 실행하면 오름차순으로 배열이 정리된 모습을 확인 할 수 있다.

  1. 리스트 안의 요소 한개를 선택한다. - 기준값(Pivot) 설정

    1
    
     int pivot = begin; // 기준값(pivot)은 첫번째 인덱스 
    
  2. 설정한 기준값(Pivot)을 기준으로 보다 작은 요소는 왼쪽 보다 큰 요소들은 모두 오른쪽으로 반복적으로 옮긴다.

    1
    2
    3
    4
    
     // Left값이 Pivot값 보다 작은경우 반복
     while (left <= end && arr[left] <= arr[pivot]) left++;
     // Right값이 Pivot값 보다 큰경우 반복
     while (right > begin && arr[right] >= arr[pivot]) right--; 
    
  3. 기준값(Pivot)을 제외한 왼쪽, 오른쪽 리스트를 정렬한다.

    1
    2
    3
    4
    
    // Left, Right의 인덱스 즉 순서가 엇갈리면, Pivot, Right swap
    if(left > right) swap(arr[pivot], arr[right]);
    // Pivot값보다 L, R의 조건이 모두 맞지 않는 경우 swap(L, R)
    else swap(arr[left], arr[right]);
    
  4. 분할된 부분의 리스트를 다시 호출 - 정렬한다.
  5. 분할 된 리스트도 마찬가지고 기준값(Pivot)을 기준으로 2개의 리스트로 나누는 과정을 재귀적으로 반복한다.

    1
    2
    3
    4
    5
    
     // 재귀적 호출 (log N)번 실행
     // pivot 보다 작은 값들의 리스트 (pivot 포함 O)
     QuickSort(arr, begin, right - 1); 
     // pivot 보다 큰 값들의 리스트   (picot 포함 X)
     QuickSort(arr, right + 1, end); 
    
  6. 언제까지?? ~~> 리스트의 크기가 0이나 1이 될때까지

    1
    
     if(begin >= end) return; // 원소가 1개인 경우 종료
    

예시문제


두개의 배열 A, B가 존재한다. 이 두 배열은 N개의 원소로 구성되어 있으며, 배열의 원소는 모두 자연수이다.

두 배열 A, B를 서로 K 번의 스왑을 할 수 있을 때 배열 A의 모든 원소의 합이 최대가 되어야 한다.

SOL

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#include <iostream>
#include <vector>
#include <algorithm>

using namespace std; 


void solution(vector<int> arr_1, vector<int> arr_2, int k)
{
    int answer = 0;
    sort(arr_1.begin(), arr_1.end());
    sort(arr_2.rbegin(), arr_2.rend());
    for(int i=0; i < k; i++)
        if(arr_1[i] < arr_2[i]) swap(arr_1[i], arr_2[i]);
    for(auto el : arr_1)
        answer += el;
    cout << answer << endl;
}

int main()
{
    vector<int> arr_1{1,2,5,4,3};
    vector<int> arr_2{5,5,6,6,5};
		int k = 3;
    solution(arr_1, arr_2, k);

    return 0;
}

Reference


자바 [JAVA] - 퀵 정렬 (Quick Sort)

(이코테 2021 강의 몰아보기) 4. 정렬 알고리즘

This post is licensed under CC BY 4.0 by the author.