Pythonを使用したクイックソート


92

私はpythonをまったく使っていないので、それにクイックソートを実装しようとしています。誰かが私のコードを完成するのを手伝ってくれませんか?

3つの配列を連結して出力​​する方法がわかりません。

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)

リストを組み合わせるには、plus演算子を使用できます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
また、第二入れ替えることができifて、forループでSをelifし、else不必要な比較を行うことを避けるために
SlimPDX

13
これはクイックソートではなくマージソートのように聞こえます
Emad Mokhtar 2015年

47
これは実際、どこでもクイックソート用に見つけた最高で最も読みやすいpythonコードです。インデックス、ヘルパー関数はなく、アルゴリズムの要点を明確に示します(分割統治)。(配列のデフォルト値はかなり不要です)
cmantas

19
@jsmedmarインプレースバージョンよりも多くのメモリを使用します。インプレースクイックソートについてはsuquantの回答を参照してください。
ジョン

14
非常に読みやすいですが、これは「インプレース」ソートを実現しないため、クイックソートの目的を無効にしませんか?ここでの@RasmiRanjanNayakソートは、組み込み関数ではなく、ユーザー定義関数(その再帰呼び出し)です。
Maksood 2016

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
クイックソートは適切な順序でソートされるため(そしてマージソートよりも)、多くの場合、クイックソートが選択アルゴリズムであるため(これはO(nlogn)ランタイムを提供します)、これが選択された答えになります。
BoltzmannBrain

3
if end is None:は何度もチェックされ、一度だけチェックされますTrue。これをラッパー関数に入れて、一度だけ呼び出されるようにする必要があります。
Gillespie

申し訳ありませんが、bruhs、@ mksteveは正しく、この行は正しくありません。さらに、array[pivot], array[begin] = array[begin], array[pivot]で置き換える必要がbeginありendます。
Ryan J McCall

2
インプレースは適切ですが、これは遅く、多くのアイテムがある場合に最大再帰深度に到達するためエラーになります。repl.it/@almenon/quicksorts?language=python3
Almenon

@mksteveとRyanは、これらの変更をテストしましたが、並べ替えの問題が発生しました
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 2016

13
これらのコメントは本当ですか?パフォーマンスが必要な場合は、を使用してくださいsorted。これは明らかに教育目的です。そして、それは可読であり、受け入れられた答えよりも可読です。
ノビリス2018

3
FWIW、私はこれがそれらすべての中で最も読みやすい実装だと思いました。これは、アルゴリズムの再帰構造を他のどの回答よりもよく示しています。もちろん、パフォーマンスはそれほど大きくなりません。
SolveIt 2018

35

この回答は、のインプレースQuickSortです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)

また、インプレースプロパティを省略してもかまわない場合は、クイックソートの背後にある基本的な考え方をよりよく説明する別のバージョンを以下に示します。読みやすさは別として、他の利点は安定していることです(ソートされたリストでは、ソートされていないリストで使用されていたのと同じ順序で同じ要素が表示されます)。この安定性プロパティは、上に示したメモリ消費の少ないインプレース実装では保持されません。

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 2015年

@Tammy Big-O表記を誤解しているようです。また、質問の内容がよくわかりません。個別にお願いしてもらえますか?最後に、アルゴリズムとしてのクイックソートは、O(n logn)時間とO(n)空間で実行されます。
alisianoi

3
私の悪い。なぜ私は再帰を数えていましたか?:-)まあ、15回の再帰は[1コール(レベル0)+ 2コール(レベル1パーティション)+ 4コール(レベル2パーティション)+ 8コール(レベル3パーティションまたはリーフノード))です。したがって、高さは(lg8 + 1)= lgnのままです。各レベルの合計計算は、c1(いくらかコスト)* nに対するものです。したがって、O(n lgn)。1つのインプレース交換のスペースの複雑さ= O(1)。したがって、n要素= O(n)の場合。ポインタをありがとう。
ForeverLearner 2015年

3
これはインターネット上で最高のpythonクイックソートであり、非常に多くのO(n)空間ソリューションの下に埋め込まれているのを見るのは悲しいです:(
Sasha Kondrashov 2018

親切な言葉@Timofeyをありがとう。私のアルゴリズムリポジトリを確認することをお勧めします。ソート、グラフアルゴリズムなどの他のバージョンが含まれています。github.com
alisianoi/

23

Pythonを使用したクイックソート

実際には、Pythonが提供する組み込みのソートを常に使用する必要があります。ただし、クイックソートアルゴリズムを理解することは有益です。

ここでの私の目標は、参考資料に戻る必要なく、読者が簡単に理解して複製できるように主題を分解することです。

クイックソートアルゴリズムは基本的に次のとおりです。

  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]]))

ラムダは単一のエプレッションを返すため、単一の式に簡略化されているため(読みにくくなっていますが)、ラムダを使用できるようになりました。

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]]))

そして、この例に絞るために、関数と変数の名前を1文字に短縮し、不要な空白を削除します。

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パーティションスキームを使用したインプレースクイックソート

以前の実装では、多くの不要な追加リストが作成されます。これをインプレースで実行できれば、スペースの浪費を回避できます。

以下の実装では、ウィキペディアで詳細読むことができるHoareパーティションスキームを使用しています(ただしpartition()、do-whileの代わりにwhileループセマンティクスを使用し、絞り込みステップを最後に移動することで、呼び出しごとに最大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]

結論

このアルゴリズムは、コンピューターサイエンスのコースで頻繁に教えられ、面接で尋ねられます。再帰や分割統治について考えるのに役立ちます。

組み込みのtimsortアルゴリズムは非常に効率的であり、再帰制限があるため、PythonではQuicksortはあまり実用的ではありません。リストを同じ場所でlist.sort並べ替えるか、新しい並べ替えられたリストを作成することを期待しますsorted-どちらもa keyreverse引数を取ります。


あなたのpartition関数は次に対して正しく機能していないようです:partition([5,4,3,2,1], 0, 4)。期待されるリターンインデックスは4ですが、3を返します
マティーノ2018年

@matinoその期待はどこから来たのですか?私はアルゴリズムを簡略化したと信じています(ウィキペディアでこの回答を書いているときに述べたように)。クイックソート機能全体が失敗するテストケースを見つけることができれば、それは役に立ちます。
アーロンホール

ピボット= a_list [high]を選択したときの@AaronHallですが、それを機能させる方法がわかりません。手伝ってくれますか ?
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混合理论2014年

3
最初のコード例では、「[lesser]」と「[greater]」ではなく「lesser」と「greater」になるはずです。それ以外の場合は、フラットなリストではなくネストされたリストになります。
アリス

@Scott混合理论私はまだ時間の複雑さを学んでいます、この実装がなぜであるか詳しく説明できますO(N!)か?ネストされたリスト[lesser][greater]タイプミスがあると仮定すると、O(3N logN)予想される平均に減少するのは平均ではないでしょうO(N logN)か?確かに、3つのリストカンプは、不必要な仕事をする...
Chrispy

@Chrispy 5,4,3,2,1のように逆順のリストを並べ替えるとどうなるでしょう
Scott混合理论

@Scott混合理论クイックソートの最悪の場合の実行時間が遅いことは正しいですΘ(n ^ 2)、しかし「アルゴリズムの概要」によれば、平均的な場合の実行時間はΘ(n lg n)です。さらに重要なのは、実際には、クイックソートの方がヒープソートよりもパフォーマンスが優れていることです
Lungang Fang

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)

リストはpython 3で予約されています。ここでコードの修正バージョンを参照してください: gist.github.com/kunthar/9d8962e1438e93f50dc6dd94d503af3d
Kunthar

akarcaと@Kunthar python2またはpython3のどちらのソリューションも、実行されるたびにリストから要素をポップするため、リストが破壊されます。ここに修正バージョンがあります:gist.github.com/joshuatvernon/634e0d912401899af0fdc4e23c94192b
joshuatvernon

4

関数型プログラミングのアプローチ

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

私はここで両方の答えが提供されたリスト(元の質問に答える)では問題なく動作すると思いますが、一意でない値を含む配列が渡されると壊れます。したがって、完全を期すために、それぞれの小さなエラーを指摘し、それらを修正する方法を説明します。

たとえば、次の配列[12,4,5,6,7,3,1,15,1](1は2回出現することに注意してください)をBrioniusアルゴリズムでソートしようとすると、ある時点で、より少ない配列が空になりますそして、次の反復で分離できない値のペア(1,1)を持つ等しい配列とlen()> 1 ...したがって、無限ループになります

zangw回答のように、空が少ない場合は配列を返すか、等しい配列でsortを呼び出さないほうがよい場合は、それを修正できます

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]])

ちなみに、簡潔なバージョンは、リスト内包表記で配列を2回繰り返しているため、長いバージョンよりもパフォーマンスが低くなります。
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

パーティション -小さい要素が左に移動し、大きい要素が右に移動する、またはその逆のピボットによって配列を分割します。ピボットは、配列のランダムな要素にすることができます。このアルゴリズムを作成するには、配列の開始インデックスと終了インデックスが何で、ピボットがどこにあるかを知る必要があります。次に、2つの補助ポインターL、Rを設定します。

したがって、配列user [...、begin、...、end、...]があります。

LおよびRポインターの開始位置
[...、begin、next、...、end、...]
     R L

一方、L <end
  1. user [pivot]> user [L]の場合、Rを1つ移動し、user [R]をuser [L]と交換します
  。2. Lを1つ移動します。

その後、user [R]をuser [pivot]と交換します。

クイックソート -ピボットによる分割のすべての次の部分が開始インデックスが終了インデックス以上になるまで、パーティションアルゴリズムを使用します。

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つのサブリストを作成します。
  2. 最初のサブリストには、ピボットよりも少ないアイテムが含まれています
  3. 2番目のサブリストには、ピボット以上のアイテムが含まれています
  4. 次に、それぞれをすばやくソートし、最初のグループ+ピボット+ 2番目のグループを組み合わせて、最終的な結果を取得します。

以下は、それに伴う視覚的な例です...(pivot)9,11,2,0

平均:nの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))


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、2015年

1

「真の」インプレース実装[Michael T. GoodrichおよびRoberto TamassiaによるAlgorithm Design and Applications Bookのアルゴリズム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. 配列を3つの異なる部分(左、ピボット、右)に分割します。ピボットには1つの要素しかありません。このピボット要素を配列の最初の要素として選択しましょう
  2. 要素をピボット要素と比較して、それぞれのパーツに要素を追加します。(コメントの説明)
  3. 配列内のすべての要素がソートされるまで、このアルゴリズムを再帰します
  4. 最後に、左+ピボット+右のパーツを結合します

Pythonのアルゴリズムのコード:

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主に読みやすさを向上させるための、関数形式のusing モジュール。

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と同様の場合、Arnaldo P. Figueira FigueiraBirgerの答えは過ぎ去った年から。これが質問を読んだときの答えかどうかはわかりません… complete my code?。を使用lambdaして定義することsublist()は、厳密に必要であるようには見えません。
greybeard 2017年

@greybeard実は、アルナルドのコードができるか、また、Pythonの3でコンパイルしていなかったsublistだけで使用して定義しますかfilter?それは可能ですか?
ライフバランス2017年

1
(一時的なコメント:thinkin 'of- defまだいじり始めていなかったのは、イテラブルの要素を個別のリスト(または1つのリストに1つのカテゴリ」の後に))
greybeard '06 / 06/22

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]))

1

アルゴリズムには2つの境界が含まれ、1つはピボットよりも小さい要素(インデックス "j"で追跡)を持ち、もう1つはピボットより大きい要素(インデックス "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つのサブシーケンスになります。決定論的に、ピボット要素は単純な方法で、またはシーケンスの中央値を計算することによって選択できます。

ピボットを選択する単純な実装は、最初または最後の要素になります。この場合の最悪のランタイムは、サブシーケンスの1つが空になり、再帰呼び出しごとに1つの要素のみが削除されるため、入力シーケンスが既にソートまたは逆ソートされている場合です。

ピボットがシーケンスの中央要素である場合、完全にバランスの取れた分割が実現されます。それより大きく、それより小さい要素の数は同じです。このアプローチは、全体的な実行時間の改善を保証しますが、はるかに時間がかかります。

ピボットを選択する非決定的/ランダムな方法は、要素をランダムに均一に選択することです。これは、最悪のシナリオを最小限に抑え、大まかにバランスのとれた分割をもたらす単純で軽量なアプローチです。これは、素朴なアプローチとピボットを選択する中央値のアプローチとの間のバランスも提供します。


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の元の「3つの配列を連結する方法」という質問に答えます。あなたの答えは何か新しいことを追加しますか?
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)

0

私の答えは@alisianoiのすばらしい答えとよく似ています。しかし、私彼のコードにわずかな非効率性があると思います(私のコメントを参照)。さらに、説明を追加し、重複(ピボット)値の問題についてもう少し詳しく説明しました。

def quicksort(nums, begin=0, end=None):
    # Only at the beginning end=None. In this case set to len(nums)-1
    if end is None: end = len(nums) - 1

    # If list part is invalid or has only 1 element, do nothing
    if begin>=end: return

    # Pick random pivot
    pivot = nums[random.randint(begin, end)]

    # Initialize left and right pointers
    left, right = begin, end
    while left < right:
        # Find first "wrong" value from left hand side, i.e. first value >= pivot
        # Find first "wrong" value from right hand side, i.e. first value <= pivot
        # Note: In the LAST while loop, both left and right will point to pivot!
        while nums[left] < pivot: left += 1
        while nums[right] > pivot: right -= 1

        # Swap the "wrong" values
        if left != right:
            nums[left], nums[right] = nums[right], nums[left]
            # Problem:  loop can get stuck if pivot value exists more than once. Simply solve with...
            if nums[left] == nums[right]:
                assert nums[left]==pivot
                left += 1

    # Now, left and right both point to a pivot value.
    # All values to its left are smaller (or equal in case of duplicate pivot values)
    # All values to its right are larger.
    assert left == right and nums[left] == pivot
    quicksort(nums, begin, left - 1)
    quicksort(nums, left + 1, end)
    return
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.