Merge Sort와 마찬가지로 QuickSort는 Divide and Conquer 알고리즘입니다. 요소를 피벗으로 선택하고 선택한 피벗을 중심으로 주어진 배열을 분할합니다. 다양한 방법으로 피벗을 선택하는 여러 버전의 quickSort가 있습니다.
- 항상 첫 번째 요소를 피벗으로 선택합니다.
- 항상 마지막 요소를 피벗으로 선택 (아래에 구현 됨)
- 임의의 요소를 피벗으로 선택
- li>
- 중앙값을 피벗으로 선택합니다.
quickSort의 핵심 프로세스는 partition ()입니다. 파티션의 대상은 배열과 배열의 요소 x가 피벗으로 주어지면 정렬 된 배열의 올바른 위치에 x를 놓고 x 앞에 모든 작은 요소 (x보다 작음)를 배치하고 x보다 큰 모든 요소를 뒤에 배치합니다. 엑스. 이 모든 작업은 선형 시간으로 수행되어야합니다.
재귀 적 QuickSort 함수를위한 의사 코드 :
파티션 알고리즘
파티션을 수행하는 방법은 여러 가지가있을 수 있으며, 의사 코드는 CLRS 책에 제공된 방법을 채택합니다. 논리는 간단합니다. 가장 왼쪽 요소부터 시작하여 더 작은 (또는 같음) 요소의 인덱스를 i로 추적합니다. 순회하는 동안 더 작은 요소를 찾으면 현재 요소를 arr로 바꿉니다. 그렇지 않으면 현재 요소를 무시합니다.
partition ()에 대한 의사 코드
partition () 그림 :
구현 :
다음은 QuickSort의 구현입니다.
출력 :
Sorted array:1 5 7 8 9 10
QuickSort 분석
일반적으로 QuickSort에서 소요되는 시간은 다음과 같이 쓸 수 있습니다.
T(n) = T(k) + T(n-k-1) + (n)
처음 두 용어는 두 개의 재귀 호출에 대한 것이고 마지막 용어는 파티션 프로세스에 대한 것입니다. k는 피벗보다 작은 요소의 수입니다.
QuickSort에 걸리는 시간은 입력 배열 및 파티션 전략에 따라 다릅니다. 다음은 세 가지 경우입니다.
최악의 경우 : 파티션 프로세스가 항상 가장 큰 또는 가장 작은 요소를 피벗으로 선택하는 경우 최악의 경우가 발생합니다. 마지막 요소가 항상 피벗으로 선택되는 위의 파티션 전략을 고려하면 배열이 이미 증가 또는 감소 순서로 정렬 된 경우 최악의 경우가 발생합니다. 다음은 최악의 경우 재발입니다.
T(n) = T(0) + T(n-1) + (n)which is equivalent to T(n) = T(n-1) + (n)
위 반복의 솔루션은 (n2)입니다.
최상의 사례 : 파티션 프로세스가 항상 피벗으로 중간 요소. 다음은 최상의 경우에 대한 반복입니다.
T(n) = 2T(n/2) + (n)
위의 반복에 대한 해결책은 (nLogn)입니다. Master Theorem의 사례 2를 사용하여 해결할 수 있습니다.
Average Case :
평균 사례 분석을 수행하려면 배열의 가능한 모든 순열을 고려하고 그렇지 않은 모든 순열에 걸리는 시간을 계산해야합니다.
파티션이 한 세트에 O (n / 9) 요소를, 다른 세트에 O (9n / 10) 요소를 넣는 경우를 고려하면 평균적인 케이스를 알 수 있습니다. 다음은이 경우의 재발입니다.
T(n) = T(n/9) + T(9n/10) + (n)
위의 반복에 대한 솔루션도 O (nLogn)입니다.
QuickSort의 최악의 경우 시간 복잡도는 O (n2)입니다. Merge Sort 및 Heap Sort와 같은 다른 많은 정렬 알고리즘보다 많으며 QuickSort는 내부 루프가 대부분의 아키텍처와 대부분의 실제 데이터에서 효율적으로 구현 될 수 있기 때문에 실제로 더 빠릅니다. QuickSort는 피벗 선택을 변경하여 다양한 방식으로 구현할 수 있으므로 주어진 데이터 유형에 대해 최악의 경우가 거의 발생하지 않습니다. 그러나 병합 정렬은 일반적으로 데이터가 크고 외부 저장소에 저장 될 때 더 나은 것으로 간주됩니다.
QuickSort는 안정적입니까?
기본 구현은 안정적이지 않습니다. 그러나 인덱스를 비교 매개 변수로 고려하여 모든 정렬 알고리즘을 안정적으로 만들 수 있습니다.
QuickSort In-place입니까?
In-place 알고리즘의 광범위한 정의에 따라 추가를 사용하므로 내부 정렬 알고리즘으로 적합합니다. 재귀 함수 호출을 저장하기위한 공간이며 입력 조작을위한 공간은 아닙니다.
연결된 목록에 대해 QuickSort를 구현하는 방법은 무엇입니까?
단일 연결 목록에서 QuickSort를 구현하는 방법
이중 연결 목록의 QuickSort
QuickSort를 반복적으로 구현할 수 있습니까?
예, 반복적 빠른 정렬을 참조하십시오.
배열 정렬을 위해 MergeSort보다 빠른 정렬이 선호되는 이유
일반적인 형태의 빠른 정렬은 내부 정렬 (즉, 추가 스토리지가 필요하지 않음) 인 반면 병합 정렬에는 O (N) 추가 스토리지가 필요하며 N은 상당히 비쌀 수있는 어레이 크기를 나타냅니다. 병합 정렬에 사용되는 추가 공간을 할당 및 할당 해제하면 알고리즘의 실행 시간이 늘어납니다. 평균 복잡도를 비교하면 두 종류의 정렬 모두 O (NlogN) 평균 복잡도가 있지만 상수는 다릅니다. 배열의 경우 추가 O (N) 저장 공간 사용으로 인해 병합 정렬이 손실됩니다.
대부분의 빠른 정렬 구현은 무작위 버전을 사용합니다. 무작위 버전의 예상 시간 복잡성은 O (nLogn)입니다.무작위 버전에서도 최악의 경우가 가능하지만 특정 패턴 (예 : 정렬 된 배열)에서는 최악의 경우가 발생하지 않으며 무작위 빠른 정렬은 실제로 잘 작동합니다.
빠른 정렬은 캐시 친화적 인 정렬이기도합니다. 알고리즘이 배열에 사용될 때 참조의 좋은 지역성을 갖기 때문입니다.
Quick Sort는 꼬리 재귀 적이므로 꼬리 호출 최적화가 수행됩니다.
연결된 목록에서 QuickSort보다 MergeSort가 선호되는 이유 ?
연결 목록의 경우 배열과 연결 목록의 메모리 할당 차이로 인해 대소 문자가 다릅니다. 배열과 달리 연결된 목록 노드는 메모리에서 인접하지 않을 수 있습니다. 배열과 달리 연결 목록에서는 O (1) 추가 공간과 O (1) 시간에 중간에 항목을 삽입 할 수 있습니다. 따라서 병합 정렬의 병합 작업은 연결 목록을위한 추가 공간없이 구현할 수 있습니다.
배열에서는 요소가 메모리에서 연속적이기 때문에 임의 액세스를 수행 할 수 있습니다. 정수 (4 바이트) 배열 A가 있고 A의 주소를 x로 설정 한 다음 A에 액세스하기 위해 (x + i * 4)의 메모리에 직접 액세스 할 수 있습니다. 배열과 달리 연결 목록에서는 임의 액세스를 할 수 없습니다. 빠른 정렬에는 이러한 종류의 액세스가 많이 필요합니다. i 번째 인덱스에 액세스하기위한 연결 목록에서는 연속적인 메모리 블록이 없기 때문에 헤드에서 i 번째 노드로 각각의 모든 노드를 이동해야합니다. 따라서 빠른 정렬을 위해 오버 헤드가 증가합니다. 병합 정렬은 데이터에 순차적으로 액세스하며 임의 액세스의 필요성이 낮습니다.
최악의 경우 O (Log n) 추가 공간을 차지하도록 QuickSort를 최적화하는 방법은 무엇입니까?
QuickSort Tail Call Optimization (최악의 경우 Log n으로 공간 줄이기)을 참조하십시오.
스냅 샷 :
- Quiz on QuickSort
- Recent Articles on QuickSort
- 정렬.
GeeksforGeeks / GeeksQuiz의 기타 정렬 알고리즘 :
선택 정렬, 버블 정렬, 삽입 정렬, 병합 정렬, 힙 정렬, QuickSort , 기수 정렬, 계수 정렬, 버킷 정렬, ShellSort, Comb 정렬, Pigeonhole 정렬