Quicksort avec Python

92

Je suis totalement nouveau sur python et j'essaye d'y implémenter quicksort. Quelqu'un pourrait-il m'aider à compléter mon code?

Je ne sais pas comment concaténer les trois tableaux et les imprimer.

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)
user2687481
la source
Pour combiner des listes, vous pouvez utiliser l'opérateur plus my_list = list1 + list2 + .... Ou décompressez les listes dans une nouvelle listemy_list = [*list1, *list2]
Mark Mishyn

Réponses:

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
Brionius
la source
8
Vous pouvez également échanger les 2e ifs de la boucle for avec elifet elsepour éviter de faire des comparaisons inutiles
SlimPDX
13
Cela ressemble à un tri de fusion pas à un tri rapide
Emad Mokhtar
47
C'est en fait le code python le meilleur et le plus lisible que j'ai trouvé pour le tri rapide n'importe où . Pas d'indices, pas de fonctions d'assistance, montre clairement l'essentiel de l'algorithme (diviser et conquérir). (La valeur par défaut du tableau est plutôt inutile)
cmantas
19
@jsmedmar il utilisera plus de mémoire qu'une version en place, voir la réponse de suquant pour un tri rapide sur place.
John
14
Très lisible, mais cela ne va-t-il pas à l'encontre de l'objectif du tri rapide puisque cela n'atteindra pas le tri «en place»? @RasmiRanjanNayak sort ici est la fonction définie par l'utilisateur (c'est un appel récursif), pas une fonction intégrée.
Maksood
161

Tri rapide sans mémoire supplémentaire (en place)

Usage:

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)
suquant
la source
23
Cela devrait être la réponse choisie, car le tri rapide est souvent l'algorithme de choix (par exemple, le tri par fusion) car il trie sur place (et donne toujours le temps d'exécution O (nlogn)).
BoltzmannBrain
3
if end is None:va être vérifié plusieurs fois, et une seule fois True. Vous devriez le mettre dans une fonction wrapper pour qu'il ne soit appelé qu'une seule fois.
Gillespie
Ackchyually, bruhs, @mksteve a raison et cette ligne est incorrecte. De plus, array[pivot], array[begin] = array[begin], array[pivot]devrait remplacer beginpar end.
Ryan J McCall
2
Bien que la mise en place soit bonne, elle est lente et entraîne des erreurs en raison de l'atteinte de la profondeur de récursivité maximale lorsqu'il y a beaucoup d'éléments. voir repl.it/@almenon/quicksorts?language=python3
Almenon
@mksteve et Ryan, j'ai testé ces changements et ça casse le tri
aljgom
68

Il existe une autre version concise et belle

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

Laissez-moi vous expliquer les codes ci-dessus pour plus de détails

  1. choisissez le premier élément du tableau arr[0]comme pivot

    [arr[0]]

  2. qsort les éléments du tableau qui sont inférieurs à pivoter avec List Comprehension

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

  3. qsort les éléments du tableau qui sont plus grands que pivotent avec List Comprehension

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

zangw
la source
15
@zangw raisons possibles d'un vote négatif: 1) Exécution quadratique sur des tableaux déjà triés ou inversés 2) La solution n'est pas en place. Par conséquent, une mise en œuvre terrible, désolé.
alisianoi
16
pas du tout lisible, essayez-vous vraiment de minimiser le nombre de lignes? Le code est interprété par les machines, mais compris par les humains.
jsmedmar
4
@AlfredoGallegos, tout l'intérêt du tri rapide est qu'il se produit sur place, vous pouvez aussi bien implémenter le tri par fusion si vous voulez le faire.
Padraic Cunningham
13
Ces commentaires sont-ils réels? Si vous voulez des performances, utilisez sorted, c'est clairement à des fins éducatives. Et il est lisible, plus lisible que la réponse acceptée.
Nobilis
3
FWIW, je pensais que c'était l'implémentation la plus lisible de toutes. Il montre la structure récursive de l'algorithme mieux que toute autre réponse. Bien sûr, les performances ne seront pas trop bonnes.
SolveIt
35

Cette réponse est un tri rapide sur place pour Python 2.x. Ma réponse est une interprétation de la solution en place de Rosetta Code qui fonctionne Python 3aussi pour :

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)

Et si vous êtes prêt à renoncer à la propriété in-situ, voici une autre version qui illustre mieux les idées de base derrière le tri rapide. Outre la lisibilité, son autre avantage est qu'il est stable (les éléments égaux apparaissent dans la liste triée dans le même ordre qu'ils avaient dans la liste non triée). Cette propriété de stabilité ne tient pas avec l'implémentation sur place moins gourmande en mémoire présentée ci-dessus.

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
alisianoi
la source
Merci de partager cette solution. Pouvez-vous nous aider à comprendre la complexité du temps? Je vois que la récursion l'appellera 15 fois. De ces 8 sont des appels valides à la fonction. Cela signifie-t-il que la complexité temporelle est O (n) pour la première solution et que la complexité spatiale est O (1) comme tri sur place?
ForeverLearner
@Tammy il semble que vous comprenez mal la notation big-O. De plus, je ne comprends pas vraiment votre question. Pourriez-vous peut-être la poser séparément? Enfin, Quicksort en tant qu'algorithme s'exécute en temps O (n logn) et dans l'espace O (n).
alisianoi
3
Ma faute. Pourquoi diable ai-je compté les récursions ?? :-) Eh bien, 15 récursions sont [1 appel (niveau 0) + 2 appel (partition de niveau 1) + 4 appel (partition de niveau 2) + 8 appel (partition de niveau 3 ou nœuds feuille). Donc, nous avons toujours la hauteur comme (lg8 + 1) = lgn. Le calcul total dans chaque niveau est pour c1 (un certain coût) * n. D'où O (n lgn). Complexité spatiale, pour un échange sur place = O (1). Donc pour n éléments = O (n). Merci pour le pointeur.
ForeverLearner
3
c'est de loin le meilleur tri rapide de python sur Internet, et il est triste de le voir enterré sous autant de solutions spatiales O (n) :(
Sasha Kondrashov
Merci pour les aimables paroles @Timofey. Vous voudrez peut-être jeter un œil à mon référentiel d'algorithmes, il a d'autres versions de sortes, algorithmes de graphes, etc. etc. github.com/alisianoi/algos-py
alisianoi
23

Quicksort avec Python

Dans la vraie vie, nous devrions toujours utiliser le tri intégré fourni par Python. Cependant, comprendre l' algorithme de tri rapide est instructif.

Mon but ici est de décomposer le sujet de manière à ce qu'il soit facilement compris et reproductible par le lecteur sans avoir à revenir sur les matériaux de référence.

L'algorithme de tri rapide est essentiellement le suivant:

  1. Sélectionnez un point de données pivot.
  2. Déplacez tous les points de données inférieurs (au-dessous) du pivot vers une position sous le pivot - déplacez ceux supérieurs ou égaux à (au-dessus) du pivot vers une position au-dessus.
  3. Appliquer l'algorithme aux zones au-dessus et en dessous du pivot

Si les données sont distribuées de manière aléatoire, la sélection du premier point de données comme pivot équivaut à une sélection aléatoire.

Exemple lisible:

Tout d'abord, regardons un exemple lisible qui utilise des commentaires et des noms de variables pour pointer vers des valeurs intermédiaires:

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

Pour reformuler l'algorithme et le code illustrés ici, nous déplaçons les valeurs au-dessus du pivot vers la droite et les valeurs sous le pivot vers la gauche, puis transmettons ces partitions à la même fonction pour être triées davantage.

Golfé:

Cela peut être joué à 88 caractères:

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

Pour voir comment nous y arrivons, prenez d'abord notre exemple lisible, supprimez les commentaires et les docstrings, et recherchez le pivot sur place:

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

Trouvez maintenant ci-dessous et ci-dessus, en place:

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

Maintenant, sachant que andrenvoie l'élément précédent si faux, sinon s'il est vrai, il évalue et renvoie l'élément suivant, nous avons:

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

Puisque les lambdas renvoient une seule expression, et que nous avons simplifié à une seule expression (même si elle devient de plus en plus illisible), nous pouvons maintenant utiliser un lambda:

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

Et pour réduire à notre exemple, raccourcissez les noms des fonctions et des variables à une lettre et éliminez les espaces qui ne sont pas nécessaires.

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

Notez que ce lambda, comme la plupart des jeux de code, est plutôt mauvais.

Tri rapide sur place, à l'aide du schéma de partitionnement Hoare

L'implémentation précédente crée beaucoup de listes supplémentaires inutiles. Si nous pouvons le faire sur place, nous éviterons de gaspiller de l'espace.

L'implémentation ci-dessous utilise le schéma de partitionnement Hoare, sur lequel vous pouvez en savoir plus sur wikipedia (mais nous avons apparemment supprimé jusqu'à 4 calculs redondants par partition()appel en utilisant la sémantique en boucle while au lieu de do-while et en déplaçant les étapes de réduction à la fin de la boucle while externe.).

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

Je ne sais pas si je l'ai suffisamment testé:

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]

Conclusion

Cet algorithme est fréquemment enseigné dans les cours d'informatique et demandé lors des entretiens d'embauche. Cela nous aide à penser à la récursivité et à diviser pour conquérir.

Quicksort n'est pas très pratique en Python car notre algorithme de tri temporel intégré est assez efficace et nous avons des limites de récursivité. Nous nous attendrions à trier les listes sur place avec list.sortou à créer de nouvelles listes triées avec sorted- les deux prenant un argument keyet reverse.

Salle Aaron
la source
Votre partitionfonction semble pas fonctionner correctement pour: partition([5,4,3,2,1], 0, 4). L'indice de retour attendu est 4, alors qu'il renvoie 3.
matino
@matino D'où vient cette attente? Je crois que j'ai simplifié l'algorithme (comme indiqué sur wikipedia lors de la rédaction de cette réponse), bien que je puisse me tromper, ou peut-être moins efficace. Si vous pouviez trouver un cas de test pour lequel toute la fonction de tri rapide échoue, ce serait utile.
Aaron Hall
@AaronHall quand j'ai choisi pivot = a_list [high] mais je ne peux tout simplement pas comprendre comment le faire fonctionner alors. Pouvez-vous m'aider?
Qiulang
@matino j'ai eu la même confusion! La fonction de partition est correcte, l'invariant qu'il satisfait est plus faible que ce que vous attendez - il n'est pas nécessaire de trouver l'endroit exact qui sépare la gauche et la droite en plus petit et plus grand que le pivot. Il garantit seulement une partition non triviale et que toute la gauche de l'index retourné est plus petite que la droite de l'index retourné.
Dror Speiser le
21

Il existe déjà de nombreuses réponses à cela, mais je pense que cette approche est la mise en œuvre la plus propre:

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

Vous pouvez bien sûr ignorer tout stocker dans des variables et les renvoyer immédiatement comme ceci:

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]])
Sebastian Dahlgren
la source
11
SUR!)? est-ce un «slowSort»?
Scott 混合 理论
3
Je crois que dans le premier exemple de code, il devrait être «moindre» et «supérieur» au lieu de «[moindre]» et «[supérieur]» - sinon vous vous retrouverez avec des listes imbriquées au lieu d'une liste plate.
Alice le
@Scott 混合 理论 J'apprends toujours la complexité du temps, pouvez-vous expliquer pourquoi cette implémentation est O(N!)? En supposant que la liste imbriquée [lesser]et qu'il [greater]y ait des fautes de frappe, ne serait-ce pas une moyenne O(3N logN)qui se réduirait à la moyenne prévue O(N logN)? Certes, les 3 compositions de la liste font un travail inutile.
Chrispy
@Chrispy et si vous triez une liste d'ordre inversé, comme 5,4,3,2,1
Scott 混合 理论
@Scott 混合 理论 vous avez raison de dire que le temps d'exécution dans le pire des cas du tri rapide est lent Θ (n ^ 2), mais selon "introduction à l'algorithme", le temps d'exécution moyen est Θ (n lg n). Et, plus important encore, le tri rapide surpasse généralement le tri en tas dans la pratique
Lungang Fang
6

Approche fonctionnelle:

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

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

    return qsort(left) + [pivot] + qsort(right)
Akarca
la source
la liste est réservée en python 3. voir la version modifiée de votre code ici: gist.github.com/kunthar/9d8962e1438e93f50dc6dd94d503af3d
Kunthar
akarca et @Kunthar Ces deux solutions en python2 ou python3 feront apparaître un élément de la liste à chaque exécution, détruisant ainsi la liste. Voici une version corrigée
joshuatvernon
4

approche de programmation fonctionnelle

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]
Arnaldo P. Figueira Figueira
la source
4

Implémentation facile à partir d'algorithmes de 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)
Alice
la source
3

Je pense que les deux réponses ici fonctionnent bien pour la liste fournie (qui répondent à la question d'origine), mais se casseraient si un tableau contenant des valeurs non uniques est passé. Donc, pour être complet, je voudrais simplement souligner la petite erreur dans chacun et expliquer comment les corriger.

Par exemple, essayer de trier le tableau suivant [12,4,5,6,7,3,1,15,1] (notez que 1 apparaît deux fois) avec l' algorithme de Brionius .. à un moment donné, le tableau le moins vide sera et le tableau égal avec une paire de valeurs (1,1) qui ne peuvent pas être séparées dans l'itération suivante et le len ()> 1 ... donc vous vous retrouverez avec une boucle infinie

Vous pouvez le corriger en retournant un tableau si less est vide ou mieux en n'appelant pas sort dans votre tableau égal, comme dans zangw answer

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

La solution plus sophistiquée se brise également, mais pour une cause différente, il manque la clause de retour dans la ligne de récursivité, ce qui provoquera à un moment donné le renvoi None et une tentative de l'ajouter à une liste ...

Pour résoudre ce problème, ajoutez simplement un retour à cette ligne

def qsort(arr): 
   if len(arr) <= 1:
      return arr
   else:
      return qsort([x for x in arr[1:] if x<arr[0]]) + [arr[0]] + qsort([x for x in arr[1:] if x>=arr[0]])
FedeN
la source
À propos, la version concise a moins de performances que la version longue, car elle itère le tableau deux fois dans la liste des compréhensions.
FedeN
3

Ceci est une version du tri rapide utilisant le schéma de partition Hoare et avec moins de swaps et de variables locales

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)
Madu Alikor
la source
3

Partition - Divisez un tableau par un pivot pour que les éléments plus petits se déplacent vers la gauche et les éléments plus grands se déplacent vers la droite ou vice versa. Un pivot peut être un élément aléatoire d'un tableau. Pour créer cet algorithme, nous devons savoir ce qu'est l'index de début et de fin d'un tableau et où se trouve un pivot. Ensuite, définissez deux pointeurs auxiliaires L, R.

Nous avons donc un utilisateur de tableau [..., begin, ..., end, ...]

La position de départ des pointeurs L et R
[..., begin, next, ..., end, ...]
     R L

while L <end
  1. Si un utilisateur [pivot]> user [L], déplacez R de un et permutez l'utilisateur [R] avec l'utilisateur [L]
  2. déplacez L par un

Après un certain temps, permutez l'utilisateur [R] avec l'utilisateur [pivot]

Tri rapide - Utilisez l'algorithme de partition jusqu'à ce que chaque partie suivante de la division par un pivot ait un index de début supérieur ou égal à l'index de fin.

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)
Grzegorz Eleryk
la source
Veuillez expliquer votre code / ajouts afin que OP et les vues futures puissent en bénéficier davantage.
Omar Einea le
2

Ou si vous préférez un one-liner qui illustre également l'équivalent Python des varags C / C ++, des expressions lambda et des expressions if:

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])
Bruce Lucas
la source
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)
MoeChen
la source
2

Je sais que beaucoup de gens ont répondu correctement à cette question et j'ai aimé les lire. Ma réponse est presque la même que zangw mais je pense que les contributeurs précédents n'ont pas bien expliqué visuellement comment les choses fonctionnent réellement ... alors voici ma tentative d'aider les autres qui pourraient visiter cette question / réponses à l'avenir à propos d'un solution simple pour la mise en œuvre de tri rapide.

Comment ça marche ?

  1. Nous sélectionnons essentiellement le premier élément comme pivot dans notre liste, puis nous créons deux sous-listes.
  2. Notre première sous-liste contient les éléments qui sont inférieurs à pivot
  3. Notre deuxième sous-liste contient nos articles qui sont supérieurs ou égaux à pivot
  4. Nous trions ensuite rapidement chacun de ceux-ci et nous les combinons le premier groupe + pivot + le deuxième groupe pour obtenir le résultat final.

Voici un exemple avec visuel pour aller avec ... (pivot) 9,11,2,0

moyenne: n log de n

pire cas: n ^ 2

entrez la description de l'image ici

Le code:

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 (tri rapide (articles))

grepit
la source
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 []
dapangmao
la source
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
Amy
la source
1
Veuillez expliquer ce que fait votre code et comment il répond à la question. Surtout comment cela se rapporte-t-il au code affiché dans la question. La réponse devrait donner à l'OP et aux futurs visiteurs des conseils sur la façon de déboguer et de résoudre leur problème. Indiquer quelle est l'idée derrière votre code aide grandement à comprendre le problème et à appliquer ou modifier votre solution. Stack Overflow n'est pas un service d'écriture de code, c'est un lieu d'enseignement et d'apprentissage.
Palec
1

Une implémentation «vraie» sur place [Algorithmes 8.9, 8.11 du livre de conception et d'applications d'algorithmes de Michael T. Goodrich et Roberto Tamassia]:

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()
anask
la source
1

L'algorithme comporte 4 étapes simples:

  1. Divisez le tableau en 3 parties différentes: gauche, pivot et droite, où pivot n'aura qu'un seul élément. Choisissons cet élément pivot comme premier élément du tableau
  2. Ajoutez des éléments à la pièce respective en les comparant à l'élément pivot. (explication dans les commentaires)
  3. Réexécutez cet algorithme jusqu'à ce que tous les éléments du tableau aient été triés
  4. Enfin, joignez les parties gauche + pivot + droite

Code pour l'algorithme en 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])

Continuez avec cet algorithme de manière récursive avec les parties gauche et droite.

Mamta Kanvinde
la source
1

Une autre implémentation de tri rapide:

# 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)
Gillespie
la source
1

Pour la version Python 3.x : un operatormodule utilisant de style fonctionnel , principalement pour améliorer la lisibilité.

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

et est testé comme

print (qsort([3,1,4,2,5]) == [1,2,3,4,5])
équilibre de vie
la source
Nice (jusqu'à un code non documenté va), si semblable à acarca de , de P. Arnaldo Figueira Figueira et pour Birger réponses des années passées. Pas sûr que ce soit une réponse quand la question se lit … complete my code?. Utiliser lambdapour définir sublist()ne semble pas strictement nécessaire.
greybeard
@greybeard En fait, le code d'Arnaldo ne s'est pas compilé en Python 3. Aussi, comment peut- subliston définir uniquement en utilisant filter? Est-ce que c'est possible?
lifebalance
1
(Commentaire temporaire: penser à def- je n'ai pas encore commencé à bricoler car j'essaie de déterminer s'il existe un moyen plus efficace de "distribuer" les éléments d'un itérable pour séparer les listes (ou, alternativement, une liste avec une " catégorie "après l'autre).)
greybeard
1

Voici une implémentation simple: -

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]))
abhishek4996
la source
1

L'algorithme contient deux limites, l'une ayant des éléments inférieurs au pivot (suivi par l'index "j") et l'autre ayant des éléments supérieurs au pivot (suivi par l'index "i").

À chaque itération, un nouvel élément est traité en incrémentant j.

Invariant: -

  1. tous les éléments entre pivot et i sont inférieurs au pivot, et
  2. tous les éléments entre i et j sont supérieurs au pivot.

Si l'invariant est violé, les ième et jième éléments sont permutés et i est incrémenté.

Une fois que tous les éléments ont été traités, et tout après le partitionnement du pivot, l'élément pivot est échangé avec le dernier élément plus petit que lui.

L'élément pivot sera maintenant à sa place correcte dans la séquence. Les éléments avant lui seront inférieurs à lui et ceux qui après lui seront supérieurs, et ils ne seront pas triés.

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

Sélection d'un pivot

Un "bon" pivot se traduira par deux sous-séquences à peu près de la même taille. De manière déterministe, un élément pivot peut être sélectionné soit de manière naïve, soit en calculant la médiane de la séquence.

Une implémentation naïve de la sélection d'un pivot sera le premier ou le dernier élément. Le pire des cas d'exécution dans ce cas sera lorsque la séquence d'entrée est déjà triée ou triée inversée, car l'une des sous-séquences sera vide, ce qui entraînera la suppression d'un seul élément par appel récursif.

Une répartition parfaitement équilibrée est obtenue lorsque le pivot est l'élément médian de la séquence. Il y a un nombre égal d'éléments supérieur à lui et inférieur à lui. Cette approche garantit une meilleure durée de fonctionnement globale, mais prend beaucoup plus de temps.

Une manière non déterministe / aléatoire de sélectionner le pivot serait de choisir un élément uniformément au hasard. Il s'agit d'une approche simple et légère qui minimisera le pire des cas et conduira également à une répartition à peu près équilibrée. Cela fournira également un équilibre entre l'approche naïve et l'approche médiane de sélection du pivot.


la source
0
  1. Tout d'abord, nous déclarons que la première valeur du tableau est la valeur pivot_value et nous définissons également les marques gauche et droite
  2. Nous créons la première boucle while, cette boucle while est là pour dire au processus de partition de s'exécuter à nouveau s'il ne satisfait pas la condition nécessaire
  3. puis nous appliquons le processus de partition
  4. une fois que les deux processus de partition ont été exécutés, nous vérifions s'il satisfait à la bonne condition. Si c'est le cas, nous le marquons comme terminé, sinon nous inversons les valeurs gauche et droite et l'appliquons à nouveau
  5. Une fois que c'est fait, changez les valeurs gauche et droite et renvoyez le split_point

Je joins le code ci-dessous! Ce tri rapide est un excellent outil d'apprentissage en raison de l' emplacement de la valeur pivot . Comme il est dans un endroit constant, vous pouvez le parcourir plusieurs fois et vraiment comprendre comment tout cela fonctionne. En pratique, il est préférable de randomiser le pivot pour éviter l'exécution 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
DanHabib
la source
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
feroz
la source
18 autres réponses, dont plus de la moitié répondent à la question initiale de OP sur "comment concaténer les trois tableaux". Votre réponse ajoute-t-elle quelque chose de nouveau?
Teepeemm
0

Exemple complet avec des variables imprimées à l'étape de partition:

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))
Andrei Sura
la source
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()
Igor Mansurov
la source
1
veuillez fournir une explication
Rai
0

Cet algorithme n'utilise pas de fonctions récursives.

Soit Nune liste de nombres avec len(N) > 0. Définissez K = [N]et exécutez le programme suivant.

Remarque: il s'agit d'un algorithme de tri stable .

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)

SORTIE DU PROGRAMME:

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]
CopyPasteIt
la source
0

Probablement juste une chose de préférence, mais j'aime ajouter la validation en haut de mes fonctions.

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)
Robo Rick
la source
0

Ma réponse est très similaire à la grande de @alisianoi. Cependant, je pense qu'il y a une légère inefficacité dans son code (voir mon commentaire), que j'ai supprimé. De plus, j'ai ajouté plus d'explications et j'étais un peu plus précis sur le problème des valeurs en double (pivot).

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
gebbissimo
la source