본문 바로가기

알고리즘

3. 분할정복 - 퀵 정렬

퀵 정렬이란?

퀵 정렬은 병합 정렬과 마찬가지로 분할정복을 이용하여 정렬을 한다. 퀵 정렬은 피벗을 이용한 정렬 방식이다. 피벗은 두 개의 집합을 나누기 위해 생성된 기준으로 피벗보다 작은 것은 왼쪽으로 피벗보다 큰 것은 오른쪽으로 정렬이 된다. 피벗은 어떤 수가 되어도 상관이 없다. 피벗을 기준으로 두 개의 집합을 나누는 분할의 과정을 거치면 다시 피벗을 기준으로 정렬을 한다.   

 

다음은 퀵  정렬을 표현한 예이다.

 

시간복잡도

퀵 정렬은 두 개의 집합으로 분할하는 것과 피벗을 기준으로 정렬하는 것으로 정렬을 수행한다. 두 개의 집합으로 분리하는 시간은 항상 O(n)을 유지한다. 피벗을 기준으로 정렬하는 시간은 자료의 배치에 따라 평균적으로 O(logn)을 유지하고 최악의 경우는 O(n2)까지 시간이 걸린다. 따라서 시간복잡도는 평균적으로 O(nlogn)을 유지하고 최악의 경우 O(n^2)을 유지한다.

 

예제

#include <iostream>
#include <vector>
using namespace std;

//swap함수 
void change(int& a, int& b) {
	int temp = a;
	a = b;
	b = temp;
}

//피벗을 기준으로 정렬하는 과정
void partition(int low, int high, int & pivot, vector<int> &v) {
	int i, j; //i는 이터레이터, j는 피벗이 처음 존재하는 지점
	int pivot_item; //피벗의 위치에 존재하는 원소

	pivot_item = v[low]; //피벗을 제일 처음 원소로 설정
	j = low; //피벗의 처음위치는 시작점
	for (i = low + 1; i <= high; i++) { //피벗 다음 원소부터 반복문 실행
		if (v[i] < pivot_item) { //현재 원소가 피봇보다 작으면
			j++; //피봇의 위치를 한 칸 뒤로 민다.
			change(v[i], v[j]); //현재 위치와 피봇의 위치를 swap한다.
		}
	}
	pivot = j; //피봇의 위치는 j가 최종적으로 도달한 위치가 된다.
	change(v[low], v[pivot]); //제일 처음위치를 피봇으로 잡았으니까 현재 j가 있는 곳과 swap한다.
}

//퀵정렬, 피벗을 기준으로 정렬하고 피봇을 기준으로 두 개의 집합으로 나눈다.
void quicksort(vector<int> &v, int low, int high) {
	if (low < high) { //인덱스 순서는 시작 ~ 끝 지점
		int pivot; //피봇이 원래 들어갈 위치
		partition(low, high, pivot, v); //피봇을 기준으로 정렬을한다.
		quicksort(v, low, pivot - 1); //피봇 앞에 정렬된 내용을 이용하여 퀵정렬을 사용한다.
		quicksort(v, pivot + 1, high); //피봇 뒤에 정렬된 내용을 이용하여 퀵정렬을 사용한다.
	}
}

int main() {
	vector<int> v;
	v.push_back(15);
	v.push_back(22);
	v.push_back(13);
	v.push_back(27);
	v.push_back(12);
	v.push_back(10);
	v.push_back(20);
	v.push_back(25);

	quicksort(v, 0, v.size() - 1);

	for (int i = 0; i < v.size(); i++) {
		cout << v[i] << ' ';
	}
}

'알고리즘' 카테고리의 다른 글

그리디 알고리즘  (0) 2020.03.09
2. 분할정복 - 병합정렬  (0) 2020.02.16
1. 분할정복 - 이분 탐색  (0) 2020.02.14