106

私は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)
4

36 に答える 36

297
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
于 2013-08-15T21:42:31.090 に答える
84

別の簡潔で美しいバージョンがあります

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

詳細については、上記のコードを説明しましょう

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

于 2013-11-28T05:32:06.280 に答える
49

この回答は、のインプレース QuickSort ですPython 2.x。私の答えは、次の場合にも機能するRosetta Codeのインプレース ソリューションの解釈です。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
于 2015-06-28T17:31:19.943 に答える
34

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

結論

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

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

于 2016-12-18T18:10:48.427 に答える
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]])
于 2014-08-04T07:57:31.040 に答える
9

機能的アプローチ:

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

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

    return qsort(left) + [pivot] + qsort(right)
于 2014-06-25T11:24:32.427 に答える
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]
于 2015-10-16T06:22:46.120 に答える
3

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

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

zangwの回答のように、 lessが空の場合は配列を返すか、等しい配列で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)
            elif x == pivot:
                equal.append(x)
            else: # 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 が返され、それをリストに追加しようとします ....

それを修正するには、その行に return を追加するだけです

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]])
于 2014-02-22T10:27:17.957 に答える
2

または、C/C++ varag、ラムダ式、および 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])
于 2014-04-02T13:30:41.940 に答える
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)
于 2017-08-10T00:16:20.033 に答える
1

「真の」インプレース実装 [Michael T. Goodrich と Roberto Tamassia による Algorithm Design and Applications Book の Algorithms 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()
于 2016-04-06T18:19:05.630 に答える
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 []
于 2015-02-04T15:14:21.350 に答える
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])

左と右の部分を使用して、このアルゴリズムを再帰的に続行します。

于 2016-07-06T23:34:13.540 に答える
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
于 2015-03-06T08:22:36.033 に答える
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
于 2015-10-27T00:02:29.970 に答える
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
于 2016-04-18T15:12:05.833 に答える
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)
于 2020-04-20T01:29:47.880 に答える
-1

以下の 3 つの異なる配列を取得してすべてを連結する代わりに、従来の概念 (パーティショニング方法) を試してください。

http://pythonplanet.blogspot.in/2015/08/quick-sort-using-traditional-partition.html

これは組み込み関数を使用していません。

パーティショニング機能 -

def partitn(alist, left, right):  
 i=left  
 j=right  
 mid=(left+right)/2   

pivot=alist[mid]  
while i <= j:  
    while alist[i] < pivot:  
        i=i+1   

    while alist[j] > pivot:  
        j=j-1   

    if i <= j:  
        temp = alist[i]  
        alist[i] = alist[j]  
        alist[j] = temp  
        i = i + 1   
        j = j - 1   
于 2015-09-16T12:00:11.603 に答える
-2
def quick_sort(list):
    if len(list) ==0:
        return []

    return  quick_sort(filter( lambda item: item < list[0],list)) + [v for v in list if v==list[0] ]  +  quick_sort( filter( lambda item: item > list[0], list))
于 2014-03-06T13:16:46.757 に答える
-2

インレースソート

def qsort(a, b=0, e=None):
    # partitioning
    def part(a, start, end):
        p = start
        for i in xrange(start+1, end):
            if a[i] < a[p]:
                a[i], a[p+1] = a[p+1], a[i]
                a[p+1], a[p] = a[p], a[p+1]
                p += 1
        return p

    if e is None:
        e = len(a)
    if e-b <= 1: return

    p = part(a, b, e)
    qsort(a, b, p)
    qsort(a, p+1, e)

再帰なし:

deq = collections.deque()
deq.append((b, e))
while len(deq):
    el = deq.pop()
    if el[1] - el[0] > 1:
        p = part(a, el[0], el[1])
        deq.append((el[0], p))
        deq.append((p+1, el[1]))
于 2015-09-03T09:56:13.853 に答える