1. 퀵정렬
: 기준값[pivot]을 선정해 해당 값보다 작은 데이터와 큰 데이터로 분류하는 것을 반복해 정렬
-> 기준값을 선택하는 함수가 시간 복잡도에 영향을 미치며, 평균적으로 시간복잡도는 O(nlogn)이다.
-> 퀵이라는 이름에서 알 수 있듯이 평균적인 상황에서 최고의 성능을 나타낸다.
[컴퓨터로 가장 많이 구현된 정렬 알고리즘 중 하나]
(1) 구현 과정
-> 기준값 pivot을 중심으로 데이터를 2개의 집합으로 나누는 것을 반복해 정렬
- 데이터를 분할하는 pivot 설정 [방법에 따라 다르지만 여기서는 가장 오른쪽 끝 값으로 설정]
- 기준값을 기준으로 2개의 집합으로 분리하는 과정
- start 데이터< pivot 데이터 -> start를 오른쪽으로 1칸 이동 [우측 shift 연산]
- end 데이터> pivot 데이터 -> end를 왼쪽으로 1칸 이동 [좌측 shift 연산]
- start 데이터>pivot 데이터 && end 데이터<pivot 데이터 -> start 데이터와 end 데이터 swap 연산 후,
start는 오른쪽으로 1칸 이동 [우측 shift 연산], end는 왼쪽으로 1칸 이동 [좌측 shift 연산] - start와 end가 만날때까지 해당 과정 반복
- start == end인 경우 만난 지점의 데이터와 pivot의 데이터를 비교
- index 데이터 < pivot 데이터 -> index +1 지점에 데이터 삽입
- index 데이터 > pviot 데이터 -> index -1 지점에 데이터 삽입
- index 데이터 < pivot 데이터 -> index +1 지점에 데이터 삽입
- 분리 집합에서 각각 다시 pivot 선정 [기준값 변경]
- 분리집합이 1개 이하가 될 때까지 해당 과정 반복
문제 19. K번째 수 구하기
수 N개(A1 ~ AN)가 주어진다. A를 오름차순 정렬했을 때 앞에서부터 K번째에 있는 수를 구하는 프로그램을 작성하시오.
입력
첫 번째 줄에 N(1 <= N <= 5,000,000)과 K(1 <= K <= N), 두 번째 줄에 A1 ~ AN이 주어진다(-109 <= Ai <= 109)
출력
A를 정렬했을 때 앞에서부터 K번째에 있는 수를 출력한다.
예제 입력 1
5 2 //데이터 개수,K번째 수 2
4 12 3 5
예제 출력 1
2
1단계. 문제 분석하기
-> N의 범위를 보면 5,000,000이므로 O(nlogn)의 시간 복잡도인 퀵 정렬 이용
-> 퀵 정렬을 사용해 오름차순 정렬 후, K번째 수 출력
-> 먼저 기준값인 pivot을 구하는 방법 찾기
# pivot을 구하는 방법
pivot == K : K번째를 찾았으므로 알고리즘 종료
pivot > K : K가 pivot보다 작기 때문에 왼쪽 집합만 정렬 수행 [S ~ pivot -1]
pivot < K : K가 pivot보다 크기 때문에 오른쪽 집합만 정렬 수행 [pivot - 1 ~ E]
-> 데이터가 정렬되어있는 경우에 pivot의 위치가 앞에 존재할 경우 불필요한 연산이 많아진다
-> 따라서, 예시의 경우 배열의 중간 위치를 pivot으로 설정
2단계. 손으로 풀어 보기
- 중간 인덱스를 pivot으로 설정 후, 맨 앞에 있는 값과 swap연산 수행
-> pivot을 맨 앞으로 옮겨서 i, j 이동을 편하게 하기 위해서
-> i와 j를 pivot을 제외한 그룹에서 왼쪽과 오른쪽 끝으로 설정
[즉, i는 1번 인덱스, j는 마지막 인덱스에 위치] - 먼저 j를 이동시키기 위해, j보다 pivot보다 크면 j-- 연산을 반복
-> 반복 결과 j는 1에 위치하게 된다
-> j를 이동 후, i<pivot && i<j일 경우, i++연산 반복 - pivot을 i와 j가 만나는 위치와 swap 연산 수행
- K가 2이므로 정렬하지 않고 A[2] 출력
3단계. 슈도 코드 작성하기
N(숫자의 개수) K(K번째 수)
A(숫자 데이터 저장 배열)
for(N만큼 반복하기) {
A 배열 저장하기
}
퀵 소트 실행하기
K번째 데이터 출력하기
[별도의 함수 구현 부분]
퀵 소트 함수(시작,종료,K)
{
피벗 구하기 함수(시작, 종료)
if(피벗 == K) 종료
else if(K < 피벗) 퀵 소트 수행하기(시작, 피벗 - 1, K)
else 퀵 소트 수행하기(피벗 + 1, 종료,K)
}
피벗 구하기 함수(시작, 종료)
{
중앙값을 피벗으로 설정하기
i(시작점), j(종료점)
while(i < j)
{
피벗보다 큰 수가 나올 때까지 i++
피벗보다 작은 수가 나올 때까지 j찾은
i와 j 데이터를 swap
}
피벗 데이터를 나눠진 두 그룹의 경계 index에 저장하기
}
package sorting.ch04;
import java.util.Scanner;
public class sorting_q19_sol {
public static void main(String[] args) {
sorting_q19_sol T = new sorting_q19_sol();
Scanner kb = new Scanner(System.in);
int n = kb.nextInt();
int k = kb.nextInt();
int[] arr = new int[n];
for (int i = 0; i < n; i++)
arr[i] = kb.nextInt();
sort(arr, 0, n - 1, k - 1);
System.out.println(arr[k - 1]);
}
public static void sort(int[] arr, int start, int end, int k) {
if (start < end) {
int pivot = part(arr, start, end);
if (pivot == k)
return;
else if (k < pivot)
sort(arr, start, pivot - 1, k);
else
sort(arr, pivot + 1, end, k);
}
}
public static int part(int[] arr, int start, int end) {
int mid = (start + end) / 2;
swap(arr, start, mid);
int pivot = arr[start];
int i = start, j = end;
while (i < j) {
while (pivot < arr[j]) {
j--;
}
while (i < j && pivot >= arr[i]) {
i++;
}
swap(arr, i, j);
}
arr[start] = arr[i];
arr[i] = pivot;
return i;
}
public static void swap(int[] arr, int i, int j) {
int tmp = arr[i];
arr[i] = arr[j];
arr[j] = tmp;
}
}
'Java > Java 알고리즘' 카테고리의 다른 글
[알고리즘] 1-6. 기수 정렬 [구간 합 이용] (+ 우선순위 큐 이용) (0) | 2022.06.28 |
---|---|
[알고리즘] 1-5. 병합 정렬 [재귀 함수, 투 포인터 이용] (0) | 2022.06.28 |
[알고리즘] 1-3. 삽입 정렬 # (0) | 2022.06.27 |
[알고리즘] 1-2. 선택 정렬 # (0) | 2022.06.27 |
[알고리즘] 문제 풀이 순서 정리 (0) | 2022.06.27 |