퀵 정렬(Quick Sort)
퀵 정렬은 기준값(pivot)을 선정해 해당 값보다 작은 데이터와 큰 데이터로 분류하는 것을 반복해 정렬하는 알고리즘이다. 기준값이 어떻게 선정되는지가 시간 복잡도에 많은 영향을 미치고, 평균적인 시간 복잡도는 O(nlogn)이다.
pivot을 중심으로 계속 데이터를 2개의 집합으로 나누면서 정렬하는 것이 퀵 정렬의 핵심이다.
퀵 정렬 과정
- 데이터를 분할하는 pivot 설정
- pivot 기준 다음 2.1~2.5 과정을 거쳐 데이터를 2개의 집합으로 분리
- start가 가리키는 데이터가 pivot이 가리키는 데이터보다 작으면 start 오른쪽으로 1칸 이동
- end가 기리키는 데이터가 pivot이 가리키는 데이터보다 크면 end 왼쪽으로 1칸 이동
- start가 가리키는 데이터가 pivot이 가리키는 데이터보다 크고, end가 가리키는 데이터가 pivot이 가리키는 데이터보다 작으면 start, end가 가리키는 데이터를 swap하고 start는 오른쪽, end는 왼쪽으로 1칸씩 이동
- start가 end를 지나갈 때까지 반복
- start가 가리키는 값과 end가 가리키는 값 중 작은 값을 pivot과 교환, 이 pivot 값은 정렬 위치 확정
- 분리 집합에서 각각 다시 pivot을 선정
- 분리 집합이 1개 이하가 될 때까지 과정 1~3을 반복
퀵 정렬의 시간 복잡도는 비교적 준수하므로 코딩 테스트에서도 종종 응용한다.
[백준 11004번] K번째 수
https://www.acmicpc.net/problem/11004
N의 최대 범위가 5,000,000이므로 O(nlogn)의 시간 복잡도로 정렬을 수행하면 된다.
이 문제는 시간 복잡도가 민감하므로 퀵 정렬 알고리즘에서 K번째 수를 좀 더 빨리 구하기 위한 아이디어를 먼저 고안하기 위해 우선 어떤 값을 pivot으로 정해야 할지 생각해 봐야 한다.
pivot을 정하는 방법
- pivot == K : K번째 수를 찾은 것이므로 알고리즘을 종료한다.
- pivot > K : pivot의 왼쪽 부분에 K가 있으므로 왼쪽 (S ~ pivot-1)만 정렬을 수행한다.
- pivot < K : pivot의 오른쪽 부분에 K가 있으므로 오른쪽 (pivot-1 ~ E)만 정렬을 수행한다.
데이터가 대부분 정렬되어 있는 경우 앞쪽에 있는 수를 pivot으로 선택하면 불필요한 연산이 많아진다. 따라서 이 문제는 배열의 중간 위치를 pivot으로 설정하겠다.
[정답]
import java.io.*;
import java.util.*;
public class Main {
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
StringTokenizer st = new StringTokenizer(br.readLine());
int N = Integer.parseInt(st.nextToken());
int K = Integer.parseInt(st.nextToken());
st = new StringTokenizer(br.readLine());
int[] arr = new int[N];
for (int i = 0; i < N; i++) {
arr[i] = Integer.parseInt(st.nextToken());
}
quickSort(arr, 0, N-1, K-1);
System.out.println(arr[K-1]);
}
public static void quickSort(int[] arr, int S, int E, int K) {
if(S < E) {
int pivot = partition(arr, S, E);
if(pivot == K) // K번째 수가 pivot이면 더 이상 구할 필요 없음
return;
else if(K < pivot) // K가 pivot보다 작으면 왼쪽 그룹만 정렬 수행하기
quickSort(arr, S, pivot-1, K);
else // K가 pivot보다 크면 오른쪽 그룹만 정렬 수행하기
quickSort(arr, pivot+1, E, K);
}
}
public static int partition(int[] arr, int S, int E) {
if(S + 1 == E) { // 비교 대상 2개 뿐. 소팅하지 말고 그냥 직접 비교
if(arr[S] > arr[E]) swap(arr, S, E);
return E;
}
int M = (S + E) / 2;
swap(arr, S, M); // 중앙값을 1번째 요소로 이동하기
int pivot = arr[S];
int i = S + 1, j = E;
while(i <= j) {
while(pivot < arr[j] && j > 0) // 피벗보다 작은 수가 나올 때까지 j--
j--;
while(pivot > arr[i] && i < arr.length-1) // 피벗보다 큰 수가 나올 때까지 i++
i++;
if(i <= j)
swap(arr, i++, j--);
}
// i == j 피벗의 값을 양쪽으로 분리한 가운데에 오도록 설정하기
arr[S] = arr[j];
arr[j] = pivot;
return j;
}
public static void swap(int[] arr, int i, int j) {
int tmp = arr[i];
arr[i] = arr[j];
arr[j] = tmp;
}
}
[참고 자료]
- 알고리즘 코딩 테스트 자바편
[참고]
[알고리즘] 퀵 정렬 (Quick sort) - 자바 Java
[ 개념 ] - 분할 정복(divide and conquer) 방법을 통해 주어진 배열 정렬 - 불안정 정렬, 비교 정렬 📍 분할 정복 (divide and conquer) : 문제를 작은 2개의 문제로 분리하고 각각을 해결한 후, 결과를 모아
erinh.tistory.com
- https://st-lab.tistory.com/250
자바 [JAVA] - 퀵 정렬 (Quick Sort)
[정렬 알고리즘 모음] 더보기 1. 계수 정렬 (Counting Sort) 2. 선택 정렬 (Selection Sort) 3. 삽입 정렬 (Insertion Sort) 4. 거품 정렬 (Bubble Sort) 5. 셸 정렬 (Shell Sort) 6. 힙 정렬 (Heap Sort) 7. 합병(병합) 정렬 (Merge
st-lab.tistory.com
- https://cdragon.tistory.com/entry/Algorithm-Quick-Sort%ED%80%B5-%EC%A0%95%EB%A0%AC
[Algorithm | Java] Quick Sort(퀵 정렬)
이번시간에는 퀵 정렬에 대해서 배워 보도록 하겠습니다. 저번 포스팅에서 설명했던 정렬 방식들(버블 정렬, 삽입 정렬, 선택 정렬) 은 가장 기본적인 수준의 정렬 방식들입니다. 그러나 이것들
cdragon.tistory.com
'알고리즘 > Do it! 알고리즘 코딩 테스트' 카테고리의 다른 글
기수 정렬 (Radix sort) (0) | 2024.05.21 |
---|---|
병합 정렬 (Merge sort) (0) | 2024.05.21 |
삽입 정렬 (Insertion sort) (0) | 2024.05.16 |
선택 정렬 (Selection sort) (0) | 2024.05.15 |
버블 정렬 (Bubble sort) (0) | 2024.05.15 |