##서, 파티션의 마지막 원소로 한 경우이다. function quicksort ( q ) var list less, pivotList, greater if length ( q ) 1 then return q select a pivot value pivot from q for each x in q except the pivot element if x < pivot then add x to less if x pivot then add x to greater add pivot to pivotList return concatenate ( quicksort ( less ) ( less ), pivotList, quicksort ( greater ) ) 그러나 위 코드는 두 개의 작은 리스트를 저장하기 위해 ( n ) 크기의 저장공간이 따로 필요하다는 단점이 있다. 실제 구현에서 임시 메모리는 캐시 성능과 속도에 심각한 저하를 가져올 수 있다. 아래와 같이 추가 메모리 없이 배열 내부에서 분할 작업을 구현하면 좀 더 복잡하지만 추가 메모리를 사용하지 않고 구현할 수 있다. 작은 크기의 예제 리스트에 대해 내부 분할을 적용한 그림 : 두꺼운 선으로 둘러싸인 원소는 피벗 원소이다. 파란색은 피벗보다 작거나 같고 빨간색은 피벗보다 크다. function partition ( a
) 의 중위법을 이용하여 분할한다. 이 방법을 사용하면 중앙에서 분할될 가능성이 높아 전체적으로 정렬의 성능이 좋아진다. 복잡도 [ 편집 ] n { displaystyle n } 의 리스트를 정렬하는데 걸리는 시간을 T ( n ) { displaystyle T ( n ) }, c { displaystyle c } 는 임의의 상수라 하고, 리스트가 두 개의 거의같은 부분집합으로 나뉜다고 가정하여 얻은 평균 시간 복잡도는 다음과 같다. T ( n ) c n 2 T ( n 2 ) c n 2 { c n 2 2 T ( n 4 ) } 2 c n 4 T ( n 4 ) c n log 2 n n T ( 1 ) = O ( n log 2 n ) { displaystyle { begin { aligned } T ( n ) & geq cn2T left ( { frac { n } { 2 } } right ) & geq cn2 left { { frac { cn } { 2 } } 2T left ( { frac { n } { 4 } } right ) right } & geq 2cn4T left ( {
4 } } right ) right } & geq 2cn4T left ( { frac { n } { 4 } } right ) & cdots & geq cn log - { 2 } nnT ( 1 ) = O ( n log - { 2 } n ) end { aligned } } } 소스 코드 [ 편집 ] C [ 편집 ] void quickSort ( int arr [ ], int left, int right ) { int i = left, j = right ; int pivot = arr [ ( left right ) / 2 ] ; int temp ; do { while ( arr [ i ] < pivot ) i ; while ( arr [ j ] > pivot ) j - - ; if ( i < = j ) { temp = arr [ i ] ; arr [ i ] = arr [ j ] ; arr [ j ] = temp ; i ; j - - ; } } while ( i < = j ) ; / * recursion * / if ( left < j ) quickSort ( arr, left, j ) ; if ( i < right ) quickSort ( ar
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 피벗은 p, 리스트 왼쪽 끝과 오른쪽 끝에서 시작한 인덱스들을 i, j라고 하자. 5 - 3 - 7 - 6 - 2 - 1 - 4 p 리스트 왼쪽에 있는 i 위치의 값이 피벗 값보다 크고, 오른쪽에 있는 j 위치의 값은 피벗 값보다 작으므로 둘을 교환한다. 5 - 3 - 7 - 6 - 2 - 1 - 4 i j p 1 - 3 - 7 - 6 - 2 - 5 - 4 i j p j 위치의 값이 피벗 값보다 작지만, i 위치의 값도 피벗값보다 작으므로 교환하지 않는다. 1 - 3 - 7 - 6 - 2 - 5 - 4 i j p i위치를 피벗 값보다 큰 값이 나올 때 까지 진행해 j 위치의 값과 교환한다. 1 - 3 - 7 - 6 - 2 - 5 - 4 i j p 1 - 3 - 2 - 6 - 7 - 5 - 4 i j p i위치가 j 위치보다 커지면, i 위치의 값과 피벗 값을 교환한다. 1 - 3 - 2 - 6
히트율이 높아지기 때문이다. ), 대부분의 실질적인 데이터를 정렬할 때 제곱 시간이 걸릴 확률이 거의 없도록 알고리즘을 설계하는 것이 가능하다. 때문에 일반적인 경우 정렬은 다른 O ( n log n ) 알고리즘에 비해 훨씬 빠르게 동작한다. 그리고 정렬은 정렬을 위해 O ( log n ) 만큼의 memory를 필요로한다. 또한 정렬은 불안정 정렬에 속한다. 목차 1 알고리즘 2 예제 3 정확성 증명 4 의사 코드와 세부 설명 4. 1 개선된 피벗 선택 5 복잡도 6 소스 코드 6. 1 C 6. 2 java 6. 3 비주얼 베이직 6. 4 OCaml 6. 5 하스켈 6. 6 파이썬 6. 7 파이썬 ( Cache 없이 ) 6. 8 스칼라 6. 9 자바스크립트 알고리즘 [ 편집 ] 정렬은 분할 정복 ( divide and conquer ) 방법을 통해 리스트를 정렬한다. 리스트 가운데서 하나의 원소를 고른다. 이렇게 고른 원소를 피벗이라고 한다. 피벗 앞에는 피벗보다 값이 작은 모든 원소들이 오고, 피벗 뒤에는 피벗보다 값이 큰 모든 원소들이 오도록 피벗을 기준으로 리스트를 둘로 나눈다. 이렇게 리스트를 둘로 나누는 것을 분할이라고 한다. 분할을 마친 뒤에 피
상한이라고 한다. 그리고 x는 n개의 자료의 값 가운데 있을 필요는 없다. x를 임의의 수로 놓는 이유는 알고리즘들이 피봇이라는 수를 다양한 방식으로 고를 수 있기 때문이다. 증명 방법은 수학적 강귀납법이다. 임의의 수 x와 1개의 자료가 주어지면 그 유일한 자료의 값이 x 미만이거나 이상이다. 미만일 경우는 x 이상의 값을 가지는 것들 자체가 없다는 뜻이므로 주어진 명제는 참이다. 이상일 경우는 x 이상의 값을 가지는 것들이 x뿐이므로 x 이후에 그런 자료들이 놓여 있다. k개 이하의 자료가 주어져도 주어진 명제가 성립한다고 가정한다. k1개의 자료가 주어지면 가장 처음 자료의 값이 x 미만이거나 이상이다. 미만일 경우, 알고리즘은 왼쪽 끝 첨수 ( index ) 를 1 증가시켜 분할할 자료의 수를 k개로 만든다. 이 k개의 자료는 2번 가정으로 x 이상의 값을 가지는 것들 중 최솟값의 자료 이후로 오직 x 이상의 값을 가지는 자료들만 놓일 것이다. 전체 k1개 자료 역시 마찬가지다. 이상일 경우, 알고리즘은 자료의 마지막부터 x 미만의 값을 가지는 최초의 자료를 찾아 x 이상인 가장 처음 자료와 자리를 바꾼다. 그래서 분할
정렬 - 위키백과, 우리 모두의 백과사전 정렬 위키백과, 우리 모두의 백과사전. 둘러보기로 가기 검색하러 가기 정렬 난수열에 대해 정렬을 실행한 그림. 수평선은 피벗 값을 가리킨다. 분류 정렬 알고리즘자료 구조 배열최악 시간복잡도 O ( n 2 ) { displaystyle O ( n ^ { 2 } ) } 최선 시간복잡도 O ( n log n ) { displaystyle O ( n log n ) } 평균 시간복잡도 O ( n log n ) { displaystyle O ( n log n ) } 정렬 ( Quicksort ) 은 찰스 앤터니 리처드 호어가 개발한 정렬 알고리즘이다. 다른 원소와의 비교만으로 정렬을 수행하는 비교 정렬에 속한다. 정렬은 n개의 데이터를 정렬할 때, 최악의 경우에는 O ( n2 ) 번의 비교를 수행하고, 평균적으로 O ( n log n ) 번의 비교를 수행한다. 정렬의 내부 루프는 대부분의 컴퓨터 아키텍처에서 효율적으로 작동하도록 설계되어 있고 ( 그 이유는 메모리 참조가 지역화되어 있기 때문에 CPU 캐시의 히트율이 높아지기 때문이다. ), 대부분의 실질적인 데이터를 정렬할 때 제곱 시간이 걸릴 확률이 거의 없도록 알고리즘
나타날 때까지 j index를 감소시키도록 한다. ) pivot을 기준으로 값 비교가 완료되었다면 index 결과 i, j 를 비교 해본다. i 값이 j 값 보다 작거나 같다면 분명 pivot 을 기준으로 교환을 해야할 값이 있다는 뜻이 된다. 교환한 뒤 i 인덱스는 증가 j 인덱스는 감소 연산을 수행한다. i 인덱스가 j 인덱스보다 작거나 같다면 계속 반복해서 수행한다. 위 와 같은 과정은 pivot을 기준으로 왼쪽으로 정렬된 list 에서는 최초 Left 값이 감소된 j 보다 작다면 계속 재귀하면되고, pivot을 기준으로 오른쪽으로 정렬된 list 에서는 최초 Right 값이 증가된 i 값보다 크다면 계속 재귀하면된다. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
= end 5 done = False 6 while not done : 7 while left < = right and arr [ left ] < = pivot : 8 left = 1 9 while left < = right and pivot < = arr [ right ] : 10 right - = 1 11 if right < left : 12 done = True 13 else : 14 arr [ left ], arr [ right ] = arr [ right ], arr [ left ] 15 arr [ start ], arr [ right ] = arr [ right ], arr [ start ] 16 return right 17 18 19 def quick - sort ( arr, start, end ) : 20 if start < end : 21 pivot = partition ( arr, start, end ) 22 quick - sort ( arr, start, pivot - 1 ) 23 quick - sort ( arr, pivot 1, end ) 24 return arr 스칼라 [ 편집 ] def quickSort ( arr : Array [ Int ] ) : Array [ Int ] = { if ( arr. length < = 1 ) return arr
j ) quickSort ( arr, left, j ) ; if ( i < right ) quickSort ( arr, i, right ) ; } java [ 편집 ] public void quickSort ( int [ ] arr, int left, int right ) { int i, j, pivot, tmp ; if ( left < right ) { i = left ; j = right ; pivot = arr [ ( leftright ) / 2 ] ; / / 분할 과정 while ( i < j ) { while ( arr [ j ] > pivot ) j - - ; / / 이 부분에서 arr [ j - 1 ] 에 접근해서 익셉션 발생가능함. while ( i < j & & arr [ i ] < = pivot ) i ; tmp = arr [ i ] ; arr [ i ] = arr [ j ] ; arr [ j ] = tmp ; } / / 정렬 과정 quickSort ( arr, left, i - 1 ) ; quickSort ( arr, i 1, right ) ; } } 비주얼 베이직 [ 편집 ] Private Function QR ( a As Long, left As Long, right As Long ) Dim pivot, left -
퀵 정렬(Quicksort)은 찰스 앤터니 리처드 호어가 개발한 정렬 알고리즘이다. 다른 원소와의 비교만으로 정렬을 수행하는 비교 정렬에 속한다. 퀵 정렬은 n개의 데이터를 정렬할 때, 최악의 경우에는 O(n2)번의 비교를 수행하고, 평균적으로 O(n log n)번의 비교를 수행한다. 퀵 정렬의 내부 루프는 대부분의 컴퓨터 아키텍처에서 효율적으로 작동하도록 설계되어 있고(그 이유는 메모리 참조가 지역화되어 있기 때문에 CPU 캐시의 히트율이 높아지기 때문이다.), 대부분의 실질적인 데이터를 정렬할 때 제곱 시간이 걸릴 확률이 거의 없도록 알고리즘을 설계하는 것이 가능하다. 또한 매 단계에서 적어도 1개의 원소가 자기 자리를 찾게 되므로 이후 정렬할 개수가 줄어든다. 때문에 일반적인 경우 퀵 정렬은 다른 O(n log n) 알고리즘에 비해 훨씬 빠르게 동작한다. 이러한 이유로 퀵소트(빠른정렬)라는 이름의 기원이 되었다. 그리고 퀵 정렬은 정렬을 위해 평균적으로 O(log n)만큼의 memory를 필요로한다. 이는 재귀적 호출로 발생하는 것이며, 최악의 경우 O(n)의 공간복잡도를 보인다. 원소들 중에 같은 값이 있는 경우 같은 값들의 정렬 이후 순서가 초기 순서와 달라질 수 있어 불안정 정렬에 속한다. 이러한 경우의 C코드 예: 51, 52, 3, 2, 1를 정렬하면 1, 2, 3, 52, 51 이 된다.