분할 정복 기법을 통해 정렬하는 알고리즘이며, 다른 원소와의 비교만으로 정렬을 수행하는 비교정렬이다. 또한, 합병 정렬과 달리 배열을 비균등하게 분할한다.
분할 정복 (Divide and Conquer)
- 문제를 작은 2개의 문제로 분리하고 각각을 해결한 다음, 결과를 모아서 원래의 문제를 해결하는 전략
- 재귀 호출을 이용하여 구현
컴퓨터로 가장 많이 구현된 알고리즘 중 하나이며, 거의 모든 언어에서 제공되는 정렬 함수는 퀵 정렬 혹은 퀵 정렬의 변형 알고리즘을 사용한다.
Java의 Arrays.sort()는 퀵 정렬의 변형 알고리즘인 듀얼피봇 퀵 정렬(Dual-Pivot Quick sort)을 사용한다.
public class Main {
static int[] nums = {1000, 400, 12, -59, 328, 121, -3};
public static void main(String[] args) {
int size = nums.length-1;
quickSort(0, size);
System.out.println(Arrays.toString(nums));
}
// method 1
static void quickSort(int left, int right) {
if(left >= right) {
return;
}
int pivot = partition(left, right);
quickSort(left, pivot-1);
quickSort(pivot+1, right);
}
// method 2
static int partition(int left, int right) {
int pivot = nums[left];
int i = left, j = right;
while(i < j) {
while(pivot < nums[j]) {
j--;
}
while(i < j && pivot >= nums[i]) {
i++;
}
swap(i, j);
}
nums[left] = nums[i];
nums[i] = pivot;
return i;
}
static void swap(int x, int y) {
int temp = nums[x];
nums[x] = nums[y];
nums[y] = temp;
}
}
method 1: 부분 배열을 정렬한다. 부분 배열의 크기가 충분히 작지 않으면 재귀 호출을 이용하여 다시 분할 정복 방법을 이용한다.
method 2: 배열을 피벗을 기준으로 비균등하게 2개의 부분 배열로 나눈다. 피벗의 왼쪽에는 피벗보다 작은 값, 오른쪽에는 피벗보다 큰 값이 온다.
(log₂n)
(n)
따라서, 최선의 시간 복잡도는 재귀 호출의 깊이 * 비교 연산 = nlog₂n
이 된다. 이동 횟수는 비교 횟수보다 적으므로 무시할 수 있다.
최악의 경우는 정렬하고자 하는 배열이 오름차순 혹은 내림차순 정렬되어 있어 배열이 계속 불균형하게 나누어지는 경우 혹은 피벗을 최솟값이나 최댓값으로 선택하는 경우이다.
(n)
(n)
따라서, 최악의 시간 복잡도는 재귀 호출의 깊이 * 비교 연산 = n^2
이다. 마찬가지로 이동 횟수는 비교 횟수보다 적으므로 무시할 수 있다.
주어진 배열 안에서 교환(Swap)을 통해 정렬이 수행되므로 O(n) 이다.