Python ile Hızlı Sırala


94

Python'da tamamen yeniyim ve içinde hızlı sıralama uygulamaya çalışıyorum. Birisi lütfen kodumu tamamlamama yardım edebilir mi?

Üç diziyi nasıl birleştirip yazdıracağımı bilmiyorum.

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)

Listeleri birleştirmek için artı işlecini kullanabilirsiniz my_list = list1 + list2 + .... Veya listeleri yeni listeye my_list = [*list1, *list2]
çıkartın

Yanıtlar:


255
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
Ayrıca 2 takas olabilir ifçemberinde için de s elifve elsegereksiz karşılaştırmalar yaparak önlemek için
SlimPDX

13
Bu tür değil hızlı sıralama birleştirme gibi ses
Emad Mokhtar

47
Aslında her yerde hızlı sıralama için bulduğum en iyi ve en okunabilir python kodu . Endeks yok, yardımcı işlev yok, algoritmanın özünü açıkça gösteriyor (böl ve yönet). (Dizi için varsayılan değer oldukça gereksizdir)
cmantas

20
@jsmedmar, yerinde bir sürümden daha fazla bellek kullanacaktır, yerinde hızlı sıralama için suquant'ın cevabına bakın.
John

15
Çok okunabilir ancak bu, 'yerinde' sıralamayı başaramayacağına göre, hızlı sıralama amacını bozmaz mı? @RasmiRanjanNayak burada sıralama, herhangi bir yerleşik işlev değil, kullanıcı tanımlı işlevdir (özyinelemeli bir çağrıdır).
Maksood

161

Ek bellek olmadan hızlı sıralama (yerinde)

Kullanım:

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
Hızlı sıralama genellikle tercih edilen algoritmadır (örneğin, birleştirme sıralaması yerine), çünkü yerinde sıralar (ve yine de O (nlogn) çalışma süresi verir).
BoltzmannBrain

3
if end is None:birçok kez kontrol edilecek ve yalnızca bir kez kontrol edilecek True. Bunu bir sarmalayıcı işlevine koymalısınız, böylece yalnızca bir kez çağrılır.
Gillespie

Ackchyually, bruhs, @mksteve haklı ve bu satır yanlış. Ek olarak, ile array[pivot], array[begin] = array[begin], array[pivot]değiştirilmelidir . beginend
Ryan J McCall

2
Yerinde olması iyi olsa da, bu yavaştır ve çok fazla öğe olduğunda maksimum özyineleme derinliğine ulaşılması nedeniyle hata verir. bakınız repl.it/@almenon/quicksorts?language=python3
Almenon

@mksteve ve Ryan, bu değişiklikleri test ettim ve sıralamaları bozdu
aljgom

68

Başka bir özlü ve güzel versiyon var

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

Ayrıntılar için yukarıdaki kodları açıklamama izin verin

  1. dizinin ilk öğesini arr[0]pivot olarak seç

    [arr[0]]

  2. qsort pivot'tan daha küçük olan dizinin elemanları List Comprehension

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

  3. qsort pivot'tan daha büyük olan dizi elemanları List Comprehension

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


15
@zangw bir downvote için olası nedenler: 1) Önceden sıralanmış veya tersine çevrilmiş dizilerde ikinci dereceden çalışma zamanı 2) Çözüm yerinde değil. Bu nedenle, korkunç bir uygulama üzgünüm.
alisianoi

16
hiç okunmuyor, gerçekten satır sayısını en aza indirmeye mi çalışıyorsunuz? Kod makineler tarafından yorumlanır, ancak insanlar tarafından anlaşılır.
jsmedmar

4
@AlfredoGallegos, quicksort'un tüm amacı yerinde gerçekleşmesidir, bunu yapacaksanız birleştirme sıralaması da uygulayabilirsiniz.
Padraic Cunningham

14
Bu yorumlar gerçek mi? Performans istiyorsanız, kullanın sorted, bu açıkça eğitim amaçlıdır. Ve okunabilir, kabul edilen cevaptan daha okunabilir.
Nobilis

4
FWIW, bunun hepsinin en okunabilir uygulaması olduğunu düşündüm. Algoritmanın yinelemeli yapısını diğer yanıtlardan daha iyi gösterir. Tabii ki, performans çok da iyi olmayacak.
SolveIt

37

Bu yanıt , için yerinde bir QuickSort'tur Python 2.x. Cevabım, Rosetta Code'un yerinde çözümünün bir yorumudur ve bu da işe yararPython 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)

Ve yerinde mülkünden vazgeçmeye istekli iseniz, aşağıda, quicksort'un arkasındaki temel fikirleri daha iyi gösteren başka bir versiyon bulunmaktadır. Okunabilirliğin yanı sıra, diğer bir avantajı da kararlı olmasıdır (sıralı listede eşit öğeler, sıralanmamış listede olduğu gibi aynı sırayla görünür). Bu kararlılık özelliği, yukarıda sunulan daha az bellek gerektiren yerinde uygulama ile geçerli değildir.

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

Bu çözümü paylaştığınız için teşekkürler. Zaman karmaşıklığını anlamamıza yardım eder misiniz? Özyinelemenin onu 15 kez arayacağını görüyorum. Bu 8 tanesi işleve yönelik geçerli çağrılardır. Bu, ilk çözüm için zaman karmaşıklığının O (n) olduğu ve uzay karmaşıklığının yerinde sıralama olarak O (1) olduğu anlamına mı geliyor?
ForeverLearner

@Tammy, büyük-O notasyonunu yanlış anladınız gibi görünüyor. Üstelik sorunuzu gerçekten anlamıyorum. Belki ayrı bir soru sorabilir misin? Son olarak, bir algoritma olarak Quicksort, O (n logn) zamanı ve O (n) uzayında çalışır.
alisianoi

3
Benim hatam. Neden yinelemeleri sayıyordum? :-) Pekala, 15 özyineleme [1 çağrı (Seviye 0) + 2 çağrı (Seviye 1 bölüm) + 4 çağrı (Seviye 2 bölüm) + 8 çağrı (Seviye 3 bölüm veya Yaprak düğümler). Yani, hala (lg8 + 1) = lgn olarak yüksekliğimiz var. Her seviyedeki toplam hesaplama c1 (bir miktar maliyet) içindir * n. Dolayısıyla O (n lgn). Yerinde bir değişim için uzay karmaşıklığı = O (1). Dolayısıyla n eleman için = O (n). İşaretçi için teşekkürler.
ForeverLearner

3
bu internetteki en iyi python hızlı sıralamasıdır ve bu kadar çok O (n) uzay çözümünün altına gömüldüğünü görmek üzücü :(
Sasha Kondrashov

Nazik sözler için teşekkürler @ Timofey. Algoritma depoma
alisianoi

23

Python ile Hızlı Sırala

Gerçek hayatta, her zaman Python tarafından sağlanan yerleşik sıralamayı kullanmalıyız. Bununla birlikte, hızlı sıralama algoritmasını anlamak öğreticidir.

Buradaki amacım, konuyu, okuyucu tarafından referans materyallere geri dönmek zorunda kalmadan kolayca anlaşılacak ve kopyalanabilecek şekilde parçalamaktır.

Hızlı sıralama algoritması esasen aşağıdaki gibidir:

  1. Bir pivot veri noktası seçin.
  2. Pivotun altındaki (altındaki) tüm veri noktalarını pivotun altındaki bir konuma taşıyın - pivottan büyük veya ona eşit olanları (yukarısında) bir konuma taşıyın.
  3. Algoritmayı pivotun üstündeki ve altındaki alanlara uygulayın

Veriler rastgele dağıtılmışsa, pivot olarak ilk veri noktasını seçmek rastgele bir seçime eşdeğerdir.

Okunabilir örnek:

İlk olarak, ara değerlere işaret etmek için yorumlar ve değişken adları kullanan okunabilir bir örneğe bakalım:

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

Burada gösterilen algoritmayı ve kodu yeniden ifade etmek için - pivotun üzerindeki değerleri sağa ve pivotun altındaki değerleri sola taşırız ve daha sonra bu bölümleri daha fazla sıralanmak üzere aynı işleve aktarırız.

Golf oynadı:

Bu 88 karaktere golf oynanabilir:

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

Oraya nasıl geldiğimizi görmek için önce okunabilir örneğimizi alın, yorumları ve dokümanları kaldırın ve pivotu yerinde bulun:

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

Şimdi aşağıda ve yukarıda, yerinde bulun:

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

Şimdi, bunun andyanlışsa önceki öğeyi döndürdüğünü bildiğimiz halde, eğer doğruysa, aşağıdaki öğeyi değerlendirir ve döndürür:

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

Lambdalar tek bir ifade döndürdüğünden ve tek bir ifadeye sadeleştirdiğimizden (daha okunamaz hale gelse bile) artık bir lambda kullanabiliriz:

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

Örneğimize indirgemek için, işlev ve değişken adlarını bir harfe kısaltın ve gerekli olmayan boşlukları kaldırın.

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

Çoğu kod golfü gibi bu lambda'nın da oldukça kötü bir stil olduğunu unutmayın.

Hoare Bölümleme şemasını kullanarak yerinde Hızlı Sıralama

Önceki uygulama, birçok gereksiz ekstra liste yaratır. Bunu yerinde yapabilirsek, yer israfından kaçınırız.

Aşağıdaki uygulama , Wikipedia'da daha fazla bilgi edinebileceğiniz Hoare bölümleme şemasını kullanır (ancak görünüşe göre partition(), do-while yerine while döngüsü semantiğini kullanarak ve daraltma adımlarını sonuna taşıyarak çağrı başına 4'e kadar fazlalık hesaplamayı kaldırdık . dış while döngüsü.).

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

Yeterince test edip etmediğimden emin değilim:

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]

Sonuç

Bu algoritma genellikle bilgisayar bilimleri derslerinde öğretilir ve iş görüşmeleri istenir. Özyineleme ve böl ve yönet hakkında düşünmemize yardımcı olur.

Quicksort Python'da çok pratik değildir çünkü yerleşik zaman sıralaması algoritmamız oldukça verimli ve özyineleme limitlerimiz var. Listeleri yerinde list.sortsıralamayı veya yeni sıralı listeler oluşturmayı bekleriz sorted- her ikisi de bir keyve reverseargüman alır.


Sizin partitionişlevi için düzgün çalışmıyor gibi: partition([5,4,3,2,1], 0, 4). Beklenen getiri endeksi 4 iken 3 döndürür.
matino

@matino Bu beklenti nereden geldi? Yanlış veya belki daha az verimli olsam da algoritmayı basitleştirdiğime inanıyorum (bu cevabı yazarken Wikipedia'da belirtildiği gibi). Tüm hızlı sıralama işlevinin başarısız olduğu bir test durumu bulabilirseniz, bu yardımcı olacaktır.
Aaron Hall

@AaronHall pivot = a_list [high] seçtiğimde ama o zaman nasıl çalışmasını sağlayacağımı çözemiyorum. Yardım edebilir misin ?
Qiulang

@matino Ben de aynı kafa karışıklığı yaşadım! Bölme işlevi iyidir, karşıladığı değişmezlik beklediğinizden daha zayıftır - solu ve sağı pivottan daha küçük ve daha büyük olarak ayıran tam yeri bulması gerekmez. Yalnızca önemsiz olmayan bir bölümü ve döndürülen dizinin tüm sollarının döndürülen dizinin sağından daha küçük olmasını garanti eder.
Dror Speıser

21

Bunun zaten birçok cevabı var, ancak bu yaklaşımın en temiz uygulama olduğunu düşünüyorum:

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

Elbette her şeyi değişkenler halinde saklamayı atlayabilir ve bunları hemen şu şekilde geri döndürebilirsiniz:

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
O (N!)? bu bir 'slowSort' mu?
Scott 混合 理论

3
İlk kod örneğinde, "[daha küçük]" ve "[daha büyük]" yerine "daha küçük" ve "daha büyük" olması gerektiğine inanıyorum - aksi takdirde düz bir liste yerine iç içe geçmiş listeler elde edeceksiniz.
Alice

@Scott 混合 理论 Hala zamanın karmaşıklığını öğreniyorum, bu uygulamanın neden olduğunu açıklayabilir O(N!)misin? İç içe geçmiş listeyi [lesser]ve [greater]yazım hataları olduğunu varsayarsak, O(3N logN)beklenen ortalamaya düşecek olan ortalama olmaz O(N logN)mı? Verildi, 3 liste kompozisyonu gereksiz işler yapıyor ..
Chrispy

@Chrispy,
5,4,3,2,1

@Scott 混合 理论 hızlı sıralamanın en kötü durumdaki çalışma süresinin yavaş olduğu konusunda haklısınız Θ (n ^ 2), ancak "algoritmaya giriş" e göre, ortalama durum çalışma süresi Θ (n lg n). Ve daha da önemlisi, hızlı sıralama uygulamada genel olarak yığın tür geride
Lungang Fang'ı

6

Işlevsel yaklaşım:

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)

liste python 3'te saklıdır. Kodunuzun değiştirilmiş sürümüne buradan bakın: gist.github.com/kunthar/9d8962e1438e93f50dc6dd94d503af3d
Kunthar

akarca ve @Kunthar Hem python2 hem de python3'teki bu çözümlerin ikisi de, her çalıştırıldığında listeden bir öğe açacak ve bu nedenle listeyi yok edecektir. İşte sabit bir sürüm: gist.github.com/joshuatvernon/634e0d912401899af0fdc4e23c94192b
joshuatvernon

4

fonksiyonel programlama yaklaşımı

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 algoritmalarından kolay uygulama

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

Buradaki her iki cevabın da sağlanan liste için iyi çalıştığını düşünüyorum (orijinal soruyu cevaplayan), ancak benzersiz olmayan değerler içeren bir dizi geçirilirse bozulur. Yani tamlık için, her birindeki küçük hataya işaret eder ve bunları nasıl düzelteceğimi açıklardım.

Örneğin aşağıdaki diziyi [12,4,5,6,7,3,1,15,1] (1'in iki kez göründüğünü unutmayın) Brionius algoritması ile sıralamayı denemek .. bir noktada daha az dizi boş ile sonuçlanacaktır ve bir sonraki yinelemede ayrılamayan bir çift değere (1,1) sahip eşit dizi ve len ()> 1 ... dolayısıyla sonsuz bir döngü ile sonuçlanacaksınız

Sen eğer ikisinden biri dönen dizinin tarafından tamir edebiliriz az daha iyi boş veya değil gibi senin eşit dizideki sıralama çağıran zangw cevap

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

Daha meraklı çözüm de bozulur, ancak farklı bir nedenden ötürü , özyineleme satırındaki dönüş cümlesini kaçırır, bu da bir noktada Hiçbiri döndürmesine ve onu bir listeye eklemeye çalışmasına neden olur ....

Düzeltmek için sadece o satıra bir dönüş ekleyin

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

Bu arada, kısa sürüm, uzun sürümden daha az performansa sahip, çünkü diziyi liste anlayışlarında iki kez yineliyor.
FedeN

3

Bu, Hoare bölümleme şemasını kullanan ve daha az takas ve yerel değişken içeren bir hızlı sıralama sürümüdür

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

Bölüm - Bir diziyi, daha küçük elemanların sola ve daha büyük elemetlerin sağa veya tam tersi yönde hareket ettiği bir pivot ile bölün. Bir pivot, bir diziden rastgele bir öğe olabilir. Bu algoritmayı yapmak için bir dizinin başlangıç ​​ve bitiş indeksinin ne olduğunu ve pivotun nerede olduğunu bilmemiz gerekir. Ardından iki yardımcı işaretçi L, R ayarlayın.

Yani bir dizi kullanıcımız var [..., başlangıç, ..., bitiş, ...]

Sol ve Sağ işaretçilerinin başlangıç ​​konumu
[..., başlangıç , sonraki, ..., bitiş, ...]
     R L

L <son
  1. Eğer bir kullanıcı [pivot]> kullanıcı [L] ise, R'yi birer birer hareket ettirin ve [R] kullanıcısını [L] ile değiştirin
  2. L'yi birer birer hareket ettirin

Bir süre sonra [R] kullanıcısını [pivot] ile değiştir

Hızlı sıralama - Bir pivot tarafından bölünmenin her bir sonraki bölümü başlangıç ​​dizini bitiş dizininden büyük veya bitiş dizininden büyük olana kadar bölüm algoritmasını kullanın.

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 ve gelecekteki görünümlerin daha fazla yararlanabilmesi için lütfen kodunuzu / eklemelerinizi açıklayın.
Omar Einea

2

Veya C / C ++ varags, lambda ifadeleri ve if ifadelerinin Python eşdeğerini de gösteren tek satırlık bir ürünü tercih ederseniz:

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

Birçok kişinin bu soruyu doğru yanıtladığını biliyorum ve okumaktan zevk aldım. Cevabım neredeyse zangw ile aynı, ancak önceki katkıda bulunanların işlerin gerçekte nasıl çalıştığını görsel olarak açıklamakta iyi bir iş çıkarmadıklarını düşünüyorum ... bu yüzden, gelecekte bu soruyu / cevapları ziyaret edebilecek başkalarına yardım etme girişimim şudur: hızlı sıralama uygulaması için basit çözüm.

O nasıl çalışır ?

  1. Listemizden temel olarak ilk öğeyi pivot olarak seçiyoruz ve ardından iki alt liste oluşturuyoruz.
  2. İlk alt listemiz, pivottan daha az olan öğeleri içerir
  3. İkinci alt listemiz, pivot değerine eşit veya harika öğelerimizi içerir
  4. Daha sonra bunların her birini hızlıca sıralıyoruz ve nihai sonucu elde etmek için onları birinci grup + pivot + ikinci grubu birleştiriyoruz.

İşte görselle birlikte buna uygun bir örnek ... (pivot) 9,11,2,0

ortalama: n log of n

daha kötü durum: n ^ 2

görüntü açıklamasını buraya girin

Kod:

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)

öğeler = [9,11,2,0] baskı (hızlı sıralama (öğeler))


2

Algoritma, biri pivottan daha az öğelere ("j" indeksi ile izlenir) ve diğeri pivottan daha büyük öğelere (indeks "i" tarafından izlenir) sahip olan iki sınır içerir.

Her yinelemede, j'yi artırarak yeni bir eleman işlenir.

Değişmez: -

  1. pivot ve i arasındaki tüm öğeler pivottan daha küçüktür ve
  2. i ve j arasındaki tüm öğeler pivottan büyüktür.

Değişmez ihlal edilirse, i'inci ve j'inci elemanlar değiştirilir ve i artırılır.

Tüm öğeler işlendikten ve pivot bölümlendikten sonra her şey işlendikten sonra, pivot öğesi ondan daha küçük olan son öğe ile değiştirilir.

Pivot öğesi artık sıradaki doğru yerinde olacaktır. Ondan önceki öğeler ondan daha az olacak ve ondan sonraki öğeler ondan daha büyük olacak ve sıralanmayacaklar.

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

Bir pivot seçme

"İyi" bir pivot, kabaca aynı boyutta iki alt sekansla sonuçlanacaktır. Belirleyici olarak, bir pivot elemanı ya naif bir şekilde ya da dizinin medyanını hesaplayarak seçilebilir.

Bir pivot seçmenin saf bir uygulaması, ilk veya son öğe olacaktır. Bu durumda en kötü durum çalışma zamanı, giriş dizisinin halihazırda sıralandığı veya ters sıralandığı zaman olacaktır, çünkü alt dizilerden biri boş olacaktır ve bu da yinelemeli çağrı başına yalnızca bir elemanın kaldırılmasına neden olacaktır.

Pivot, dizinin medyan öğesi olduğunda mükemmel dengelenmiş bir bölünme elde edilir. Eşit sayıda ondan daha büyük ve ondan daha az eleman vardır. Bu yaklaşım daha iyi bir genel çalışma süresi garanti eder, ancak çok daha fazla zaman alır.

Pivotu seçmenin deterministik olmayan / rastgele bir yolu, bir öğeyi tek tip olarak rastgele seçmek olacaktır. Bu, en kötü durum senaryosunu en aza indirecek ve aynı zamanda kabaca dengeli bir bölünmeye yol açacak basit ve hafif bir yaklaşımdır. Bu aynı zamanda naif yaklaşım ile pivot seçiminin medyan yaklaşımı arasında bir denge sağlayacaktır.


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)

Bu kod soruna bir çözüm sağlayabilir ancak bu kodun soruyu neden ve / veya nasıl yanıtladığına ilişkin ek bağlam sağlamanız şiddetle önerilir. Yalnızca kod yanıtları genellikle uzun vadede işe yaramaz hale gelir çünkü benzer sorunları yaşayan gelecekteki izleyiciler çözümün arkasındaki mantığı anlayamaz.
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
Lütfen kodunuzun ne yaptığı ve soruyu nasıl yanıtladığı ile ilgili açıklama ekleyin. Özellikle soruda yayınlanan kodla nasıl bir ilgisi var? Yanıt, OP'ye ve gelecekteki ziyaretçilere sorunlarının nasıl giderileceği ve nasıl düzeltileceği konusunda rehberlik etmelidir. Kodunuzun arkasındaki fikrin ne olduğuna işaret etmek, sorunu anlamanıza ve çözümünüzü uygulamaya veya değiştirmenize büyük ölçüde yardımcı olur. Stack Overflow bir kod yazma hizmeti değil, bir öğretme ve öğrenme yeridir.
Palec

1

"Gerçek" yerinde uygulama [Algorithms 8.9, 8.11, Algorithm Design and Applications Book, Michael T. Goodrich ve Roberto Tamassia'dan]:

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

Algoritmanın 4 basit adımı vardır:

  1. Diziyi 3 farklı bölüme ayırın: left, pivot ve right, burada pivot yalnızca bir öğeye sahip olacaktır. Bu pivot elemanını dizinin ilk elemanı olarak seçelim
  2. Öğeleri, pivot öğeyle karşılaştırarak ilgili parçaya ekleyin. (yorumlarda açıklama)
  3. Dizideki tüm öğeler sıralanana kadar bu algoritmayı tekrarlayın
  4. Son olarak, sol + pivot + sağ bölümleri birleştirin

Python'daki algoritmanın kodu:

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

Sol ve sağ kısımlarla bu algoritmayı tekrarlı olarak devam ettirin.


1

Başka bir hızlı sıralama uygulaması:

# 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

Sürüm Python 3.x için : operatoröncelikle okunabilirliği artırmak için modül kullanan işlevsel bir stil .

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

ve olarak test edildi

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

Nice ( belgelenmemiş kod giderse), eğer acarca'nınkine benziyorsa , Arnaldo P. Figueira Figueira'nın ve Birger'in yıllardır verdiği cevaplar. Soru okunduğunda bunun bir cevap olduğundan emin değilim … complete my code?. Tanımlamak lambdaiçin kullanmak sublist()kesinlikle gerekli görünmüyor.
gri sakal

@greybeard Aslında Arnaldo'nun kodu Python 3'te derlenmedi. Ayrıca nasıl sublistsadece kullanılarak tanımlanabilir filter? Bu mümkün mü?
lifebalance

1
(Geçici yorum: Düşünmek def- henüz bir yinelenebilir öğenin öğelerini ayrı listelere (veya alternatif olarak, bir listeyi içeren tek bir listeye ")" dağıtmanın "daha etkili bir yolu olup olmadığını anlamaya çalıştığım için üzerinde oynama yapmaya başlamadı. birbiri ardına kategori")).
Greybeard

1

İşte kolay bir uygulama: -

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. Öncelikle dizideki ilk değeri pivot_value olarak bildiririz ve ayrıca sol ve sağ işaretleri de belirleriz
  2. İlk while döngüsünü oluşturuyoruz, bu while döngüsü bölümleme işleminin gerekli koşulu karşılamıyorsa tekrar çalışmasını söylemek için var
  3. sonra bölümleme sürecini uygularız
  4. her iki bölüm işlemi de çalıştıktan sonra, uygun koşulu karşılayıp karşılamadığını kontrol ederiz. Varsa bitti olarak işaretleriz, yoksa sağ ve sol değerleri değiştirip tekrar uygularız.
  5. Tamamlandığında, sol ve sağ değerleri değiştirin ve split_point'i döndürün

Aşağıdaki kodu ekliyorum! Bu hızlı sıralama, pivot değerinin Konumu nedeniyle harika bir öğrenme aracıdır . Sabit bir yerde olduğu için, birden çok kez yürüyebilir ve gerçekten nasıl çalıştığını gerçekten anlayabilirsiniz. Pratikte, O (N ^ 2) çalışma süresinden kaçınmak için pivotu rastgele hale getirmek en iyisidir.

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 diğer yanıt, bunların yarısından fazlası OP'nin "üç dizinin nasıl birleştirileceği" sorusuna yanıt veriyor. Cevabınız yeni bir şey katıyor mu?
Teepeemm

0

Bölüm adımında yazdırılan değişkenlerle tam örnek:

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
lütfen biraz açıklama yapın
Rai

0

Bu algoritma yinelemeli işlevleri kullanmaz.

Izin Nvermek herhangi bir sayı listesi olabilir len(N) > 0. K = [N]Aşağıdaki programı ayarlayın ve çalıştırın.

Not: Bu kararlı bir sıralama algoritmasıdır.

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)

PROGRAM ÇIKIŞI:

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

Muhtemelen sadece bir tercih meselesi, ancak işlevlerimin üstüne doğrulama eklemeyi seviyorum.

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)
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.