알고리즘/정렬

퀵 정렬

Dero Lee 2021. 12. 25. 00:07
728x90

 

퀵 정렬 : 하나의 큰 문제를 두 개의 작은 문제로 분할하는 정렬 알고리즘이다.

특정한 값을 기준(pivot)으로 큰 숫자와 작은 숫자를 선택해 서로 교환한 뒤 배열을 반으로 나눈다. 일반적으로 pivot은 첫번째 원소부터 설정된다.

Ex)

pivot을 기준으로 왼쪽에선 큰값을 찾고, 오른쪽에선 작은 값을 찾는다.

 

3 7 8 1 5 9 6 10 2 4

3 2 8 1 5 9 6 10 7 4

3 2 1 8 5 9 6 10 7 4

 

(작은 값의 인덱스가 큰 값의 인덱스보다 작아 엇갈릴 때 작은 값과 pivot이 교환되어 첫번째 분할이 일어남.)

 

1 2 3 8 5 9 6 10 7 4

 

(3을 기준으로하여 왼쪽의 집합과 오른쪽의 집합에서 각각 pivot이 정해진 뒤 정렬을 수행함.)

 

위의 과정 반복

 

1 2 3 8 5 4 6 10 7 9

1 2 3 8 5 4 6 7 10 9

1 2 3 7 5 4 6 8 10 9

1 2 3 6 5 4 7 8 10 9

~

1 2 3 4 5 6 7 8 9 10

 

정렬을 수행했을 때 왼쪽과 오른쪽이 계속 나눠진다.

 

시간복잡도 : 선택, 버블, 삽입 정렬에 비해 훨씬 빠른 분할 정복 알고리즘으로 평균적인 시간복잡도는 O(N*logN)이다.데이터의 개수가 n개이고, 반씩 분할되므로 O(N*logN)이 나오게 된다.

 

그러나, 최악의 경우 O(N^2)이 나오는데 이미 정렬이 되어있는 경우 pivot을 설정하고 분할이 되지않으므로(엇갈릴 때 계속 자기 자신과 스왑하므로) 연산 횟수가 N^2/2번이 나오게 된다. 이러한 경우 삽입 정렬을 사용하는 것이 속도가 빠르다.

 

#include <iostream>

using namespace std;

int n = 10;
int data[10] = {1, 10, 5, 8, 7, 6, 4, 3, 2, 9};

void Quick_Sort(int* data, int start, int end) {
	if(start >= end) // 원소가 1개인 경우 
		return; 
		
	int pivot = start; // pivot은 처음에 첫번째 원소로 설정됨.
	int i = start + 1; // 왼쪽 출발 지점 
	int j = end; // 오른쪽 출발 지점 
	int temp;
	
	while(i<=j) { // 엇갈릴 때까지 반복 
		while(data[i]<=data[pivot]) // 피봇 값보다 큰 값을 만날때까지 오른쪽 이동
			i++;
		while(data[j]>=data[pivot] && j > start) // 피봇 값보다 작은 값을 만날때까지 왼쪽 이동
			j--; 
		
		if(i>j) { // 현재 엇갈린 상태면 피봇 값과 교체 
			temp = data[j];
			data[j] = data[pivot];
			data[pivot] = temp; 
		}
		else {
			temp = data[i];
			data[i] = data[j];
			data[j] = temp;
		}
	}
	// 엇갈렸을 때 스왑한 후 분할이 일어나고 다시 왼쪽과 오른쪽에서 퀵정렬이 수행된다. 
	Quick_Sort(data, start, j-1);  
	Quick_Sort(data, j+1, end);
}

int main() {
	Quick_Sort(data, 0, n-1);
	for(int i = 0; i<n; i++) {
		cout<<data[i]<<' ';
	}
}
728x90