주로 정렬 된 데이터에 가장 적합한 정렬 알고리즘은 무엇입니까? [닫은]


174

주로 정렬 된 데이터에 가장 적합한 정렬 알고리즘은 무엇입니까?


컨텍스트 부족으로 추측-중간 결과를 디스크에 흘릴 필요없이 메모리 내 정렬을 요구하고 있습니까?
Jonathan Leffler

1
이러한 애니메이션에 따르면 삽입 정렬은 대부분 정렬 된 데이터에서 가장 잘 작동합니다.
dopple April

답변:


259

애니메이션 GIF 를 보는 매우 과학적인 방법을 바탕으로 Insertion 및 Bubble 정렬이 좋은 후보라고 말합니다.


19
그건 그렇고, kudos와 +1
21시 44 분

5
버블 정렬은 끔찍합니다. 항상 O (n ^ 2)입니다. 적어도 당신의 대답에서 그것을 꺼내십시오.
jjnguy

79
jjnguy, 그건 명백한 잘못이다. 알고리즘 클래스를 다시 받아야한다고 생각합니다. 거의 정렬 된 데이터 (적응 형 사례)에서는 O (N)입니다. 그러나 데이터를 두 번 통과하면 거의 정렬 된 데이터에 대해 삽입이 1 만 수행되므로 삽입이 승자가됩니다. 거품은 여전히 ​​좋다
mmcdole

3
데이터가 거의 정렬되지 않으면 성능이 크게 저하됩니다. 나는 아직도 개인적으로 사용하지 않을 것입니다.
Blorgbeard는

5
내가 시도했을 때 그 링크가 끊어졌습니다. 대신 이것을보십시오 : sorting-algorithms.com
Michael La Voie

107

몇 개의 항목 만 => 삽입 열

항목은 대부분 이미 정렬되어 있습니다 => INSERTION SORT

최악의 시나리오에 대한 우려 => HEAP SORT

좋은 평균 사례 결과에 관심이 있음 => QUICKSORT

조밀 한 우주에서 아이템을 추출 함 => 버킷 소트

가능한 적은 코드를 작성하고 싶음 => 삽입 SORT


1
그것은 내가 찾던 정답입니다. 책을 읽었지만 특정 경우에 알고리즘을 선택하는 것에 대한 명확한 설명을 찾지 못하는 것 같습니다.이를 자세히 설명하거나 링크를 전달하여 개로 들어갈 수 있습니까? 조금 더? 감사합니다
심란 카 우르

9
"데이터는 이미 다른 기준으로 정렬되었습니다 => MERGE SORT"
Jim Hunziker

30

팀 소트

Timsort 는 " 다양한 부분 정렬 배열 (lg (N!) 비교가 필요하지 않고 N-1만큼 적음)의 초자연적 성능 "을 갖춘 "적응 형의 안정적이고 자연스러운 병합 정렬 "입니다. 파이썬 내장sort()이 알고리즘을 한동안 사용해 왔으며 결과는 좋았습니다. 특히 실제 데이터 세트에서 발생하는 입력에서 부분적으로 정렬 된 하위 시퀀스를 감지하고 활용하도록 설계되었습니다. 실제로는 목록에서 항목을 바꾸는 것보다 비교가 훨씬 더 비싼 경우가 종종 있습니다. 일반적으로 포인터를 바꾸는 것만으로도 종종 팀 소트를 탁월한 선택으로 만듭니다. 그러나 비교가 항상 매우 저렴하다는 것을 알고 있다면 (예를 들어 32 비트 정수를 정렬하기 위해 장난감 프로그램 작성) 성능이 더 좋은 다른 알고리즘이 있습니다. timsort를 이용하는 가장 쉬운 방법은 물론 Python을 사용하는 것이지만 Python은 오픈 소스이므로 코드를 빌릴 수도 있습니다. 대안 적으로, 위의 설명은 자신의 구현을 작성하기에 충분한 세부 사항을 포함합니다.


16
log (n!)은 Ο (n * log (n))이므로 "초자연적"이 아닙니다.
jfs

다음은 JDK7
Tim

log (n!)는 빠르지 않습니다. wolframalpha.com/input/?i=plot[log(N)! , {N, 0,1000}]
베 루즈

9
@JF Sebastian : timsort는 lg(n!)거의 정렬 된 배열의 비교 보다 훨씬 빠릅니다 O(n)! | @behrooz : 없음 비교 정렬보다 더 나은의 평균 사건을 가질 수 O(n log n)lg(n!) 이다 O(n log n). 따라서 팀소 르트의 최악의 경우는 다른 비교 방식보다 최악의 경우가 아닙니다. 또한 가장 좋은 경우는 다른 비교 정렬보다 낫거나 같습니다.
Artelius

3
Timsort는 최악의 경우 여전히 O (nlogn)이지만 좋은 경우는 매우 기쁘게 생각합니다. 다음은 일부 그래프와 비교 한 것입니다. stromberg.dnsalias.org/~strombrg/sort-comparison Cython의 timsort는 Python이 C의 timsort에 내장 된 것만 큼 빠르지는 않았습니다.
user1277476

19

다음과 같은 동작으로 삽입 정렬 :

  1. k슬롯의 각 요소 에 대해 1..n먼저 여부를 확인하십시오 el[k] >= el[k-1]. 그렇다면 다음 요소로 이동하십시오. (첫 번째 요소는 생략하십시오.)
  2. 그렇지 않은 경우 요소에서 이진 검색 1..k-1을 사용하여 삽입 위치를 확인한 다음 요소를 스쿠 트하십시오. (경우에만이 작업을 수행 할 수있는 k>TT, 작은 일부 임계 값이 k이 과잉이다.)

이 방법은 비교 횟수를 최소화합니다.


정렬되지 않은 요소의 수가 매우 적 으면 (예 : 하나 또는 두 개) 거품 정렬이이를 이길 수 있다고 생각하지만 일반적으로 이것은 아마도 최상의 솔루션으로 생각납니다.
Sol

1 단계로 인해 이미 정렬 된 모든 요소에 대해 정확히 하나의 비교 및 ​​제로 데이터 이동이 있으며 이는 분명히 최선의 방법입니다. 2 단계는 개선 할 수있는 것이지만 거품은 같은 수의 요소를 이동하고 impl에 따라 더 많은 비교를 할 수 있습니다.
Jason Cohen

사실, 추가 생각에 거품 정렬이 생각보다 강하다고 생각합니다. 실제로 상당히 까다로운 질문입니다. 예를 들어, 마지막으로해야 할 요소가 첫 번째 요소를 제외하고 목록이 완전히 정렬 된 경우 버블 정렬은 설명하는 것보다 훨씬 뛰어납니다.
Sol

나는 이것을 구현하려고 시도했지만 요소를 삽입하기 위해 전체 블록을 이동해야하기 때문에 이진 검색은 크게 개선되지 않았습니다. 따라서 2xrange 대신 range + logb (range)를 얻습니다.
이번

11

내성적 인 정렬을 시도하십시오. http://en.wikipedia.org/wiki/Introsort

그것은 퀵 정렬을 기반으로하지만, 퀵 정렬이 거의 정렬 된 목록에 대해 최악의 행동을 피합니다.

이 정렬 알고리즘은 빠른 정렬이 최악의 모드로 전환되는 경우를 감지하고 힙 정렬 또는 병합 정렬로 전환하는 경우입니다. 거의 정렬되지 않은 파티션은 기본이 아닌 일부 파티션 방법으로 감지되며 작은 파티션은 삽입 정렬을 사용하여 처리됩니다.

더 많은 코드와 복잡성으로 인해 모든 주요 정렬 알고리즘을 최대한 활용할 수 있습니다. 또한 데이터의 모양에 관계없이 최악의 상황이 발생하지 않도록 보장 할 수 있습니다.

C ++ 프로그래머라면 std :: sort 알고리즘을 확인하십시오. 내부적으로 이미 내성 정렬을 사용하고있을 수 있습니다.


7

Splaysort스플레이 트리를 기반으로하는 모호한 정렬 방법입니다 적응 이진 트리 유형 인 입니다. Splaysort는 부분적으로 정렬 된 데이터뿐만 아니라 부분적으로 역 분류 된 데이터 또는 실제로 어떤 종류의 기존 순서를 가진 데이터에도 적합합니다. 일반적인 경우에는 O (nlogn)이고 데이터가 어떤 방식으로 (정방향, 역방향, 오르간 파이프 등) 정렬 된 경우에는 O (n)입니다.

삽입 정렬에 비해 큰 장점은 데이터가 전혀 정렬되지 않은 경우 O (n ^ 2) 동작으로 되돌아 가지 않기 때문에 데이터를 사용하기 전에 데이터가 부분적으로 정렬되어 있는지 확실하게 알 필요는 없습니다 .

단점은 필요한 스플레이 트리 구조의 추가 공간 오버 헤드와 스플레이 트리를 구축하고 파괴하는 데 필요한 시간입니다. 그러나 데이터 크기와 예상되는 사전 정렬 된 양에 따라 오버 헤드가 속도를 높이는 데 가치가있을 수 있습니다.

splaysort에 용지가 연습 및 경험 - 소프트웨어에 출판되었다.



5

Dijkstra의 smoothsort는 이미 분류 된 데이터에 대한 훌륭한 정렬입니다. O (n lg n) 최악의 경우와 O (n) 최상의 경우에서 실행되는 힙 정렬 변형입니다. 나는 분석을 썼다 , 알고리즘을 경우에 당신이 어떻게 작동하는지 궁금.

Natural mergesort는 이것에 대한 또 다른 좋은 방법입니다. 입력을 여러 다른 정렬 된 범위의 연결로 취급 한 다음 merge 알고리즘을 사용하여 결합하여 작동하는 상향식 mergesort 변형입니다. 모든 입력 범위가 정렬 될 때까지이 과정을 반복합니다. 데이터가 이미 정렬되어 있고 O (n lg n) 최악의 경우 O (n) 시간에 실행됩니다. 매우 우아하지만 실제로 Timsort 또는 smoothsort와 같은 다른 적응 형 종류만큼 좋지 않습니다.


다른 정렬 알고리즘과 비교할 때 smoothsort의 런타임 상수는 무엇입니까? (즉, 동일한 데이터에 대한 runtime (smoothsort) / runtime (insertionsort))
Arne Babenhauserheide

4

요소가 이미 정렬되었거나 요소가 거의없는 경우 삽입 정렬의 완벽한 사용 사례가됩니다!


3

삽입 정렬에는 시간 O (n + 반전 횟수)가 걸립니다.

반전은 다음 (i, j)과 같은 쌍 입니다 i < j && a[i] > a[j]. 즉, 비 순차적 쌍입니다.

"거의 정렬 된"측정 값 중 하나는 반전 수입니다.- "거의 정렬 된 데이터"를 사용하여 반전이 거의없는 데이터를 의미 할 수 있습니다. 선형이되는 역전 횟수를 알고있는 경우 (예 : 정렬 된 목록에 O (1) 요소를 추가 한 경우) 삽입 정렬에는 O (n) 시간이 걸립니다.


2

다른 사람들이 말했듯이 순진한 Quicksort를 조심하십시오. 정렬 된 또는 거의 정렬 된 데이터에 대해 O (N ^ 2) 성능을 가질 수 있습니다. 그럼에도 불구하고 피벗을 선택하기위한 적절한 알고리즘 (랜덤 또는 3의 중간 값 -Quicksort 의 피벗 선택 참조 )을 사용하면 Quicksort가 여전히 제대로 작동합니다.

일반적으로 삽입 정렬과 같은 알고리즘을 선택하는 데 어려움은 Quicksort가 실제로 더 빠를 정도로 데이터의 순서가 불충분 한 시점을 결정하는 데 있습니다.


2

실제 답변을 얻으려면 알고리즘을 코딩하고 대표 데이터 샘플에 대해 프로파일 링해야 할 수도 있기 때문에 여기에 모든 답변을하는 척하지는 않습니다. 그러나 나는 저녁 내내이 질문에 대해 생각 해 왔으며 여기까지 나에게 일어난 일이 있으며 어떤 것이 가장 잘 작동하는지 추측합니다.

N은 총 항목 수, M은 비 순차적 번호입니다.

버블 정렬은 모든 N 항목을 통과하는 2 * M + 1과 같은 것을 만들어야합니다. M이 매우 작 으면 (0, 1, 2?) 나는 이길 수 없을 것이라고 생각합니다.

M이 작 으면 (예 : log N보다 작음) 삽입 정렬의 평균 성능이 우수합니다. 그러나 내가 보지 못한 트릭이 없다면 최악의 성능이 나빠질 것입니다. (오른쪽? 주문의 마지막 항목이 먼저 오면 가능한 한 모든 단일 항목을 삽입해야 성능이 저하됩니다.) 더 신뢰할 수있는 정렬 알고리즘이 있다고 추측합니다. 경우, 그러나 나는 그것이 무엇인지 모른다.

M이 더 크면 (예 : log N보다 크거나 같음), 내성적 인 정렬이 거의 확실합니다.

이 모든 것의 예외 : 정렬되지 않은 요소를 미리 알고 있다면 가장 좋은 방법은 해당 항목을 꺼내고 내성 정렬을 사용하여 정렬 한 다음 정렬 된 두 목록을 하나의 정렬 된 목록으로 병합하는 것입니다. 어떤 항목이 고장 났는지 신속하게 파악할 수 있다면, 이것이 일반적인 해결책 일 것입니다. 그러나 나는 이것을하는 간단한 방법을 알아낼 수 없었습니다.

추가 생각 (야간) : M + 1 <N / M 인 경우 정렬 된 행에서 N / M 런을 찾은 목록을 스캔 한 다음 해당 런을 어느 한 방향으로 확장하여 범위를 벗어남을 찾을 수 있습니다. 주문 상품. 최대 2N 비교가 필요합니다. 그런 다음 정렬되지 않은 항목을 정렬하고 두 목록에서 정렬 된 병합을 수행 할 수 있습니다. 총 비교는 4N + M log2 (M)와 같은 것보다 작아야합니다. 이는 전문화되지 않은 정렬 루틴을 능가 할 것이라고 생각합니다. (더 이상 생각하면 : 생각보다 까다 롭지 만 여전히 가능하다고 생각합니다.)

이 질문에 대한 또 다른 해석은 많은 비정품 품목이있을 수 있지만 목록에 있어야하는 위치에 매우 가깝다는 것입니다. (정렬 된 목록으로 시작하여 다른 모든 항목을 그 다음에 오는 항목으로 바꾸는 것을 상상해보십시오.)이 경우 거품 정렬이 매우 잘 수행된다고 생각합니다. 패스 수가 항목에서 가장 멀리 떨어진 곳에 비례한다고 생각합니다. 입니다. 모든 주문 항목이 삽입을 트리거하기 때문에 삽입 정렬이 제대로 작동하지 않습니다. 내성적 인 종류 나 그와 비슷한 것이 잘 작동 할 것으로 생각됩니다.


1

정렬 알고리즘, 데이터 구조 또는 위의 링크가있는 것을 구현하기 위해 특정 구현이 필요한 경우 CodePlex 의 우수한 "데이터 구조 및 알고리즘" 프로젝트를 추천 해 주 시겠습니까?

휠을 재발 명하지 않고 필요한 모든 것을 갖추고 있습니다.

내 작은 곡물 알갱이.


1

대답 에서이 목적을위한이 훌륭한 정렬 알고리즘 모음에는 Gnome Sort 가 부족한 것 같습니다 .Gnome Sort 도 적합하며 아마도 최소한의 구현 노력이 필요합니다.


0

삽입 정렬은 정렬 된 입력에서 가장 좋은 경우 O (n)입니다. 그리고 그것은 대부분 정렬 된 입력 (빠른 정렬보다 낫습니다)에 매우 가깝습니다.


0

숙고 해보십시오. 나는 그것이 O (n lg n) 종류 중 가장 일관된 것이라고 생각합니다.


일관성은 여기서 중요하지 않습니다. 힙 정렬은 정렬 된 데이터에서도 O (n lg n)를 제공하며 실제로는 적합하지 않습니다. 사용 가능한 옵션은 삽입 정렬, 팀 정렬 및 버블 정렬입니다.
Max

0

버블 정렬 (또는 더 안전하지만 양방향 버블 정렬)은 대부분 정렬 된 목록에 이상적 일 수 있지만 목록이 없을 때 조정 된 콤 정렬 (초기 간격 크기가 훨씬 더 낮음)이 조금 더 빠르다고 생각합니다. 완벽하게 정렬되지 않았습니다. 빗 정렬은 거품 정렬로 저하됩니다.


0

잘 사용 사례에 따라 다릅니다. 어떤 요소가 변경되었는지 아는 경우 제거 및 삽입이 가장 좋은 경우입니다.


1
알고리즘 효율성에 대한이 "내가 생각하는 한"테스트는 하루를 밝게 밝혔습니다. 그러나 "제거 및 삽입"을 작성할 때 삽입 정렬 (이전 답변에서 이미 언급 한)을 의미 했습니까? 새로운 종류의 알고리즘? 그렇다면 답변을 확장하십시오.
yoniLavi

0

기포 정렬은 확실히 승자입니다 레이더의 다음 것은 삽입 정렬입니다.


4
설명과 함께 답변을 게시하십시오.

1
중복을 피하기 위해 게시하기 전에 사용 가능한 답변을 살펴 보는 것이 좋습니다.
angainor

-1

사전 정렬 된 데이터에는 매우 비효율적 인 QuickSort를 멀리하십시오. 삽입 정렬은 가능한 적은 값을 이동하여 거의 정렬 된 데이터를 잘 처리합니다.


-1 Quicksort의 모든 산업 구현에는 합리적인 피벗 선택이 있습니다
Stephan Eggermont

1
예. 그러나 비싸지 않으면 피벗 선택이 완벽하지 않습니다.
user1277476
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.