병합 정렬보다 빠른 정렬이 더 좋은 이유는 무엇입니까?


354

인터뷰 중에이 질문을 받았습니다. 그들은 O (nlogn)이며 대부분의 사람들은 Mergesort 대신 Quicksort를 사용합니다. 왜 그런 겁니까?


91
이것은 좋은 인터뷰 질문이 아닙니다. 실제 데이터는 섞이지 않습니다. 종종 스마트 정렬에서 사용할 수있는 많은 순서가 포함되며, 알고리즘이 자동으로 수행하지는 않지만 퀵 정렬보다 병합 정렬을 해킹하는 것이 더 쉽습니다. GNU libc qsort, Python list.sortArray.prototype.sortFirefox의 JavaScript는 모두 통합 된 병합 정렬입니다. (GNU의 STL은 sortIntrosort 대신 사용하지만 C ++에서 교환 잠재적으로 복사하는 동안 큰 승리 때문에 그 수 있습니다.)
제이슨 Orendorff에게

3
@Jason Orendorff : 왜 그렇 "easier to hack a mergesort to do it than a quicksort"습니까? 인용 할 수있는 구체적인 예가 있습니까?
Lazer

16
@eSKay 병합 정렬은 초기 데이터를 정렬 된 하위 배열로 그룹화하여 시작합니다. 배열에 처음에 이미 정렬 된 영역이 포함되어 있으면 시작하기 전에 해당 영역이 있음을 감지하여 많은 시간을 절약 할 수 있습니다. 그리고 당신은 O (n) 시간에 그것을 할 수 있습니다. 구체적인 예는 앞서 언급 한 세 가지 프로젝트의 소스 코드를 참조하십시오! 가장 좋은 예는 파이썬의 여기 상세하게 설명 Timsort 될 수 있습니다 svn.python.org/view/python/trunk/Objects/...을 하고 구현 svn.python.org/view/python/trunk/Objects/... .
Jason Orendorff

4
@JasonOrendorff : 이미 정렬 된 섹션을 활용하기 위해 mergesort를 더 쉽게 수정할 수 있다는 주장을 잘 모르겠습니다. 퀵 정렬의 분할 단계는 나중에 결과 파티션이 모두 정렬되어 있는지 확인하고 재귀를 중지하도록 사소하게 수정 될 수 있습니다. 이것은 잠재적으로 비교 횟수를 두 배로 늘리지 만 해당 단계의 O (n) 시간 복잡성을 변경하지는 않습니다.
j_random_hacker

3
@j_random_hacker : 맞습니다. 그것이 제가 암시 한 것입니다. 그러나 {10, 2, 3, 4, 5, 6, 7, 8, 1, 9}를 고려하십시오. 거의 완전히 정렬 되었음에도 불구하고 파티션을 찾기 전에 검사하거나 검사하지 않습니다. 그리고 후속 호출이 확인하기 전에 파티션이 망가질 것입니다. 한편, 병합 정렬은 이동하기 전에 분할 단계에서 정렬 된 시퀀스를 검사하며, 스마트 정렬은 분할 단계에서 구체적으로 이와 같은 실행을 찾습니다 (팀 정렬 참조)
Mooing Duck

답변:


275

Quicksort는 O ( n 2 ) 최악의 런타임과 O ( n log n ) 평균 케이스 런타임을 갖습니다 . 그러나 많은 요인들이 알고리즘의 런타임에 영향을 미치기 때문에 많은 시나리오에서 병합 정렬이 우수합니다.

특히, 자주 인용되는 정렬 알고리즘 런타임은 데이터를 정렬하는 데 필요한 비교 횟수 또는 스왑 수를 나타냅니다. 이는 기본 하드웨어 설계와 무관하기 때문에 실제로 성능을 측정하는 좋은 방법입니다. 그러나 참조의 지역 성과 같은 다른 것 (즉, 캐시에있는 많은 요소를 읽습니까?)도 현재 하드웨어에서 중요한 역할을합니다. 특히 Quicksort는 추가 공간이 거의 필요하지 않으며 캐시 지역성이 우수하므로 많은 경우 병합 정렬보다 빠릅니다.

또한 피벗을 임의로 선택하는 등의 적절한 피벗을 선택하여 퀵소트의 최악의 경우 O ( n 2 ) 의 런타임을 거의 피할 수 있습니다 (이 방법은 탁월한 전략입니다).

실제로, 많은 현대식 quicksort 구현 (특히 libstdc ++ 's std::sort)은 실제로 introsort 이며 이론상 최악의 경우는 O ( n log n )이며 병합 정렬과 동일합니다. 재귀 수준을 제한하고 log n을 초과하면 다른 알고리즘 ( heapsort )으로 전환하여이를 달성합니다 .


4
Wikipedia 기사는 병합 정렬이 아닌 힙 정렬로 전환한다고 명시합니다.
Sev September

3
@Sev :… orignal 용지와 마찬가지로. 실수를 지적 해 주셔서 감사합니다. – 점근 적 실행 시간이 동일하기 때문에 실제로 중요하지는 않습니다.
Konrad Rudolph

110
이것이 정답으로 선택된 이유는 무엇입니까? 모든 종류의 문제를 신속하게 패치하는 방법 만 설명합니다. 왜 여전히 빠른 정렬이 다른 것보다 더 많이 사용되는지는 알 수 없습니다. 답은 "한 깊이 후에는 힙 정렬로 전환 할 수 있기 때문에 빠른 정렬이 다른 것보다 더 많이 사용됩니까?" .. 왜 처음에 heapsort를 사용하지 않습니까? .. 단지 이해하려고 노력 중 ...
codeObserver

16
@ p1 좋은 질문입니다. 실제 대답은 평균적으로 평균 데이터의 경우 퀵 정렬이 병합 정렬보다 빠르며, 퀵 정렬은 최악의 경우가 병합 정렬보다 느리더라도 매우 쉽게 완화 될 수 있다는 것입니다. (따라서 내 대답).
Konrad Rudolph

4
Quicksort는 메모리 측면에서 더 좋습니다.
Shashwat

287

많은 사람들이 언급했듯이 퀵 정렬의 평균 사례 성능은 병합 정렬보다 빠릅니다. 그러나 요청시 메모리에 액세스하는 데 일정한 시간이 있다고 가정하는 경우에만 해당됩니다.

RAM에서이 가정은 일반적으로 그렇게 나쁘지는 않습니다 (캐시 때문에 항상 사실은 아니지만 너무 나쁘지는 않습니다). 데이터 구조가 디스크에 사는 큰만큼이 그러나 경우, 다음 퀵됩니다 살해 (200)는 임의 초당 추구와 같은 평균 디스크가 무언가를한다는 사실. 그러나 동일한 디스크는 초당 메가 바이트의 데이터를 읽거나 쓰는 데 문제가 없습니다. 어떤 것이 mergesort가하는 것입니다.

따라서 데이터를 디스크에서 정렬해야하는 경우 실제로 mergesort에서 일부 변형을 사용하려고합니다. 일반적으로 하위 목록을 퀵 정렬하여 일부 크기 임계 값 이상으로 병합하기 시작합니다.

또한 해당 크기의 데이터 세트로 무엇이든 해야하는 경우 디스크를 찾지 않는 방법에 대해 열심히 생각하십시오. 예를 들어 데이터베이스에 큰 데이터로드를 수행하기 전에 인덱스를 삭제 한 다음 나중에 인덱스를 다시 작성하는 것이 표준 조언입니다. 로드 중에 인덱스를 유지 관리한다는 것은 지속적으로 디스크를 찾는 것을 의미합니다. 반대로 인덱스를 삭제하면 데이터베이스는 먼저 처리 할 정보를 병합 (물론 병합을 사용하여) 정렬 한 다음 인덱스의 BTREE 데이터 구조에로드하여 인덱스를 다시 작성할 수 있습니다. BTREE는 자연스럽게 순서대로 유지되므로 디스크를 거의 찾지 않고도 정렬 된 데이터 세트에서 하나를로드 할 수 있습니다.

디스크 검색을 피하는 방법을 이해하면 데이터 처리 작업을 며칠 또는 몇 주가 아닌 몇 시간이 걸리는 경우가 많이있었습니다.


1
아주 훌륭하고 데이터 구조에 액세스하기 위해 만들어진 가정에 대해서는 생각하지 않았습니다. 좋은 통찰력 :)
chutsu

2
"디스크로 검색"이란 데이터가 디스크에 저장 될 때 단일 값을 검색한다는 것을 의미하는 것을 설명 할 수 있습니까?
James Wierzba 2016 년

8
@JamesWierzba 나는 그가 "디스크상의 위치를 ​​찾는 것"이라는 맥락에서 그것을 취합니다. 회전하는 디스크 장치에서 "탐색"이란 읽기 헤드를 집어 들고 새로운 절대 주소로 이동시키는 것을 의미합니다. 저장된 순서대로 데이터에 액세스 할 때 디스크 하드웨어는 검색 할 필요가 없으며 항목을 순차적으로 읽는 고속으로 쟁기질을합니다.
엔 크락

1
일부는 이것을 조금 더 설명 할 수 있습니까? 이것이 내가 보는 방법입니다. Quicksort : 임의의 피벗을 사용하는 경우 호출 스택에는 임의의 방식으로 분할 된 배열 조각이 있습니다. 랜덤 액세스가 필요합니다. 그러나 스택의 각 호출에 대해 왼쪽 및 오른쪽 포인터가 순차적으로 이동합니다. 나는 이것들이 캐시에 유지 될 것이라고 가정하고있다. 스왑은 캐시에있는 정보에 대한 작업이며 다시 디스크에 기록됩니다. (다음 주석에서 계속됨)
sam

1
그냥 기여 피해 비용이 많이 드는 디스크 읽기 / 쓰기를 오버 헤드 : 디스크 액세스를 필요로 매우 큰 데이터를 정렬 할 때, 각 패스 종류의 방향을 전환하는 것이 유리합니다. 즉, 루프의 최상위 레벨에서 일단 가면을 0향하고 n다음에 가면을 n0합니다. 이는 메모리 (캐시)에서 이미 사용 가능한 데이터 블록을 후퇴 (정렬)하고 한 번의 디스크 액세스 만 두 번 공격 할 수있는 이점을 제공합니다. 대부분의 DBMS가이 최적화 기술을 사용한다고 생각합니다.
ssd

89

실제로 QuickSort는 O (n 2 )입니다. 그 평균의 경우 실행 시간은 O (nlog (n)이)입니다,하지만 최악의 경우 O (n은 2 당신은 몇 가지 고유 항목이 포함 된 목록에서 실행할 때 발생). 무작위 화에는 O (n)이 필요합니다. 물론 이것은 최악의 경우를 변경하지 않으며 악의적 인 사용자가 오랜 시간이 걸리는 것을 방지합니다.

QuickSort는 다음과 같은 이유로 더 인기가 있습니다.

  1. 제자리에 있습니다 (MergeSort는 정렬 할 요소 수에 따라 추가 메모리가 필요합니다).
  2. 작은 숨겨진 상수가 있습니다.

4
실제로 최악의 경우 O (n ^ 2)가 아닌 O (n * log (n)) 인 QuickSort 구현이 있습니다.
jfs

12
또한 컴퓨터 아키텍처에 따라 다릅니다. QuickSort는 캐시의 이점을 제공하지만 MergeSort는 그렇지 않습니다.
Cristian Ciupitu

4
@ JF Sebastian : 이들은 quicksort가 아닌 introsort 구현 일 것입니다 (intsort는 quicksort로 시작하고 n * log (n)가 되려고한다면 heapsort로 전환합니다).
CesarB

44
적절한 병합을 구현할 수 있습니다.
Marcin

6
병합 정렬은 O (1) 개의 추가 저장소 만 필요한 방식으로 구현 될 수 있지만 이러한 구현의 대부분은 성능 측면에서 크게 어려움을 겪습니다.
Clearer

29

"그러나 대부분의 사람들은 Mergesort 대신 Quicksort를 사용합니다. 왜 그런가요?"

주어지지 않은 심리적 이유 중 하나는 단순히 Quicksort의 이름이 더 영리하다는 것입니다. 즉 좋은 마케팅.

그렇습니다. 트리플 파티셔닝 기능이있는 Quicksort는 아마도 가장 일반적인 범용 정렬 알고리즘 중 하나 일 것입니다. 그러나 "Quick"정렬이 "Merge"정렬보다 훨씬 강력하다는 사실은 극복 할 수 없습니다.


3
어느 쪽이 더 낫다는 질문에 대답하지 않습니다. 알고리즘의 이름은 어느 것이 더 좋은지 결정하는 데 무관합니다.
Nick Gallimore

18

다른 사람들이 지적했듯이 Quicksort의 최악의 경우는 O (n ^ 2)이며 mergesort 및 heapsort는 O (nlogn)에 있습니다. 그러나 평균적으로 세 가지 모두 O (nlogn)입니다. 그것들은 대부분의 경우에 필적합니다.

Quicksort의 평균 성능을 향상시키는 것은 내부 루프가 여러 값을 단일 값과 비교하는 것을 의미하지만 다른 두 값은 각 비교마다 다릅니다. 다시 말해, Quicksort는 다른 두 알고리즘보다 절반의 읽기를 수행합니다. 최신 CPU에서는 성능이 액세스 시간에 의해 크게 좌우되므로 결국 Quicksort가 가장 우선적으로 선택됩니다.


9

지금까지 언급 한 세 가지 알고리즘 (mergesort, quicksort 및 heap sort) 중 하나를 추가하고 싶습니다. mergesort 만 안정적입니다. 즉, 동일한 키를 가진 값에 대해서는 순서가 변경되지 않습니다. 어떤 경우에는 이것이 바람직합니다.

그러나 실제 상황에서 대부분의 사람들은 좋은 평균 성능 만 필요로하며 퀵 정렬은 빠릅니다 ... 빠른 =)

모든 정렬 알고리즘에는 기복이 있습니다. 좋은 개요 는 정렬 알고리즘에 대한 Wikipedia 기사를 참조하십시오 .


7

에서 퀵에 위키 백과 항목 :

Quicksort는 또 다른 재귀 정렬 알고리즘 인 mergesort와 경쟁하지만 최악의 경우 Θ (nlogn) 실행 시간이라는 이점이 있습니다. Mergesort는 quicksort 및 heapsort와 달리 안정적인 정렬 방식이며, 연결된 스토리지 목록 및 디스크 스토리지 또는 네트워크 연결 스토리지와 같은 액세스가 느린 미디어에 저장된 매우 큰리스트에서 쉽게 작동하도록 조정할 수 있습니다. 퀵 정렬은 링크 된 목록에서 작동하도록 작성 될 수 있지만 랜덤 액세스 없이는 피벗을 잘못 선택하는 경우가 종종 있습니다. mergesort의 주요 단점은 배열에서 작업 할 때 최상의 경우 Θ (n) 보조 공간이 필요한 반면, 내부 분할 및 꼬리 재귀가있는 quicksort의 변형은 Θ (logn) 공간 만 사용한다는 것입니다. 연결된 목록에서 작업 할 때 mergesort는 작고 일정한 양의 보조 저장소 만 필요합니다.


7

뮤! Quicksort는 더 좋지 않습니다. mergesort와는 다른 종류의 응용 프로그램에 적합합니다.

Mergesort는 속도가 핵심이며 최악의 최악의 성능을 견딜 수 없으며 추가 공간을 사용할 수 있는지 고려할 가치가 있습니다. 1

당신은 그들이«둘 다 O (nlogn) […]»라고 말했습니다. 이것은 잘못이다. «Quicksort는 최악의 경우 약 n ^ 2 / 2 비교를 사용합니다.» 1 .

그러나 내 경험에 따르면 가장 중요한 속성은 명령형 패러다임으로 프로그래밍 언어를 사용할 때 정렬하는 동안 사용할 수있는 순차적 액세스를 쉽게 구현하는 것입니다.

1 Sedgewick, 알고리즘


추가 공간이 필요하지 않도록 Mergesort를 제자리에 구현할 수 있습니다. 예를 들어 이중 연결 목록이있는 경우 : stackoverflow.com/questions/2938495/…
lanoxx

6

Quicksort는 실제로 가장 빠른 정렬 알고리즘이지만 O (n2)만큼 성능이 떨어질 수있는 여러 병리학 적 사례가 있습니다.

힙 정렬은 O (n * ln (n))에서 실행되도록 보장되며 한정된 추가 스토리지 만 필요합니다. 그러나 실제 테스트에 대한 인용은 힙 정렬이 평균 퀵 정렬보다 상당히 느리다는 것을 보여줍니다.


5

Wikipedia의 설명은 다음과 같습니다.

내부 루프는 대부분의 아키텍처에서 효율적으로 구현 될 수 있고 대부분의 실제 데이터에서는 2 차 시간을 요구할 가능성을 최소화하는 디자인 선택이 가능하기 때문에 일반적으로 Quicksort는 다른 Θ (nlogn) 알고리즘보다 실제로 훨씬 빠릅니다. .

퀵소트

메르 조트

빠른 정렬 구현에는없는 Mergesort (Ω (n))에 필요한 스토리지 양에 문제가 있다고 생각합니다. 최악의 경우 알고리즘 시간은 동일하지만 mergesort에는 더 많은 스토리지가 필요합니다.


quicksort의 최악의 경우는 O (n), mergesort O (n log n)입니다. 따라서 큰 차이가 있습니다.
paul23

1
최악의 경우 quicksort is O (n ^ 2) – 내 이전 댓글을 편집 할 수없고 오타가 발생했습니다
paul23

@ paul23 댓글을 삭제할 수 있습니다. 또한 "대부분의 실제 데이터에서 2 차 시간을 요구할 가능성을 최소화하는 디자인을 선택할 수 있습니다"
Jim Balter

5

기존 우수 답변에 QuickSort가 최상의 경우에서 벗어날 때 수행하는 방법과 그 가능성에 대한 수학을 추가하고 싶습니다 .O (n ^ 2) 사례가 실제로 아닌 이유를 사람들이 조금 더 이해하는 데 도움이되기를 바랍니다. QuickSort의보다 복잡한 구현에 대한 우려.

임의 액세스 문제 외에 QuickSort의 성능에 영향을 줄 수있는 두 가지 주요 요인이 있으며, 둘 다 피벗을 정렬중인 데이터와 비교하는 방법과 관련이 있습니다.

1) 데이터에 적은 수의 키가 있습니다. 피벗 위치를 제외한 모든 값이 매번 한쪽에 배치되므로 동일한 값의 데이터 집합이 바닐라 2 파티션 QuickSort에서 n ^ 2 시간으로 정렬됩니다. 현대의 구현에서는 3 파티션 정렬 사용과 같은 방법으로이 문제를 해결합니다. 이러한 메소드는 O (n) 시간에 모두 동일한 값의 데이터 세트에서 실행됩니다. 따라서 이러한 구현을 사용하면 적은 수의 키를 가진 입력이 실제로 성능 시간을 향상 시키므로 더 이상 문제가되지 않습니다.

2) 매우 잘못된 피벗 선택은 최악의 경우 성능을 유발할 수 있습니다. 이상적인 경우 피벗은 항상 데이터의 50 %가 작고 데이터의 50 %가 더 커지므로 반복 할 때마다 입력이 절반으로 끊어집니다. 이것은 우리에게 n (n * logn) 시간 동안 n 개의 비교와 스왑 시간 log-2 (n) 재귀를 제공합니다.

비 이상적인 피벗 선택은 실행 시간에 어느 정도 영향을 줍니까?

데이터의 75 %가 피벗의 한쪽에 있도록 피벗이 일관되게 선택된 경우를 고려해 봅시다. 여전히 O (n * logn)이지만 이제 로그의 기본이 1 / 0.75 또는 1.33으로 변경되었습니다. 기준을 변경할 때의 성능 관계는 항상 log (2) / log (newBase)로 표시되는 상수입니다. 이 경우 상수는 2.4입니다. 따라서이 피벗 선택 품질은 이상적인 것보다 2.4 배 더 오래 걸립니다.

얼마나 빨리 악화 되나요?

피봇 선택이 (일관되게) 매우 나빠질 때까지 매우 빠르지 않습니다.

  • 한쪽면에서 50 % : (이상적인 경우)
  • 한쪽의 75 % : 2.4 배
  • 한쪽 90 % : 길이 6.6 배
  • 한쪽면의 95 % : 길이의 13.5 배
  • 한쪽 99 % : 69 배

한 쪽에서 100 %에 접근하면 실행의 로그 부분이 n에 접근하고 전체 실행이 무증상 O (n ^ 2)에 접근합니다.

QuickSort의 순진한 구현에서 정렬 된 배열 (첫 번째 요소 피벗) 또는 역 정렬 된 배열 (마지막 요소 피벗)과 같은 경우는 최악의 경우 O (n ^ 2) 실행 시간을 안정적으로 생성합니다. 또한 예측 가능한 피벗 선택을 사용하는 구현은 최악의 경우 실행을 생성하도록 설계된 데이터에 의해 DoS 공격을받을 수 있습니다. 현대의 구현에서는 정렬 전 데이터 랜덤 화, 무작위로 선택된 3 개 인덱스의 중앙값 선택 등과 같은 다양한 방법으로이를 피할 수 있습니다.

  • 작은 데이터 세트. 최악의 경우는 가능하지만 n (n ^ 2)도 작기 때문에 O (n ^ 2)는 치명적이지 않습니다.
  • 큰 데이터 세트. 이론상 최악의 경우는 가능하지만 실제로는 불가능합니다.

우리는 얼마나 끔찍한 성과를 볼 수 있습니까?

가능성은 거의 없습니다 . 일종의 5,000 개의 값을 고려해 봅시다.

우리의 가상 구현은 무작위로 선택된 3 개의 중앙값을 사용하여 피벗을 선택합니다. 25 % -75 % 범위의 피벗은 "양호한"것으로, 0 % -25 % 또는 75 % -100 % 범위에있는 피벗은 "나쁜"것으로 간주합니다. 3 개의 랜덤 인덱스의 중앙값을 사용하여 확률 분포를 보면 각 재귀는 11/16 확률로 좋은 피벗으로 끝날 수 있습니다. 수학을 단순화하기 위해 두 가지 보수적이고 가정적인 가정을 해보자.

  1. 좋은 피벗은 항상 정확히 25 % / 75 % 스플릿이며 2.4 * 이상에서 작동합니다. 우리는 이상적인 분할이나 25/75보다 나은 분할을 얻지 못합니다.

  2. 잘못된 피벗은 항상 최악의 경우이며 본질적으로 솔루션에 아무런 영향을 미치지 않습니다.

우리의 QuickSort 구현은 n = 10에서 멈추고 삽입 정렬로 전환 할 것이므로, 5,000 개의 값 입력을 분해하기 위해 22 개의 25 % / 75 % 피벗 파티션이 필요합니다. (10 * 1.333333 ^ 22> 5000) 또는 4990 최악의 피벗이 필요합니다. 우리가 어떤 시점 에서 22 개의 좋은 피벗을 축적 하면 정렬이 완료되므로 최악의 경우 나 그 근처의 물건에는 매우 운 이 필요합니다 . 실제로 n = 10으로 분류하는 데 필요한 22 개의 좋은 피벗을 달성하기 위해 88 개의 재귀가 필요한 경우 이는 이상적인 사례의 실행 시간의 약 4 배 또는 2.4 배일 것입니다. 우리가 하지 않을 가능성은 얼마나됩니까88 번의 재귀 후에 22 개의 좋은 피벗을 달성 됩니까?

이항 확률 분포 는 그에 대한 답을 얻을 수 있으며 그 답은 약 10 ^ -18입니다. 귀하의 사용자는 5,000 항목 정렬 실행하는 것이 볼 가능성이 그들보다 [SORT] 버튼을 클릭하는 데 걸리는 제 1 번개에 의해 공격 할 수 천번에 관한 것입니다 (n은 88이고, k는, p는 0.6875이다 21) 더 악화 10 * 이상적인 경우. 데이터 세트가 커질수록이 기회는 작아집니다. 다음은 몇 가지 배열 크기와 이에 상응하는 확률이 10 * 이상 이상일 수 있습니다.

  • 640 개 항목으로 구성된 배열 : 10 ^ -13 (60 회 시도 중 15 개의 피벗 포인트 필요)
  • 5,000 개 항목으로 구성된 배열 : 10 ^ -18 (88 회 시도 중 22 회 피봇 필요)
  • 40,000 개 항목으로 구성된 배열 : 10 ^ -23 (116 개 중 29 개의 좋은 피벗 필요)

이것은 현실보다 더 나쁜 두 가지 보수적 인 가정이 있다는 것을 기억하십시오. 따라서 실제 성능은 더 우수하고 나머지 확률의 균형은 이상에 가깝습니다.

마지막으로, 다른 사람들이 언급했듯이 재귀 스택이 너무 깊어지면 힙 정렬로 전환하여 터무니없는 경우조차도 제거 할 수 있습니다. 따라서 TLDR은 QuickSort를 제대로 구현 하기 위해 설계되었으며 O (n * logn) 시간 내에 실행이 완료되므로 최악의 경우 는 실제로 존재하지 않는다는 것 입니다.


1
"기존의 위대한 답변들"– 그것들은 무엇입니까? 찾을 수 없습니다.
Jim Balter

빠른 정렬의 변형은 파티션의 모든 항목에 대해 키의 상당 부분이 동일한 상황을 악용 할 수있는 방식으로 파티션에 대한 비교 기능을 알려줍니까?
supercat

4

왜 Quicksort가 좋은가요?

  • QuickSort는 최악의 경우 N ^ 2, 평균 NlogN의 경우를 사용합니다. 최악의 경우는 데이터가 정렬 될 때 발생합니다. 정렬을 시작하기 전에 임의 셔플을 통해이를 완화 할 수 있습니다.
  • QuickSort는 병합 정렬에 사용되는 추가 메모리를 사용하지 않습니다.
  • 데이터 집합이 크고 동일한 항목이있는 경우 3 방향 파티션을 사용하여 Quicksort의 복잡성을 줄입니다. 동일한 항목이 많을수록 정렬이 더 좋습니다. 모든 항목이 동일하면 선형 시간으로 정렬됩니다. [이것은 대부분의 라이브러리에서 기본 구현입니다]

Quicksort는 항상 Mergesort보다 낫습니까?

실제로는 아닙니다.

  • Mergesort는 안정적이지만 Quicksort는 그렇지 않습니다. 따라서 출력 안정성이 필요한 경우 Mergesort를 사용합니다. 많은 실제 응용 분야에서 안정성이 필요합니다.
  • 요즘 메모리는 싸다. 따라서 Mergesort에서 사용하는 추가 메모리가 응용 프로그램에 중요하지 않은 경우 Mergesort를 사용하는 데 아무런 해가 없습니다.

노트 : Java에서 Arrays.sort () 함수는 기본 데이터 유형에 Quicksort를 사용하고 객체 데이터 유형에 Mergesort를 사용합니다. 객체는 메모리 오버 헤드를 소비하므로 Mergesort에 약간의 오버 헤드를 추가해도 성능 관점에서는 문제가되지 않을 수 있습니다.

참조 : Coursera의 프린스턴 알고리즘 코스 3 주차 QuickSort 비디오보기


"정렬을 시작하기 전에 무작위 셔플을 통해이를 완화 할 수 있습니다."-아니, 그건 비쌀 것입니다. 대신 임의의 피벗을 사용하십시오.
짐 발터

4

Quicksort는 mergesort보다 낫지 않습니다. O (n ^ 2) (최악의 경우는 거의 발생하지 않음)를 사용하면 퀵 정렬은 잠재적으로 병합 정렬의 O (nlogn)보다 훨씬 느립니다. Quicksort는 오버 헤드가 적기 때문에 작은 n 및 느린 컴퓨터에서는 더 좋습니다. 그러나 오늘날 컴퓨터는 너무 빠르기 때문에 병합 정렬의 추가 오버 헤드는 무시할 수 있으며 매우 느린 퀵 정렬의 위험은 대부분의 경우 병합 정렬의 중요하지 않은 오버 헤드보다 훨씬 큽니다.

또한 mergesort는 동일한 키를 가진 항목을 원래 순서대로 유지하며 유용한 속성입니다.


2
두 번째 문장은 "... mergesort가 잠재적으로 ... mergesort보다 훨씬 느리다"고 말합니다. 첫 번째 참조는 아마도 퀵 정렬이어야합니다.
Jonathan Leffler 2018 년

병합 정렬은 병합 알고리즘이 안정적인 경우에만 안정적입니다. 이것은 보장되지 않습니다.
Clearer

@Clearer <=가 아닌 비교에 사용되는 경우 보장 되며 <, 그럴 이유가 없습니다.
Jim Balter

@ JimBalter 불안정한 병합 알고리즘을 쉽게 만들 수 있습니다 (예를 들어, 빠른 역할은 그 역할을 수행합니다). 많은 경우에 빠른 정렬이 병합 정렬보다 빠른 이유는 오버 헤드 감소 때문 이 아니라 빠른 정렬이 데이터에 액세스하는 방식 때문입니다. 이는 표준 병합 정렬 보다 캐시에 훨씬 친숙합니다.
더 명확한

@Clearer quicksort는 병합 정렬이 아닙니다 ... 응답 한 Dec 21 '14 진술은 병합 정렬과 안정적인 정렬에 관한 것입니다. quicksort와 더 빠른 것이 귀하의 의견이나 내 응답과 전혀 관련이 없습니다. 나를위한 토론의 끝.
짐 발터

3

대답은 기본 값에 대해 DualPivotQuickSort로 가져온 변경 사항에 대한 빠른 정렬로 약간 기울어집니다. java.util.Arrays 를 정렬하기 위해 JAVA 7 에서 사용됩니다.

It is proved that for the Dual-Pivot Quicksort the average number of
comparisons is 2*n*ln(n), the average number of swaps is 0.8*n*ln(n),
whereas classical Quicksort algorithm has 2*n*ln(n) and 1*n*ln(n)
respectively. Full mathematical proof see in attached proof.txt
and proof_add.txt files. Theoretical results are also confirmed
by experimental counting of the operations.

JAVA7 구현은 여기에서 찾을 수 있습니다. http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/7-b147/java/util/Arrays.java

DualPivotQuickSort에 대한 더 굉장 읽기 - http://permalink.gmane.org/gmane.comp.java.openjdk.core-libs.devel/2628


3

병합 정렬에서 일반적인 알고리즘은 다음과 같습니다.

  1. 왼쪽 하위 배열 정렬
  2. 오른쪽 하위 배열 정렬
  3. 정렬 된 2 개의 하위 배열 병합

최상위 수준에서 2 개의 정렬 된 하위 배열을 병합하려면 N 요소를 처리해야합니다.

한 단계 아래에서 3 단계를 반복 할 때마다 N / 2 요소를 다루지 만이 과정을 두 번 반복해야합니다. 따라서 여전히 2 * N / 2 == N 요소를 다루고 있습니다.

그보다 한 수준 아래에서는 4 * N / 4 == N 요소 등을 병합합니다. 재귀 스택의 모든 깊이에는 해당 깊이에 대한 모든 호출에서 동일한 수의 요소가 병합됩니다.

빠른 정렬 알고리즘을 대신 고려하십시오.

  1. 피봇 포인트 선택
  2. 모든 작은 요소가 왼쪽에 있고 큰 요소가 오른쪽에 있도록 배열의 올바른 위치에 피벗 포인트를 배치하십시오.
  3. 왼쪽 subarray를 정렬
  4. 오른쪽 subarray를 정렬

최상위 레벨에서는 N 크기의 배열을 처리합니다. 그런 다음 하나의 피벗 점을 선택하고 올바른 위치에 놓은 다음 나머지 알고리즘에 대해 완전히 무시할 수 있습니다.

그보다 한 수준 아래에서는 결합 된 크기가 N-1 인 2 개의 하위 배열을 처리합니다 (즉, 이전 피벗 점 빼기). 각 하위 배열에 대해 피벗 점을 선택하면 최대 2 개의 추가 피벗 점이 나타납니다.

그보다 한 수준 아래에서는 위와 같은 이유로 크기가 N-3 인 4 개의 하위 배열을 처리합니다.

그런 다음 N-7 ... 그런 다음 N-15 ... 그런 다음 N-32 ...

재귀 스택의 깊이는 거의 동일합니다 (logN). 병합 정렬을 사용하면 재귀 스택의 각 수준에서 항상 N 요소 병합을 처리합니다. 그러나 빠른 정렬을 사용하면 스택을 내려 갈수록 처리하는 요소의 수가 줄어 듭니다. 예를 들어, 재귀 스택의 중간 깊이를 보면 처리하는 요소의 수는 N-2 ^ ((logN) / 2)) == N-sqrt (N)입니다.

면책 조항 : 병합 정렬에서는 배열을 매번 정확히 동일한 청크 2 개로 나누기 때문에 재귀 깊이는 정확히 logN입니다. 빠른 정렬에서는 피벗 지점이 정확히 배열의 중간에 있지 않을 수 있으므로 재귀 스택의 깊이는 logN보다 약간 더 클 수 있습니다. 이 요소와 위에서 설명한 요소가 실제로 알고리즘의 복잡성에서 얼마나 큰 역할을하는지 알아보기 위해 수학을 수행하지 않았습니다.


피벗이 다음 단계에서 분류의 일부가 아니기 때문에 QS가 더 성능이 좋은 이유는 아닙니다. 추가 통찰력에 대해서는 다른 답변을 참조하십시오.
짐 발터

@JimBalter 어떤 "다른 답변"을 말하고 있습니까? 최고 답변은 QS가 "추가 공간이 거의 필요하지 않고 캐시 지역성이 우수하다는 것"이라고 말하지만 그 이유와 인용을 설명하지는 않습니다. 두 번째 답변은 단순히 병합 정렬이 더 큰 데이터 세트에
적합하다는 것입니다

QS의 성능이 뛰어난 이유에서 작동 방식에 대한 기본 사실을 설명하는 데 이르기까지 목표를 추진하고 있습니다. 다른 질문에 대한 답변은 다음과 같습니다. stackoverflow.com/questions/9444714/… ... 나는 그것이 당신에게 충분하기를 바랍니다. 더 이상 답변하지 않겠습니다.
Jim Balter 2018 년

3

병합 정렬과 달리 빠른 정렬은 보조 공간을 사용하지 않습니다. Merge Sort는 보조 공간 O (n)을 사용합니다. 그러나 병합 정렬은 최악의 시간 복잡도 O (nlogn)를 갖는 반면 빠른 정렬의 최악의 복잡도는 배열이 이미 정렬되어있을 때 발생하는 O (n ^ 2)입니다.


아니요, QuickSort의 최악의 경우는 첫 번째 또는 마지막 항목을 피벗으로 사용하지 않는 한 배열이 이미 정렬되어 있으면 발생하지 않지만 아무도하지 않습니다.
Jim Balter

2

Quicksort는 평균 대소 문자 복잡성이 더 우수하지만 일부 응용 프로그램에서는 잘못된 선택입니다. Quicksort는 서비스 거부 공격에 취약합니다. 공격자가 입력을 정렬하도록 선택할 수 있으면 최악의 시간 복잡도 o (n ^ 2)를 취하는 집합을 쉽게 구성 할 수 있습니다.

Mergesort의 평균 대소 문자 복잡성과 최악의 대소 문자 복잡성은 동일하므로 동일한 문제가 발생하지 않습니다. 이 merge-sort 속성은 실시간 시스템에 탁월한 선택입니다. 정확하게 실행하는 병리학 적 사례가 없기 때문에 더 느리게 실행됩니다.

이러한 이유로 저는 Quicksort보다 Mergesort보다 더 큰 팬입니다.


2
Quicksort의 평균 케이스 복잡도는 어떻게 향상됩니까? 둘 다 O (nlgn)입니다. 공격자가 정렬 알고리즘에 입력을 제공하지 않을 것이라고 주장하지만, 모호성으로 보안을 가정하지 않기 위해 할 수 있다고 가정합니다. n ^ 2 실행 시간이 nlgn보다 나쁘지만 한 번의 공격으로 웹 서버가 충돌하는 것만으로는 충분하지 않습니다. 실제로 모든 웹 서버는 DDOS 공격에 취약하고 공격자가 모든 호스트의 네트워크, 모든 TCP SYN 플러딩을 사용할 가능성이 높기 때문에 DOS 인수는 거의 null입니다.
CaTalyst.X

"Quicksort는 평균 사례 복잡성이 더 우수합니다."
Jim Balter

2

MergeSort의 최악은 n (log2n) -n + 1이며, n이 2 ^ k (이미 증명 했음)이면 정확합니다. 그리고 어떤 n에 대해서도 (n lg n-n + 1) 및 (n lg n + n + O (lg n)). 그러나 quickSort의 경우 가장 좋은 방법은 nlog2n입니다 (n은 2 ^ k와 같습니다). Mergesort를 quickSort로 나누면 n이 무한 할 때 1과 같습니다. MergeSort의 최악의 경우가 QuickSort의 최상의 경우보다 나은 것처럼, 왜 우리는 quicksort를 사용합니까? 그러나 MergeSort가 제자리에 있지 않고 2n memeroy 공간이 필요하다는 것을 기억하십시오. 한마디로 MergeSort는 theroy의 quicksort보다 훨씬 더 혼란 스럽지만 실제로는 memeory space, array copy의 비용, 합병은 빠른 정렬보다 느립니다. 랜덤 클래스에 의해 Java에서 1000000 자리를 얻은 실험,그리고 mergesort에 의해 2610ms, quicksort에 의해 1370ms가 걸렸습니다.


2

빠른 정렬은 최악의 경우 O (n ^ 2)이지만 평균적인 경우는 지속적으로 병합 정렬을 수행합니다. 각 알고리즘은 O (nlogn)이지만 Big O에 대해 이야기 할 때는 복잡성이 낮은 요소를 제외한다는 것을 기억해야합니다. 빠른 정렬은 일정한 요소가있을 때 병합 정렬보다 크게 개선되었습니다.

병합 정렬에는 O (2n) 메모리도 필요하지만 빠른 정렬을 수행 할 수 있습니다 (O (n) 만 필요). 이것이 빠른 정렬이 병합 정렬보다 일반적으로 선호되는 또 다른 이유입니다.

추가 정보 :

피봇을 잘못 선택하면 최악의 빠른 정렬이 발생합니다. 다음 예제를 고려하십시오.

[5, 4, 3, 2, 1]

피벗이 그룹에서 가장 작은 숫자 또는 가장 큰 숫자로 선택되면 빠른 정렬이 O (n ^ 2)로 실행됩니다. 목록의 최대 또는 최소 25 %에있는 요소를 선택할 확률은 0.5입니다. 그것은 알고리즘에게 좋은 피봇이 될 확률을 0.5로 준다. 일반적인 피벗 선택 알고리즘 (예 : 임의 요소 선택)을 사용하면 피벗을 선택할 때마다 좋은 피벗을 선택할 수 있습니다. 큰 크기의 컬렉션의 경우 항상 불량 피벗을 선택할 확률은 0.5 * n입니다. 이 확률을 기반으로 빠른 정렬은 평균 (및 일반적인) 경우에 효율적입니다.


O (2n) == O (n). 올바른 설명은 Mergesort가 O (n) 추가 메모리를 필요로한다는 것입니다 (보다 구체적으로, n / 2 보조 메모리가 필요합니다). 그리고 이것은 링크 된 목록에는 해당되지 않습니다.
Jim Balter

@JimBalter 선생님, 질문에 대한 답변으로 그들의 성과에 대해 훌륭하고 가치있는 아이디어를 우리와 공유해 주시겠습니까? 미리 감사드립니다.
snr

2

이것은 꽤 오래된 질문이지만 최근에 두 가지를 모두 다루었으므로 2c가 있습니다.

병합 정렬은 평균 ~ N log N 비교에 필요합니다. 이미 (거의) 정렬 된 정렬 된 배열의 경우 1/2 N log N으로 낮아집니다. 병합하는 동안 항상 (왼쪽) 부분을 1/2 N 번 선택한 다음 오른쪽 1/2 N 요소를 복사하기 때문입니다. 또한 이미 정렬 된 입력이 프로세서의 분기 예측기를 빛나게하지만 거의 모든 분기를 올바르게 추측하여 파이프 라인 정지를 방지한다고 추측 할 수 있습니다.

평균 빠른 정렬은 ~ 1.38 N log N 비교가 필요합니다. 비교 측면에서 이미 정렬 된 배열의 이점은 없지만 (그러나 스왑 측면과 CPU 내부의 분기 예측 측면에서) 이점이 있습니다.

상당히 현대적인 프로세서에 대한 나의 벤치 마크는 다음과 같습니다.

비교 함수가 콜백 함수 인 경우 (qsort () libc 구현에서와 같이) quicksort는 무작위 입력에서 15 %, 64 비트 정수에 대해 이미 정렬 된 배열의 경우 30 %만큼 mergesort보다 느립니다.

반면에 비교가 콜백이 아닌 경우 내 경험에 따르면 quicksort는 mergesort보다 최대 25 % 성능이 뛰어납니다.

그러나 (대형) 배열에 고유 값이 거의없는 경우 병합 정렬은 모든 경우에 퀵 정렬보다 먼저 시작됩니다.

아마도 결론은 다음과 같습니다. 비교가 비싸다면 (예를 들어 콜백 함수, 문자열 비교, 구조의 많은 부분을 비교하여 차이를 만들기 위해 제 2/3 "if"에 도달하는 경우)-기회는 더 나을 것입니다 병합 정렬 간단한 작업의 경우 빠른 정렬이 더 빠릅니다.

즉, Quicksort는 N ^ 2가 될 수 있지만 Sedgewick은 좋은 무작위 구현으로 인해 N ^ 2보다 번개로 인해 컴퓨터가 정렬을 수행 할 가능성이 더 높다고 주장합니다.-Mergesort는 추가 공간이 필요합니다


비교가 저렴한 경우 qsort가 정렬 된 입력의 경우에도 mergesort를 능가합니까?
Eonil

2

두 정렬 알고리즘을 모두 실험했을 때 재귀 호출 수를 계산하여 quicksort는 mergesort보다 재귀 호출 수가 줄었습니다. 빠른 정렬에는 피벗이 있고 다음 재귀 호출에는 피벗이 포함되지 않기 때문입니다. 이렇게하면 quicksort가 mergesort보다 재귀적인 기본 사례에 더 빨리 도달 할 수 있습니다.


피벗은 QS의 재귀 호출 수가 적은 이유와는 아무 관련이 없습니다. QS 재귀의 절반이 테일 재귀이기 때문에 제거 할 수 있기 때문입니다.
Jim Balter

2

인터뷰에서 자주 묻는 질문은 병합 정렬의 최악의 경우 성능에도 불구하고 빠른 정렬이 병합 정렬보다 더 나은 것으로 간주되는데, 특히 큰 입력의 경우입니다. 퀵 정렬이 더 나은 특정 이유가 있습니다.

1- 보조 공간 : 빠른 정렬은 내부 정렬 알고리즘입니다. 적절한 정렬은 정렬을 수행하기 위해 추가 저장 공간이 필요하지 않음을 의미합니다. 반면에 병합 병합은 정렬 된 배열을 병합하기 위해 임시 배열이 필요하므로 제자리에 없습니다.

2- 최악의 경우 :O(n^2) 무작위 퀵 정렬을 사용하면 최악의 퀵 정렬 을 피할 수 있습니다. 올바른 피벗을 선택하면 높은 확률로 쉽게 피할 수 있습니다. 올바른 피벗 요소를 선택하여 평균 사례 동작을 확보하면 성능이 향상되고 병합 정렬만큼 효율적이됩니다.

3- 참조 지역 : 특히 Quicksort는 우수한 캐시 지역을 보여 주므로 가상 메모리 환경과 같은 많은 경우 병합 정렬보다 빠릅니다.

4- 꼬리 재귀 : QuickSort는 꼬리 재귀이지만 병합 정렬은 아닙니다. 꼬리 재귀 함수는 재귀 호출이 함수에 의해 마지막으로 실행되는 함수입니다. 테일 재귀 함수는 테일 재귀가 컴파일러에 의해 최적화 될 수 있기 때문에 테일 비 재귀 함수보다 나은 것으로 간주됩니다.


1

둘 다 동일한 복잡성 클래스에 있지만 둘 다 동일한 런타임을 의미하지는 않습니다. Quicksort는 일반적으로 mergesort보다 빠릅니다. 단지 엄격한 구현을 코딩하는 것이 쉽고 작업이 더 빨라질 수 있기 때문입니다. 일반적으로 Quicksort가 사람들이 mergesort 대신 사용하는 것보다 빠릅니다.

하나! 나는 개인적으로 종종 quicksort가 제대로 작동하지 않을 때 mergesort 또는 mergesort로 저하되는 quicksort 변형을 사용합니다. 생각해 내다. Quicksort는 평균 O (n log n)입니다 . 최악의 경우는 O (n ^ 2)입니다! Mergesort는 항상 O (n log n)입니다. 실시간 성능 또는 응답 성이 필수이고 입력 데이터가 악성 소스에서 제공 될 수있는 경우 일반 퀵 정렬을 사용하지 않아야합니다.


1

모든 것이 똑같습니다. 대부분의 사람들이 가장 편리하게 사용할 수있는 것을 사용하기를 기대하며 qsort (3) 인 경향이 있습니다. 해당 정렬 이외의 빠른 정렬은 배열에서 매우 빠른 것으로 알려져 있습니다. 병합 정렬은 목록의 일반적인 선택입니다.

내가 궁금한 것은 기수 또는 버킷 정렬이 보기 드문 이유 입니다. 그들은 적어도 연결된 목록에서 O (n)이며 키를 서수로 변환하는 방법입니다. (문자열과 수레는 잘 작동합니다.)

나는 그 이유가 컴퓨터 과학을 가르치는 방법과 관련이 있다고 생각합니다. 알고리즘 분석에서 강사에게 실제로 O (n log (n))보다 더 빠르게 정렬 할 수 있음을 보여 주어야했습니다. (그는 O (n log (n))보다 빠르게 정렬을 비교할 수 없다는 증거를 가지고있었습니다 .

다른 소식으로, 수레는 정수로 정렬 될 수 있지만 나중에 음수를 돌려야합니다.

편집 : 사실, 여기 수레로서의 정수 정렬 훨씬 더 사악한 방법 http://www.stereopsis.com/radix.html을 . 비트 정렬 트릭은 실제로 사용하는 정렬 알고리즘에 관계없이 사용할 수 있습니다.


1
나는 기수 종류를 보았습니다. 그러나 올바르게 분석하면 런타임이 입력 요소 수보다 많기 때문에 O (n) 이 아니기 때문에 사용하기가 어렵습니다 . 일반적으로 기수 정렬이 입력에 대해 효율적이어야한다는 강력한 예측을하는 것은 매우 어렵습니다.
Konrad Rudolph

그것은 O (N) 여기서, N은 IS 전체 요소의 크기를 포함하여, 입력 크기이다. 구현 할 수 있으므로 많은 0으로 채워야하지만 비교에 나쁜 구현을 사용하는 것은 말이되지 않습니다. (즉, 구현, YMMV 어려울 수 있습니다 말했다.)
앤더스 Eurenius

GNU libc를 사용하는 경우 qsort병합 정렬입니다.
Jason Orendorff 2009

정확히 말해서, 필요한 임시 메모리를 할당 할 수 없다면 병합 정렬입니다. cvs.savannah.gnu.org/viewvc/libc/stdlib/…
Jason Orendorff 2009

1

빠른 대 병합 정렬에 작은 추가.

또한 정렬 항목의 종류에 따라 달라질 수 있습니다. 평면 메모리의 정수를 비교하는 것과 같이 항목, 스왑 및 비교에 대한 액세스가 간단한 작업이 아닌 경우 병합 정렬이 선호되는 알고리즘 일 수 있습니다.

예를 들어, 원격 서버에서 네트워크 프로토콜을 사용하여 항목을 정렬합니다.

또한 "링크 된 목록"과 같은 사용자 지정 컨테이너에서는 빠른 정렬의 이점이 없습니다.
1. 연결된 목록에서 정렬을 병합하고 추가 메모리가 필요하지 않습니다. 2. 빠른 정렬의 요소에 대한 액세스는 순차적이지 않습니다 (메모리에서).


0

빠른 정렬은 내부 정렬 알고리즘이므로 배열에 더 적합합니다. 반면에 병합 정렬은 O (N)을 추가로 저장해야하며 연결된 목록에 더 적합합니다.

배열과 달리 좋아하는 목록에서 O (1) 공간과 O (1) 시간을 사용하여 중간에 항목을 삽입 할 수 있으므로 병합 정렬의 병합 작업을 추가 공간없이 구현할 수 있습니다. 그러나 배열에 추가 공간을 할당 및 할당 해제하면 병합 정렬 런타임에 악영향을 미칩니다. 병합 정렬은 많은 임의의 메모리 액세스없이 순차적으로 데이터에 액세스 할 때 링크 된 목록을 선호합니다.

반면에 빠른 정렬은 많은 임의의 메모리 액세스가 필요하며 배열을 사용하면 링크 된 목록에 필요한 순회없이 메모리에 직접 액세스 할 수 있습니다. 또한 배열에 사용될 때 빠른 정렬은 배열이 메모리에 연속적으로 저장되기 때문에 좋은 참조 위치를 갖습니다.

두 정렬 알고리즘의 평균 복잡도는 모두 O (NlogN)이지만 일반적으로 일반 작업을 수행하는 사람들은 스토리지를 위해 배열을 사용하므로 빠른 정렬이 선택한 알고리즘이어야합니다.

편집 : 방금 병합 정렬 최악 / 최고 / avg 사례는 항상 nlogn이지만 빠른 정렬은 n2 (요소가 이미 정렬 된 경우 최악의 경우)에서 nlogn (avg / best 경우 피벗이 항상 배열을 두 개로 나누는 경우) 반).


0

시간과 공간의 복잡성을 모두 고려하십시오. 병합 정렬의 경우 : 시간 복잡성 : O (nlogn), 공간 복잡성 : O (nlogn)

빠른 정렬 : 시간 복잡도 : O (n ^ 2), 공간 복잡도 : O (n)

이제 둘 다 각각 하나의 장면에서 승리합니다. 그러나 임의 피벗을 사용하면 거의 항상 빠른 정렬의 시간 복잡성을 O (nlogn)로 줄일 수 있습니다.

따라서 많은 응용 프로그램에서 병합 정렬 대신 빠른 정렬이 선호됩니다.


-1

c / c ++ 토지에서 stl 컨테이너를 사용하지 않을 때는 quicksort를 사용하는 경향이 있습니다. 왜냐하면 quicksort는 런타임에 내장되어 있지만 mergesort는 그렇지 않기 때문입니다.

그래서 나는 많은 경우에 그것이 가장 저항이 적은 경로라고 믿습니다.

또한 전체 데이터 세트가 작업 세트에 맞지 않는 경우 빠른 정렬로 성능이 훨씬 향상 될 수 있습니다.


3
실제로, 그것이 당신이 말하는 qsort () 라이브러리 함수라면, 그것은 퀵 정렬로 구현되거나 구현되지 않을 수 있습니다.
Thomas Padron-McCarthy

3
콘라드,이 문제에 대해 약간 아쉬운 점을 알려 드리지만, 그 보증은 어디서 찾을 수 있습니까? ISO C 표준 또는 C ++ 표준에서 찾을 수 없습니다.
Thomas Padron-McCarthy

2
GNU libc qsort는 요소의 수가 엄청나거나 임시 메모리를 할당 할 수 없으면 병합 정렬입니다. cvs.savannah.gnu.org/viewvc/libc/stdlib/…
Jason Orendorff 2009

-3

그 이유 중 하나는 더 철학적입니다. Quicksort는 Top-> Down 철학입니다. n 개의 요소를 정렬하면 n이 있습니다! 가능성. 상호 배타적 인 m & nm의 2 개의 파티션으로 여러 가능성이 줄어 듭니다. 미디엄! * (nm)! n보다 몇 차 더 작습니다! 혼자. 5를 상상해보십시오! vs 3! * 2 !. 5! 각각 2 & 3의 2 개의 파티션보다 10 배 더 많은 가능성이 있습니다. 900K! * 100K에 100 만 계승으로 추정합니다! vs. 따라서 범위 또는 파티션 내에서 순서를 설정하는 것에 대해 걱정하는 대신 파티션에서 더 넓은 레벨로 순서를 설정하고 파티션 내에서 가능성을 줄이십시오. 파티션 자체가 상호 배타적이지 않은 경우 범위 내에서 이전에 설정된 순서는 나중에 교란됩니다.

병합 정렬 또는 힙 정렬과 같은 상향식 접근 방식은 미세한 수준에서 일찍 비교를 시작하는 작업자 또는 직원의 접근 방식과 같습니다. 그러나이 순서는 나중에 그들 사이의 요소가 발견되는 즉시 사라질 것입니다. 이러한 접근 방식은 매우 안정적이며 예측 가능하지만 일정량의 추가 작업을 수행합니다.

빠른 정렬은 처음에는 어떤 순서에 대해서도 신경 쓰지 않고 순서에 관계없이 광범위한 기준을 충족시키는 관리 접근 방식과 같습니다. 그런 다음 정렬 된 세트를 얻을 때까지 파티션이 좁아집니다. Quicksort의 실제 과제는 정렬 할 요소에 대해 아는 것이 없을 때 어둠 속에서 파티션이나 기준을 찾는 것입니다. 그렇기 때문에 중앙값을 찾기 위해 약간의 노력을 기울이거나 임의 또는 임의의 "관리적"접근 방식으로 1을 선택해야합니다. 완벽한 중앙값을 찾으려면 상당한 노력이 필요하며 어리석은 상향식 접근법으로 다시 연결됩니다. 따라서 Quicksort는 임의의 피벗을 선택하고 중간에 어딘가에 있거나 3, 5의 중간 값을 찾거나 더 나은 중간 값을 찾기 위해 더 많은 것을 찾으면서도 완벽하지는 않을 것이라고 생각합니다. 처음 주문할 때 시간을 낭비하지 마십시오. 운이 좋거나 때로는 중간 값을 얻지 못하고 기회를 잡을 때 n ^ 2로 저하되면 잘되는 것처럼 보입니다. 어떤 방식 으로든 데이터는 무작위입니다. 권리. 그래서 나는 퀵 정렬의 하향식 논리적 접근 방식에 더 동의하고 초기에 저장하는 피벗 선택 및 비교에 걸릴 확률은 꼼꼼하고 철저한 안정적인 상향 조정 방법보다 더 많은 시간 동안 더 잘 작동하는 것으로 보입니다. 병합 정렬. 그러나 이전에 절약 한 비교는 병합 정렬과 같은 세심하고 철저한 안정적인 상향식 접근 방식보다 더 많은 시간 동안 작동하는 것으로 보입니다. 그러나 이전에 절약 한 비교는 병합 정렬과 같은 세심하고 철저한 안정적인 상향식 접근 방식보다 더 많은 시간 동안 작동하는 것으로 보입니다. 그러나


퀵 정렬은 피벗 선택의 임의성으로부터 이점을 얻습니다. 랜덤 피벗은 자연스럽게 50:50 분할을 향하는 경향이 있으며 극단적 인 것 중 하나를 향해 일관성이 없을 것입니다. nlogn의 상수 인자는 평균 파티셔닝이 60-40 또는 70-30이 될 때까지 상당히 낮습니다.
Winter Melon

이건 말도 안돼 quicksort는 "철학"이 아닌 성능 때문에 사용됩니다. "순서는 상실됩니다"라는 주장은 단순히 거짓입니다.
Jim Balter
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.