Python을 사용한 Quicksort


93

나는 완전히 파이썬을 처음 접했고 그것에 퀵 정렬을 구현하려고 노력하고 있습니다. 누군가 내 코드를 완성하도록 도와 주시겠습니까?

세 배열을 연결하고 인쇄하는 방법을 모르겠습니다.

def sort(array=[12,4,5,6,7,3,1,15]):
    less = []
    equal = []
    greater = []

    if len(array) > 1:
        pivot = array[0]
        for x in array:
            if x < pivot:
                less.append(x)
            if x == pivot:
                equal.append(x)
            if x > pivot:
                greater.append(x)
            sort(less)
            sort(pivot)
            sort(greater)

목록을 결합하려면 더하기 연산자를 사용할 수 있습니다 my_list = list1 + list2 + .... 또는 새 목록에 목록 압축 풀기my_list = [*list1, *list2]
Mark Mishyn

답변:


254
def sort(array=[12,4,5,6,7,3,1,15]):
    """Sort the array by using quicksort."""

    less = []
    equal = []
    greater = []

    if len(array) > 1:
        pivot = array[0]
        for x in array:
            if x < pivot:
                less.append(x)
            elif x == pivot:
                equal.append(x)
            elif x > pivot:
                greater.append(x)
        # Don't forget to return something!
        return sort(less)+equal+sort(greater)  # Just use the + operator to join lists
    # Note that you want equal ^^^^^ not pivot
    else:  # You need to handle the part at the end of the recursion - when you only have one element in your array, just return the array.
        return array

8
또한 2 스왑 수 if와 for 루프에들 elifelse불필요한 비교를하고 피하기 위해
SlimPDX

13
이것은 빠른 정렬이 아닌 병합 정렬처럼 들립니다.
Emad Mokhtar 2015

47
실제로 어디에서나 quicksort를 위해 찾은 가장 좋고 가장 읽기 쉬운 파이썬 코드 입니다. 인덱스, 도우미 기능이없고 알고리즘의 요지를 명확하게 보여줍니다 (분할 및 정복). (배열의 기본값은 다소 불필요합니다.)
cmantas

19
@jsmedmar in place 버전보다 더 많은 메모리를 사용합니다. in place 빠른 정렬에 대한 suquant의 답변을 참조하십시오.
John

14
매우 읽기 쉽지만 이것이 '제자리'정렬을 달성하지 못하기 때문에 빠른 정렬의 목적을 무효화하지 않습니까? @RasmiRanjanNayak 정렬은 내장 함수가 아닌 사용자 정의 함수 (재귀 호출)입니다.
Maksood

161

추가 메모리없이 빠른 정렬 (제자리에 있음)

용법:

array = [97, 200, 100, 101, 211, 107]
quicksort(array)
# array -> [97, 100, 101, 107, 200, 211]
def partition(array, begin, end):
    pivot = begin
    for i in xrange(begin+1, end+1):
        if array[i] <= array[begin]:
            pivot += 1
            array[i], array[pivot] = array[pivot], array[i]
    array[pivot], array[begin] = array[begin], array[pivot]
    return pivot



def quicksort(array, begin=0, end=None):
    if end is None:
        end = len(array) - 1
    def _quicksort(array, begin, end):
        if begin >= end:
            return
        pivot = partition(array, begin, end)
        _quicksort(array, begin, pivot-1)
        _quicksort(array, pivot+1, end)
    return _quicksort(array, begin, end)

23
Quicksort는 제자리에서 정렬되고 (여전히 O (nlogn) 런타임을 제공하기 때문에) Quicksort가 종종 선택 알고리즘 (예 : 병합 정렬보다)이므로 선택해야합니다.
BoltzmannBrain

3
if end is None:여러 번 확인 될 것이고 한 번만 확인 될 것 True입니다. 한 번만 호출되도록 래퍼 함수에 넣어야합니다.
Gillespie

Ackchyually, bruhs, @mksteve가 맞고이 줄은 올바르지 않습니다. 또한, array[pivot], array[begin] = array[begin], array[pivot]교체해야합니다 begin함께 end.
Ryan J McCall 2018

2
in-place가 좋지만 이것은 느리고 항목이 많을 때 최대 재귀 깊이에 도달하여 오류가 발생합니다. 참조 repl.it/@almenon/quicksorts?language=python3
Almenon

@mksteve 라이언, 나는 이러한 변경 사항을 테스트하고 그것은을 분류 할 나누기
aljgom

68

또 다른 간결하고 아름다운 버전이 있습니다.

def qsort(arr): 
    if len(arr) <= 1:
        return arr
    else:
        return qsort([x for x in arr[1:] if x < arr[0]]) + \
               [arr[0]] + \
               qsort([x for x in arr[1:] if x >= arr[0]])

# this comment is just to improve readability due to horizontal scroll!!!

자세한 내용은 위 코드를 설명하겠습니다.

  1. 배열의 첫 번째 요소 arr[0]를 피벗으로 선택

    [arr[0]]

  2. qsort 피벗보다 작은 배열 요소 List Comprehension

    qsort([x for x in arr[1:] if x < arr[0]])

  3. qsort 피벗보다 큰 배열 요소 List Comprehension

    qsort([x for x in arr[1:] if x >= arr[0]])


15
@zangw 반대 투표에 대한 가능한 이유 : 1) 이미 정렬되었거나 반전 된 배열에 대한 2 차 런타임 2) 솔루션이 제자리에 있지 않습니다. 따라서 끔찍한 구현, 죄송합니다.
alisianoi

16
전혀 읽을 수 없습니다. 정말로 줄 수를 최소화하려고 노력하고 있습니까? 코드는 기계에 의해 해석되지만 사람은 이해합니다.
jsmedmar

4
@AlfredoGallegos, 퀵 정렬의 요점은 제자리에서 발생한다는 것입니다. 이렇게하려면 병합 정렬을 구현하는 것이 좋습니다.
Padraic Cunningham

14
이 댓글이 진짜인가요? 성능을 원하면을 사용하십시오 sorted. 이것은 분명히 교육용입니다. 그리고 받아 들인 대답보다 읽기 쉽고 읽기 쉽습니다.
Nobilis

4
FWIW, 나는 이것이 그들 모두 중 가장 읽기 쉬운 구현이라고 생각했습니다. 다른 답변보다 알고리즘의 재귀 구조를 더 잘 보여줍니다. 물론 성능이 너무 크지는 않을 것입니다.
SolveIt apr

36

이 답변Python 2.x. 내 대답은에서의 적절한 솔루션의 해석이다 로제타 코드 작동 Python 3도 :

import random

def qsort(xs, fst, lst):
    '''
    Sort the range xs[fst, lst] in-place with vanilla QuickSort

    :param xs:  the list of numbers to sort
    :param fst: the first index from xs to begin sorting from,
                must be in the range [0, len(xs))
    :param lst: the last index from xs to stop sorting at
                must be in the range [fst, len(xs))
    :return:    nothing, the side effect is that xs[fst, lst] is sorted
    '''
    if fst >= lst:
        return

    i, j = fst, lst
    pivot = xs[random.randint(fst, lst)]

    while i <= j:
        while xs[i] < pivot:
            i += 1
        while xs[j] > pivot:
            j -= 1

        if i <= j:
            xs[i], xs[j] = xs[j], xs[i]
            i, j = i + 1, j - 1
    qsort(xs, fst, j)
    qsort(xs, i, lst)

그리고 당신이 in-place 속성을 기꺼이 포기하고 싶다면, 아래는 quicksort의 기본 아이디어를 더 잘 보여주는 또 다른 버전입니다. 가독성 외에도 다른 장점은 안정적 이라는 것입니다 (정렬되지 않은 목록에서 사용했던 것과 동일한 순서로 정렬 된 목록에 동일한 요소가 나타남). 이 안정성 속성은 위에 제시된 메모리 사용량이 적은 내부 구현에서는 유지되지 않습니다.

def qsort(xs):
    if not xs: return xs # empty sequence case
    pivot = xs[random.choice(range(0, len(xs)))]

    head = qsort([x for x in xs if x < pivot])
    tail = qsort([x for x in xs if x > pivot])
    return head + [x for x in xs if x == pivot] + tail

이 솔루션을 공유해 주셔서 감사합니다. 시간의 복잡성을 이해하도록 도와 주시겠습니까? 재귀가 15 번 호출하는 것을 확인했습니다. 이 8 개 중 함수에 대한 유효한 호출이 있습니다. 그것은 첫 번째 솔루션의 시간 복잡성이 O (n)이고 공간 복잡성이 제자리 정렬로서 O (1)임을 의미합니까?
ForeverLearner

@Tammy 당신이 big-O 표기법을 오해하는 것 같습니다. 또한 나는 당신의 질문을 정말로 이해하지 못합니다. 별도의 질문으로 물어볼 수 있습니까? 마지막으로 Quicksort 알고리즘은 O (n logn) 시간과 O (n) 공간에서 실행됩니다.
alisianoi

3
내 잘못이야. 도대체 왜 내가 재귀를 세고 있었습니까? :-) 15 개의 재귀는 [1 호출 (레벨 0) + 2 호출 (레벨 1 파티션) + 4 호출 (레벨 2 파티션) + 8 호출 (레벨 3 파티션 또는 리프 노드))입니다. 그래서 우리는 여전히 높이를 (lg8 + 1) = lgn으로 가지고 있습니다. 각 수준의 총 계산은 c1 (일부 비용) * n입니다. 따라서 O (n lgn). 하나의 내부 교환에 대한 공간 복잡성 = O (1). 따라서 n 요소 = O (n). 포인터 주셔서 감사합니다.
ForeverLearner

3
이 단연코 인터넷에서 최고의 파이썬 퀵이며, 그것은 :(이 많은 O (n)의 공간 솔루션에 묻혀 볼 슬픈
사샤 Kondrashov

@Timofey라는 친절한 말에 감사드립니다. 내 알고리즘 저장소를 살펴보고 싶을 수도 있습니다. 다른 버전의 정렬, 그래프 알고리즘 등이 있습니다. github.com/alisianoi/algos-py
alisianoi

23

Python을 사용한 Quicksort

실생활에서 우리는 항상 파이썬이 제공하는 내장 정렬을 사용해야합니다. 그러나 퀵 정렬 알고리즘을 이해하면 도움이됩니다.

여기서 나의 목표는 참조 자료로 돌아 가지 않고도 독자가 쉽게 이해하고 복제 할 수 있도록 주제를 분류하는 것입니다.

퀵 정렬 알고리즘은 기본적으로 다음과 같습니다.

  1. 피벗 데이터 포인트를 선택합니다.
  2. 피벗보다 작은 (아래) 모든 데이터 포인트를 피벗 아래의 위치로 이동-피벗보다 크거나 같은 (위) 데이터 포인트를 그 위의 위치로 이동합니다.
  3. 피벗 위와 아래 영역에 알고리즘 적용

데이터가 무작위로 분포되어있는 경우 첫 번째 데이터 포인트를 피벗으로 선택하는 것은 무작위 선택과 동일합니다.

읽을 수있는 예 :

먼저 주석과 변수 이름을 사용하여 중간 값을 가리키는 읽기 쉬운 예를 살펴 보겠습니다.

def quicksort(xs):
    """Given indexable and slicable iterable, return a sorted list"""
    if xs: # if given list (or tuple) with one ordered item or more: 
        pivot = xs[0]
        # below will be less than:
        below = [i for i in xs[1:] if i < pivot] 
        # above will be greater than or equal to:
        above = [i for i in xs[1:] if i >= pivot]
        return quicksort(below) + [pivot] + quicksort(above)
    else: 
        return xs # empty list

여기에 설명 된 알고리즘과 코드를 다시 설명하기 위해 피벗 위의 값을 오른쪽으로 이동하고 피벗 아래의 값을 왼쪽으로 이동 한 다음 해당 파티션을 동일한 함수에 전달하여 추가 정렬합니다.

골프 :

88 자까지 입력 할 수 있습니다.

q=lambda x:x and q([i for i in x[1:]if i<=x[0]])+[x[0]]+q([i for i in x[1:]if i>x[0]])

어떻게 거기에 도달하는지 확인하려면 먼저 읽을 수있는 예제를 사용하고 주석과 독 스트링을 제거하고 제자리에서 피벗을 찾습니다.

def quicksort(xs):
    if xs: 
        below = [i for i in xs[1:] if i < xs[0]] 
        above = [i for i in xs[1:] if i >= xs[0]]
        return quicksort(below) + [xs[0]] + quicksort(above)
    else: 
        return xs

이제 아래 및 위, 제자리에서 찾으십시오.

def quicksort(xs):
    if xs: 
        return (quicksort([i for i in xs[1:] if i < xs[0]] )
                + [xs[0]] 
                + quicksort([i for i in xs[1:] if i >= xs[0]]))
    else: 
        return xs

이제 andfalse이면 이전 요소 를 반환하고 그렇지 않으면 true이면 다음 요소를 평가하고 반환합니다.

def quicksort(xs):
    return xs and (quicksort([i for i in xs[1:] if i < xs[0]] )
                   + [xs[0]] 
                   + quicksort([i for i in xs[1:] if i >= xs[0]]))

람다는 단일 epression을 반환하고 단일 표현식으로 단순화되었으므로 (더 읽을 수 없게 되더라도) 이제 람다를 사용할 수 있습니다.

quicksort = lambda xs: (quicksort([i for i in xs[1:] if i < xs[0]] )
                        + [xs[0]] 
                        + quicksort([i for i in xs[1:] if i >= xs[0]]))

그리고 우리의 예제로 줄이려면 함수와 변수 이름을 한 글자로 줄이고 필요하지 않은 공백을 제거하십시오.

q=lambda x:x and q([i for i in x[1:]if i<=x[0]])+[x[0]]+q([i for i in x[1:]if i>x[0]])

대부분의 코드 골프와 마찬가지로이 람다는 다소 나쁜 스타일입니다.

Hoare 파티셔닝 구성표를 사용하는 내부 Quicksort

이전 구현은 불필요한 추가 목록을 많이 만듭니다. 제자리에서이 작업을 수행 할 수 있다면 공간 낭비를 피할 수 있습니다.

아래 구현은 Hoare 파티셔닝 체계를 사용하며, 위키피디아에서 더 자세히 읽을 수 있습니다 (하지만 partition()do-while 대신 while-loop 의미 체계를 사용하고 축소 단계를 끝으로 이동하여 호출 당 최대 4 개의 중복 계산을 제거했습니다. 외부 while 루프.).

def quicksort(a_list):
    """Hoare partition scheme, see https://en.wikipedia.org/wiki/Quicksort"""
    def _quicksort(a_list, low, high):
        # must run partition on sections with 2 elements or more
        if low < high: 
            p = partition(a_list, low, high)
            _quicksort(a_list, low, p)
            _quicksort(a_list, p+1, high)
    def partition(a_list, low, high):
        pivot = a_list[low]
        while True:
            while a_list[low] < pivot:
                low += 1
            while a_list[high] > pivot:
                high -= 1
            if low >= high:
                return high
            a_list[low], a_list[high] = a_list[high], a_list[low]
            low += 1
            high -= 1
    _quicksort(a_list, 0, len(a_list)-1)
    return a_list

충분히 철저히 테스트했는지 확실하지 않습니다.

def main():
    assert quicksort([1]) == [1]
    assert quicksort([1,2]) == [1,2]
    assert quicksort([1,2,3]) == [1,2,3]
    assert quicksort([1,2,3,4]) == [1,2,3,4]
    assert quicksort([2,1,3,4]) == [1,2,3,4]
    assert quicksort([1,3,2,4]) == [1,2,3,4]
    assert quicksort([1,2,4,3]) == [1,2,3,4]
    assert quicksort([2,1,1,1]) == [1,1,1,2]
    assert quicksort([1,2,1,1]) == [1,1,1,2]
    assert quicksort([1,1,2,1]) == [1,1,1,2]
    assert quicksort([1,1,1,2]) == [1,1,1,2]

결론

이 알고리즘은 컴퓨터 과학 과정에서 자주 가르치고 면접에서 요청됩니다. 재귀와 분할 정복에 대해 생각하는 데 도움이됩니다.

Quicksort는 내장 된 timsort 알고리즘이 매우 효율적이고 재귀 제한이 있기 때문에 Python에서 그다지 실용적이지 않습니다 . 우리는 목록을 제자리에서 정렬 list.sort하거나sorted 할 수 있습니다. 둘 다 a keyreverse인수를 사용합니다.


귀하의 partition기능은 다음에 대해 올바르게 작동하지 않는 것 같습니다 : partition([5,4,3,2,1], 0, 4). 기대 수익 지수는 4이고 3을 반환합니다.
matino

@matino 그 기대는 어디에서 왔습니까? 나는 내가 틀렸거나 덜 효율적일 수 있지만 알고리즘을 단순화했다고 믿습니다 (이 답변을 작성할 때 위키피디아에 언급 됨). 전체 빠른 정렬 기능이 실패하는 테스트 케이스를 찾을 수 있다면 도움이 될 것입니다.
Aaron Hall

@AaronHall 때 pivot = a_list [high]를 선택했지만 어떻게 작동하는지 알아낼 수 없습니다. 도울 수 있니 ?
Qiulang

@ matino 나도 같은 혼란을 겪었습니다! 분할 기능은 괜찮습니다. 만족하는 불변성은 기대하는 것보다 약합니다. 왼쪽과 오른쪽을 분리하는 정확한 위치를 피벗보다 작거나 크게 찾을 필요가 없습니다. 그것은 사소하지 않은 파티션만을 보장하고 반환 된 인덱스의 모든 왼쪽이 반환 된 인덱스의 오른쪽보다 작다는 것을 보장합니다.
Dror Speiser

21

이미 이에 대한 많은 답변이 있지만이 접근 방식이 가장 깔끔한 구현이라고 생각합니다.

def quicksort(arr):
    """ Quicksort a list

    :type arr: list
    :param arr: List to sort
    :returns: list -- Sorted list
    """
    if not arr:
        return []

    pivots = [x for x in arr if x == arr[0]]
    lesser = quicksort([x for x in arr if x < arr[0]])
    greater = quicksort([x for x in arr if x > arr[0]])

    return lesser + pivots + greater

물론 모든 것을 변수에 저장하는 것을 건너 뛰고 다음과 같이 즉시 반환 할 수 있습니다.

def quicksort(arr):
    """ Quicksort a list

    :type arr: list
    :param arr: List to sort
    :returns: list -- Sorted list
    """
    if not arr:
        return []

    return quicksort([x for x in arr if x < arr[0]]) \
        + [x for x in arr if x == arr[0]] \
        + quicksort([x for x in arr if x > arr[0]])

11
의 위에!)? 이것은 'slowSort'입니까?
Scott 混合 理论

3
첫 번째 코드 예제에서는 '[lesser]'와 '[greater]'대신 'lesser'와 'greater'가되어야한다고 생각합니다. 그렇지 않으면 평평한 목록이 아닌 중첩 된 목록으로 끝납니다.
Alice

@Scott 混合 理论 나는 아직도 시간의 복잡성을 배우고 있는데,이 구현이 왜 그런지 설명 할 수 O(N!)있습니까? 중첩 된 목록을 가정 [lesser]하고 [greater]이 평균되지 않을 것, 오타는 O(3N logN)(가)의 평균 기대에 감소 것이다 O(N logN)? 부여, 3 목록 콤프 불필요한 일을 ..
Chrispy

무엇을 @Chrispy 만약 당신이 일종의 반전 인 순서 목록, 5,4,3,2,1 같은
스콧混合理论

@Scott 混合 理论 당신은 빠른 정렬의 최악의 경우 실행 시간이 느립니다 Θ (n ^ 2) 옳지 만, "알고리즘 소개"에 따르면 평균 실행 시간은 Θ (n lg n)입니다. 그리고, 더 중요한 것은, 빠른 정렬 전반적으로 힙 종류를 능가하는 성능
Lungang 팡

6

기능적 접근 :

def qsort(list):
    if len(list) < 2:
        return list

    pivot = list.pop()
    left = filter(lambda x: x <= pivot, list)
    right = filter(lambda x: x > pivot, list)

    return qsort(left) + [pivot] + qsort(right)


akarca 및 @Kunthar python2 또는 python3의 두 솔루션은 실행될 때마다 목록에서 요소를 팝하므로 목록이 삭제됩니다. 여기에 고정 된 버전은 다음과 같습니다 gist.github.com/joshuatvernon/634e0d912401899af0fdc4e23c94192b
joshuatvernon은

4

함수형 프로그래밍 aproach

smaller = lambda xs, y: filter(lambda x: x <= y, xs)
larger = lambda xs, y: filter(lambda x: x > y, xs)
qsort = lambda xs: qsort(smaller(xs[1:],xs[0])) + [xs[0]] + qsort(larger(xs[1:],xs[0])) if xs != [] else []

print qsort([3,1,4,2,5]) == [1,2,3,4,5]

4

grokking 알고리즘에서 쉽게 구현

def quicksort(arr):
    if len(arr) < 2:
        return arr #base case
    else:
        pivot = arr[0]
        less = [i for i in arr[1:] if i <= pivot] 
        more = [i for i in arr[1:] if i > pivot]
        return quicksort(less) + [pivot] + quicksort(more)

3

여기에 제공된 두 가지 답변 모두 (원래 질문에 대한 답변) 제공된 목록에서 정상적으로 작동한다고 생각하지만 고유하지 않은 값을 포함하는 배열이 전달되면 중단됩니다. 그래서 완전성을 위해 각각의 작은 오류를 지적하고 수정 방법을 설명합니다.

예를 들어 Brionius 알고리즘 ..을 사용 하여 다음 배열 [12,4,5,6,7,3,1,15,1] (1이 두 번 표시됨)을 정렬하려고하면 어느 시점에서 더 적은 배열이 비어있게됩니다. 및 동일 다음 반복와 분리 될 수없는 값 (1,1)의 쌍으로 배열 LEN ()> 1 ... 따라서는 무한 루프로 끝날 것

zangw 대답 과 같이 동일한 배열에서 sort를 호출 하지 않으면 less 가 비어 있거나 더 나은 경우 배열을 반환하여 수정할 수 있습니다.

def sort(array=[12,4,5,6,7,3,1,15]):
    less = []
    equal = []
    greater = []

    if len(array) > 1:
        pivot = array[0]
        for x in array:
            if x < pivot:
                less.append(x)
            if x == pivot:
                equal.append(x)
            if x > pivot:
                greater.append(x)

        # Don't forget to return something!
        return sort(less)+ equal +sort(greater)  # Just use the + operator to join lists
    # Note that you want equal ^^^^^ not pivot
    else:  # You need to hande the part at the end of the recursion - when you only have one element in your array, just return the array.
        return array

더 멋진 솔루션도 중단되지만 다른 원인으로 인해 재귀 줄에 return 절이 누락 되어 어떤 시점에서 None을 반환하고 목록에 추가하려고 시도합니다 ....

문제를 해결하려면 해당 라인에 리턴을 추가하십시오.

def qsort(arr): 
   if len(arr) <= 1:
      return arr
   else:
      return qsort([x for x in arr[1:] if x<arr[0]]) + [arr[0]] + qsort([x for x in arr[1:] if x>=arr[0]])

그건 그렇고, 간결한 버전은 목록 이해력에서 배열을 두 번 반복하기 때문에 긴 버전보다 성능이 떨어집니다.
FedeN 2014

3

이것은 Hoare 파티션 구성표를 사용하고 더 적은 스왑 및 로컬 변수를 사용하는 퀵 정렬 버전입니다.

def quicksort(array):
    qsort(array, 0, len(array)-1)

def qsort(A, lo, hi):
    if lo < hi:
        p = partition(A, lo, hi)
        qsort(A, lo, p)
        qsort(A, p + 1, hi)

def partition(A, lo, hi):
    pivot = A[lo]
    i, j = lo-1, hi+1
    while True:
      i += 1
      j -= 1
      while(A[i] < pivot): i+= 1
      while(A[j] > pivot ): j-= 1

      if i >= j: 
          return j

      A[i], A[j] = A[j], A[i]


test = [21, 4, 1, 3, 9, 20, 25, 6, 21, 14]
print quicksort(test)

3

분할 -작은 요소는 왼쪽으로 이동하고 큰 요소는 오른쪽으로 또는 그 반대로 이동하는 피벗으로 배열을 분할합니다. 피벗은 배열의 임의의 요소가 될 수 있습니다. 이 알고리즘을 만들려면 배열의 시작 및 끝 인덱스가 무엇인지, 피벗이 어디에 있는지 알아야합니다. 그런 다음 두 개의 보조 포인터 L, R을 설정합니다.

그래서 우리는 배열 user [..., begin, ..., end, ...]가 있습니다.

L 및 R 포인터의 시작 위치
[..., begin, next, ..., end, ...]
     R L

while L <end
  1. 사용자 [pivot]> user [L]이면 R을 1 씩 이동하고 user [R]을 user [L]과 교체
  2. L을 1 씩 이동

사용자 [R]를 사용자 [피벗]과 교체 한 후

빠른 정렬 -피벗에 의한 분할의 모든 다음 부분이 시작 인덱스가 끝 인덱스보다 크거나 같을 때까지 파티션 알고리즘을 사용합니다.

def qsort(user, begin, end):

    if begin >= end:
        return

    # partition
    # pivot = begin
    L = begin+1
    R = begin
    while L < end:
        if user[begin] > user[L]:
            R+=1
            user[R], user[L] = user[L], user[R]
        L+= 1
    user[R], user[begin] = user[begin], user[R]

    qsort(user, 0, R)
    qsort(user, R+1, end)

tests = [
    {'sample':[1],'answer':[1]},
    {'sample':[3,9],'answer':[3,9]},
    {'sample':[1,8,1],'answer':[1,1,8]},
    {'sample':[7,5,5,1],'answer':[1,5,5,7]},
    {'sample':[4,10,5,9,3],'answer':[3,4,5,9,10]},
    {'sample':[6,6,3,8,7,7],'answer':[3,6,6,7,7,8]},
    {'sample':[3,6,7,2,4,5,4],'answer':[2,3,4,4,5,6,7]},
    {'sample':[1,5,6,1,9,0,7,4],'answer':[0,1,1,4,5,6,7,9]},
    {'sample':[0,9,5,2,2,5,8,3,8],'answer':[0,2,2,3,5,5,8,8,9]},
    {'sample':[2,5,3,3,2,0,9,0,0,7],'answer':[0,0,0,2,2,3,3,5,7,9]}
]

for test in tests:

    sample = test['sample'][:]
    answer = test['answer']

    qsort(sample,0,len(sample))

    print(sample == answer)

OP 및 향후 전망이 더 많은 이점을 얻을 수 있도록 코드 / 추가 사항을 설명하십시오.
Omar Einea

2

또는 C / C ++ varags, 람다 식 및 if 식에 해당하는 Python을 보여주는 한 줄짜리 줄을 선호하는 경우 :

qsort = lambda x=None, *xs: [] if x is None else qsort(*[a for a in xs if a<x]) + [x] + qsort(*[a for a in xs if a>=x])

2
def quick_sort(self, nums):
    def helper(arr):
        if len(arr) <= 1: return arr
        #lwall is the index of the first element euqal to pivot
        #rwall is the index of the first element greater than pivot
        #so arr[lwall:rwall] is exactly the middle part equal to pivot after one round
        lwall, rwall, pivot = 0, 0, 0
        #choose rightmost as pivot
        pivot = arr[-1]
        for i, e in enumerate(arr):
            if e < pivot:
                #when element is less than pivot, shift the whole middle part to the right by 1
                arr[i], arr[lwall] = arr[lwall], arr[i]
                lwall += 1
                arr[i], arr[rwall] = arr[rwall], arr[i]
                rwall += 1
            elif e == pivot:
                #when element equals to pivot, middle part should increase by 1
                arr[i], arr[rwall] = arr[rwall], arr[i]
                rwall += 1
            elif e > pivot: continue
        return helper(arr[:lwall]) + arr[lwall:rwall] + helper(arr[rwall:])
    return helper(nums)

2

나는 많은 사람들이이 질문에 올바르게 답한 것을 알고 있고 나는 그들을 읽는 것을 즐겼다. 내 대답은 zangw와 거의 같지만 이전 기여자들은 실제로 작동하는 방식을 시각적으로 설명하지 못한 것 같습니다 ... 그래서 앞으로이 질문 / 답변을 방문 할 다른 사람들을 돕기위한 나의 시도가 있습니다. 빠른 정렬 구현을위한 간단한 솔루션입니다.

어떻게 작동합니까?

  1. 기본적으로 목록에서 피벗으로 첫 번째 항목을 선택한 다음 두 개의 하위 목록을 만듭니다.
  2. 첫 번째 하위 목록에는 피벗보다 작은 항목이 포함됩니다.
  3. 두 번째 하위 목록에는 피벗보다 크거나 같은 항목이 포함됩니다.
  4. 그런 다음 각 항목을 빠르게 정렬하고 첫 번째 그룹 + 피벗 + 두 번째 그룹을 결합하여 최종 결과를 얻습니다.

다음은 시각적으로 함께 사용할 수있는 예입니다 ... (pivot) 9,11,2,0

평균 : n log of n

더 나쁜 경우 : n ^ 2

여기에 이미지 설명 입력

코드:

def quicksort(data):
if (len(data) < 2):
    return data
else:
    pivot = data[0]  # pivot
    #starting from element 1 to the end
    rest = data[1:]
    low = [each for each in rest if each < pivot]
    high = [each for each in rest if each >= pivot]
    return quicksort(low) + [pivot] + quicksort(high)

items = [9,11,2,0] print (quicksort (items))


2

알고리즘은 두 개의 경계를 포함합니다. 하나는 피벗보다 작은 요소 (인덱스 "j"로 추적)와 다른 경계는 피벗보다 큰 요소 (인덱스 "i"로 추적)를 포함합니다.

각 반복에서 새 요소는 j를 증가시켜 처리됩니다.

불변 :-

  1. 피벗과 i 사이의 모든 요소가 피벗보다 작습니다.
  2. i와 j 사이의 모든 요소는 피벗보다 큽니다.

불변이 위반되면 i 번째와 j 번째 요소가 교체되고 i가 증가합니다.

모든 요소가 처리되고 피벗이 분할 된 이후의 모든 요소가 피벗 요소보다 작은 마지막 요소로 교체됩니다.

피벗 요소는 이제 시퀀스에서 올바른 위치에 있습니다. 그 이전의 요소는 그것보다 적을 것이고 그 이후의 요소는 그것보다 클 것이며 정렬되지 않을 것입니다.

def quicksort(sequence, low, high):
    if low < high:    
        pivot = partition(sequence, low, high)
        quicksort(sequence, low, pivot - 1)
        quicksort(sequence, pivot + 1, high)

def partition(sequence, low, high):
    pivot = sequence[low]
    i = low + 1
    for j in range(low + 1, high + 1):
        if sequence[j] < pivot:
            sequence[j], sequence[i] = sequence[i], sequence[j]
            i += 1
    sequence[i-1], sequence[low] = sequence[low], sequence[i-1]
    return i - 1

def main(sequence):
    quicksort(sequence, 0, len(sequence) - 1)
    return sequence

if __name__ == '__main__':
    sequence = [-2, 0, 32, 1, 56, 99, -4]
    print(main(sequence))

피벗 선택

"좋은"피벗은 대략 같은 크기의 두 개의 하위 시퀀스를 생성합니다. 결정적으로 피벗 요소는 순진한 방식으로 선택하거나 시퀀스의 중앙값을 계산하여 선택할 수 있습니다.

피벗을 선택하는 순진한 구현은 첫 번째 또는 마지막 요소가 될 것입니다. 이 경우 최악의 런타임은 입력 시퀀스가 ​​이미 정렬되었거나 역 정렬 된 경우입니다. 하위 시퀀스 중 하나가 비어있어 재귀 호출 당 하나의 요소 만 제거됩니다.

피벗이 시퀀스의 중앙 요소 일 때 완벽하게 균형 잡힌 분할이 이루어집니다. 그것보다 크고 작은 같은 수의 요소가 있습니다. 이 접근 방식은 더 나은 전체 실행 시간을 보장하지만 훨씬 더 많은 시간이 소요됩니다.

피벗을 선택하는 비 결정적 / 무작위적인 방법은 요소를 무작위로 균일하게 선택하는 것입니다. 이것은 최악의 시나리오를 최소화하고 대략적으로 균형 잡힌 분할로 이어지는 간단하고 가벼운 접근 방식입니다. 이것은 또한 피벗을 선택하는 순진한 접근 방식과 중앙값 접근 방식 사이의 균형을 제공합니다.


2
def quicksort(array):
 if len(array) < 2:
  return array
 else:
  pivot = array[0]

 less = [i for i in array[1:] if i <= pivot]
 greater = [i for i in array[1:] if i > pivot]
 return quicksort(less) + [pivot] + quicksort(greater)

이 코드가 문제에 대한 해결책을 제공 할 수 있지만이 코드가 질문에 대한 답변 이유 및 / 또는 방법에 대한 추가 컨텍스트를 제공하는 것이 좋습니다. 유사한 문제를 겪고있는 미래의 시청자는 솔루션이면의 이유를 이해할 수 없기 때문에 코드 전용 답변은 일반적으로 장기적으로 쓸모가 없습니다.
palaѕн

1
def quick_sort(array):
    return quick_sort([x for x in array[1:] if x < array[0]]) + [array[0]] \
        + quick_sort([x for x in array[1:] if x >= array[0]]) if array else []

1
def Partition(A,p,q):
    i=p
    x=A[i]
    for j in range(p+1,q+1):
        if A[j]<=x:
            i=i+1
            tmp=A[j]
            A[j]=A[i]
            A[i]=tmp
    l=A[p]
    A[p]=A[i]
    A[i]=l
    return i

def quickSort(A,p,q):
    if p<q:
        r=Partition(A,p,q)
        quickSort(A,p,r-1)
        quickSort(A,r+1,q)
    return A

1
코드의 기능과 질문에 대한 답변에 대한 설명을 포함 해주세요. 특히 질문에 게시 된 코드와 어떤 관련이 있습니다. 답변은 OP 및 향후 방문자에게 문제를 디버그하고 수정하는 방법에 대한 지침을 제공해야합니다. 코드이면에있는 아이디어가 무엇인지 지적하면 문제를 이해하고 솔루션을 적용하거나 수정하는 데 큰 도움이됩니다. Stack Overflow 는 코드 작성 서비스가 아니라 교육 및 학습 장소입니다.
Palec

1

"진정한"인플레 이스 구현 [Michael T. Goodrich 및 Roberto Tamassia의 알고리즘 설계 및 응용 책의 알고리즘 8.9, 8.11] :

from random import randint

def partition (A, a, b):
    p = randint(a,b)
    # or mid point
    # p = (a + b) / 2

    piv = A[p]

    # swap the pivot with the end of the array
    A[p] = A[b]
    A[b] = piv

    i = a     # left index (right movement ->)
    j = b - 1 # right index (left movement <-)

    while i <= j:
        # move right if smaller/eq than/to piv
        while A[i] <= piv and i <= j:
            i += 1
        # move left if greater/eq than/to piv
        while A[j] >= piv and j >= i:
            j -= 1

        # indices stopped moving:
        if i < j:
            # swap
            t = A[i]
            A[i] = A[j]
            A[j] = t
    # place pivot back in the right place
    # all values < pivot are to its left and 
    # all values > pivot are to its right
    A[b] = A[i]
    A[i] = piv

    return i

def IpQuickSort (A, a, b):

    while a < b:
        p = partition(A, a, b) # p is pivot's location

        #sort the smaller partition
        if p - a < b - p:
            IpQuickSort(A,a,p-1)
            a = p + 1 # partition less than p is sorted
        else:
            IpQuickSort(A,p+1,b)
            b = p - 1 # partition greater than p is sorted


def main():
    A =  [12,3,5,4,7,3,1,3]
    print A
    IpQuickSort(A,0,len(A)-1)
    print A

if __name__ == "__main__": main()

1

알고리즘에는 4 가지 간단한 단계가 있습니다.

  1. 배열을 왼쪽, 피벗 및 오른쪽의 세 부분으로 나눕니다. 피벗에는 하나의 요소 만 있습니다. 이 피벗 요소를 배열의 첫 번째 요소로 선택하겠습니다.
  2. 요소를 피벗 요소와 비교하여 각 부품에 추가합니다. (댓글 설명)
  3. 배열의 모든 요소가 정렬 될 때까지이 알고리즘을 반복합니다.
  4. 마지막으로 왼쪽 + 피벗 + 오른쪽 부분 결합

파이썬의 알고리즘 코드 :

def my_sort(A):

      p=A[0]                                       #determine pivot element. 
      left=[]                                      #create left array
      right=[]                                     #create right array
      for i in range(1,len(A)):
        #if cur elem is less than pivot, add elem in left array
        if A[i]< p:
          left.append(A[i])         
          #the recurssion will occur only if the left array is atleast half the size of original array
          if len(left)>1 and len(left)>=len(A)//2:          
              left=my_sort(left)                            #recursive call
        elif A[i]>p: 
          right.append(A[i])                                #if elem is greater than pivot, append it to right array
          if len(right)>1 and len(right)>=len(A)//2:        # recurssion will occur only if length of right array is atleast the size of original array
              right=my_sort(right)
     A=left+[p]+right                                        #append all three part of the array into one and return it
     return A

my_sort([12,4,5,6,7,3,1,15])

이 알고리즘을 왼쪽과 오른쪽 부분에서 반복적으로 수행하십시오.


1

또 다른 빠른 정렬 구현 :

# A = Array 
# s = start index
# e = end index
# p = pivot index
# g = greater than pivot boundary index

def swap(A,i1,i2):
  A[i1], A[i2] = A[i2], A[i1]

def partition(A,g,p):
    # O(n) - just one for loop that visits each element once
    for j in range(g,p):
      if A[j] <= A[p]:
        swap(A,j,g)
        g += 1

    swap(A,p,g)
    return g

def _quicksort(A,s,e):
    # Base case - we are sorting an array of size 1
    if s >= e:
      return

    # Partition current array
    p = partition(A,s,e)
    _quicksort(A,s,p-1) # Left side of pivot
    _quicksort(A,p+1,e) # Right side of pivot

# Wrapper function for the recursive one
def quicksort(A):
    _quicksort(A,0,len(A)-1)

A = [3,1,4,1,5,9,2,6,5,3,5,8,9,7,9,3,2,3,-1]

print(A)
quicksort(A)
print(A)

1

Python 3.x 버전 : operator주로 가독성을 높이기 위해 모듈을 사용하는 기능적 스타일 입니다.

from operator import ge as greater, lt as lesser

def qsort(L): 
    if len(L) <= 1: return L
    pivot   = L[0]
    sublist = lambda op: [*filter(lambda num: op(num, pivot), L[1:])]

    return qsort(sublist(lesser))+ [pivot] + qsort(sublist(greater))

그리고 다음과 같이 테스트됩니다.

print (qsort([3,1,4,2,5]) == [1,2,3,4,5])

니스 (지금까지로 문서화되지 않은 코드가 간다)와 유사한 경우 acarca의 , 아르 날도 P. 피게 피게의비 게르의 세 대답은지나 갔다. 질문이 나올 때 이것이 답인지 확실하지 않습니다 … complete my code?. lambda정의에 사용 하는 sublist()것은 꼭 필요한 것처럼 보이지 않습니다.
수염이 희끗 희끗 한

@greybeard 사실, 아르 날도의 코드는, 어떻게 파이썬 3에서 컴파일되지 않은 sublist사용하는 경우에만 정의 할 수 filter? 그게 가능할까요?
lifebalance

1
(임시 코멘트 : thinkin ' defof-이터 러블의 요소를 개별 목록 (또는 하나의 목록에 " 다른 후 카테고리 ")).
수염이 희끗 희끗 한

1

다음은 쉬운 구현입니다.

def quicksort(array):
            if len(array) < 2:
                  return array
            else:
                  pivot= array[0]
                  less = [i for i in array[1:] if i <= pivot]

                  greater = [i for i in array[1:] if i > pivot]

                  return quicksort(less) + [pivot] + quicksort(greater)

print(quicksort([10, 5, 2, 3]))

0
  1. 먼저 배열의 첫 번째 값을 pivot_value로 선언하고 왼쪽 및 오른쪽 표시도 설정합니다.
  2. 첫 번째 while 루프를 만듭니다.이 while 루프는 파티션 프로세스가 필요한 조건을 충족하지 않을 경우 다시 실행하도록 지시합니다.
  3. 그런 다음 파티션 프로세스를 적용합니다.
  4. 두 파티션 프로세스가 모두 실행 된 후 적절한 조건을 충족하는지 확인합니다. 그렇다면 완료로 표시하고 그렇지 않으면 왼쪽과 오른쪽 값을 전환하고 다시 적용합니다.
  5. 완료되면 왼쪽 및 오른쪽 값을 전환하고 split_point를 반환합니다.

아래 코드를 첨부하고 있습니다! 이 빠른 정렬은 피벗 값위치 때문에 훌륭한 학습 도구 입니다. 일정한 장소에 있기 때문에 여러 번 걸을 수 있으며 모든 것이 어떻게 작동하는지 실제로 파악할 수 있습니다. 실제로 O (N ^ 2) 런타임을 피하기 위해 피벗을 무작위로 지정하는 것이 가장 좋습니다.

def quicksort10(alist):
    quicksort_helper10(alist, 0, len(alist)-1)

def quicksort_helper10(alist, first, last):
    """  """
    if first < last:
        split_point = partition10(alist, first, last)
        quicksort_helper10(alist, first, split_point - 1)
        quicksort_helper10(alist, split_point + 1, last)

def partition10(alist, first, last):
    done = False
    pivot_value = alist[first]
    leftmark = first + 1
    rightmark = last
    while not done:
        while leftmark <= rightmark and alist[leftmark] <= pivot_value:
            leftmark = leftmark + 1
        while leftmark <= rightmark and alist[rightmark] >= pivot_value:
            rightmark = rightmark - 1

        if leftmark > rightmark:
            done = True
        else:
            temp = alist[leftmark]
            alist[leftmark] = alist[rightmark]
            alist[rightmark] = temp
    temp = alist[first]
    alist[first] = alist[rightmark]
    alist[rightmark] = temp
    return rightmark

0
def quick_sort(l):
    if len(l) == 0:
        return l
    pivot = l[0]
    pivots = [x for x in l if x == pivot]
    smaller = quick_sort([x for x in l if x < pivot])
    larger = quick_sort([x for x in l if x > pivot])
    return smaller + pivots + larger

18 개의 다른 답변 중 절반 이상이 OP의 원래 질문 인 "세 배열을 연결하는 방법"에 대한 답변입니다. 당신의 대답은 새로운 것을 추가합니까?
Teepeemm

0

파티션 단계에서 인쇄 된 변수가있는 전체 예제 :

def partition(data, p, right):
    print("\n==> Enter partition: p={}, right={}".format(p, right))
    pivot = data[right]
    print("pivot = data[{}] = {}".format(right, pivot))

    i = p - 1  # this is a dangerous line

    for j in range(p, right):
        print("j: {}".format(j))
        if data[j] <= pivot:
            i = i + 1
            print("new i: {}".format(i))
            print("swap: {} <-> {}".format(data[i], data[j]))
            data[i], data[j] = data[j], data[i]

    print("swap2: {} <-> {}".format(data[i + 1], data[right]))
    data[i + 1], data[right] = data[right], data[i + 1]
    return i + 1


def quick_sort(data, left, right):
    if left < right:
        pivot = partition(data, left, right)
        quick_sort(data, left, pivot - 1)
        quick_sort(data, pivot + 1, right)

data = [2, 8, 7, 1, 3, 5, 6, 4]

print("Input array: {}".format(data))
quick_sort(data, 0, len(data) - 1)
print("Output array: {}".format(data))

0
def is_sorted(arr): #check if array is sorted
    for i in range(len(arr) - 2):
        if arr[i] > arr[i + 1]:
            return False
    return True

def qsort_in_place(arr, left, right): #arr - given array, #left - first element index, #right - last element index
    if right - left < 1: #if we have empty or one element array - nothing to do
        return
    else:
        left_point = left #set left pointer that points on element that is candidate to swap with element under right pointer or pivot element
        right_point = right - 1 #set right pointer that is candidate to swap with element under left pointer

        while left_point <= right_point: #while we have not checked all elements in the given array
            swap_left = arr[left_point] >= arr[right] #True if we have to move that element after pivot
            swap_right = arr[right_point] < arr[right] #True if we have to move that element before pivot

            if swap_left and swap_right: #if both True we can swap elements under left and right pointers
                arr[right_point], arr[left_point] = arr[left_point], arr[right_point]
                left_point += 1
                right_point -= 1
            else: #if only one True we don`t have place for to swap it
                if not swap_left: #if we dont need to swap it we move to next element
                    left_point += 1
                if not swap_right: #if we dont need to swap it we move to prev element
                    right_point -= 1

        arr[left_point], arr[right] = arr[right], arr[left_point] #swap left element with pivot

        qsort_in_place(arr, left, left_point - 1) #execute qsort for left part of array (elements less than pivot)
        qsort_in_place(arr, left_point + 1, right) #execute qsort for right part of array (elements most than pivot)

def main():
    import random
    arr = random.sample(range(1, 4000), 10) #generate random array
    print(arr)
    print(is_sorted(arr))
    qsort_in_place(arr, 0, len(arr) - 1)
    print(arr)
    print(is_sorted(arr))

if __name__ == "__main__":
    main()

1
몇 가지 설명을 제공하십시오
Rai

0

이 알고리즘은 재귀 함수를 사용하지 않습니다.

하자 N와 숫자의 목록이 될 len(N) > 0. K = [N]다음 프로그램을 설정 하고 실행합니다.

참고 : 이것은 안정적인 정렬 알고리즘입니다.

def BinaryRip2Singletons(K, S):
    K_L = []
    K_P = [ [K[0][0]] ] 
    K_R = []
    for i in range(1, len(K[0])):
        if   K[0][i] < K[0][0]:
            K_L.append(K[0][i])
        elif K[0][i] > K[0][0]:
            K_R.append(K[0][i])
        else:
            K_P.append( [K[0][i]] )
    K_new = [K_L]*bool(len(K_L)) + K_P + [K_R]*bool(len(K_R)) + K[1:]
    while len(K_new) > 0:
        if len(K_new[0]) == 1:
            S.append(K_new[0][0])
            K_new = K_new[1:]
        else: 
            break
    return K_new, S

N = [16, 19, 11, 15, 16, 10, 12, 14, 4, 10, 5, 2, 3, 4, 7, 1]
K = [ N ]
S = []

print('K =', K, 'S =', S)
while len(K) > 0:
    K, S = BinaryRip2Singletons(K, S)
    print('K =', K, 'S =', S)

프로그램 출력 :

K = [[16, 19, 11, 15, 16, 10, 12, 14, 4, 10, 5, 2, 3, 4, 7, 1]] S = []
K = [[11, 15, 10, 12, 14, 4, 10, 5, 2, 3, 4, 7, 1], [16], [16], [19]] S = []
K = [[10, 4, 10, 5, 2, 3, 4, 7, 1], [11], [15, 12, 14], [16], [16], [19]] S = []
K = [[4, 5, 2, 3, 4, 7, 1], [10], [10], [11], [15, 12, 14], [16], [16], [19]] S = []
K = [[2, 3, 1], [4], [4], [5, 7], [10], [10], [11], [15, 12, 14], [16], [16], [19]] S = []
K = [[5, 7], [10], [10], [11], [15, 12, 14], [16], [16], [19]] S = [1, 2, 3, 4, 4]
K = [[15, 12, 14], [16], [16], [19]] S = [1, 2, 3, 4, 4, 5, 7, 10, 10, 11]
K = [[12, 14], [15], [16], [16], [19]] S = [1, 2, 3, 4, 4, 5, 7, 10, 10, 11]
K = [] S = [1, 2, 3, 4, 4, 5, 7, 10, 10, 11, 12, 14, 15, 16, 16, 19]

0

아마도 선호하는 것일 수도 있지만, 내 기능 위에 유효성 검사를 추가하는 것을 좋아합니다.

def quicksort(arr):
  if len(arr) <= 1:
    return arr

  left  = []
  right = []
  equal = []
  pivot = arr[-1]
  for num in arr:
    if num < pivot:
      left.append(num)
    elif num == pivot:
      equal.append(num)
    else:
      right.append(num)

  return quicksort(left) + equal + quicksort(right)
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.