Clasificación rápida con Python


94

Soy totalmente nuevo en Python y estoy tratando de implementar quicksort en él. ¿Podría alguien ayudarme a completar mi código?

No sé cómo concatenar las tres matrices e imprimirlas.

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)

Para combinar listas, puede usar el operador más my_list = list1 + list2 + .... O descomprima listas en una nueva listamy_list = [*list1, *list2]
Mark Mishyn

Respuestas:


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
También puede cambiar el segundo ifs en el bucle for con elify elsepara evitar comparaciones innecesarias
SlimPDX

13
Esto suena como una combinación de clasificación, no una clasificación rápida
Emad Mokhtar

47
En realidad, es el mejor y más legible código de Python que encontré para clasificación rápida en cualquier lugar . Sin índices, sin funciones auxiliares, muestra claramente la esencia del algoritmo (divide y vencerás). (El valor predeterminado para la matriz es bastante innecesario)
cmantas

20
@jsmedmar utilizará más memoria que una versión en el lugar, consulte la respuesta de suquant para una clasificación rápida en el lugar.
John

15
Muy legible, pero ¿no anula esto el propósito de la clasificación rápida, ya que no logrará la clasificación "en el lugar"? @RasmiRanjanNayak sort aquí es la función definida por el usuario (es una llamada recursiva), no una función incorporada.
Maksood

161

Clasificación rápida sin memoria adicional (en su lugar)

Uso:

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
Esta debería ser la respuesta seleccionada, ya que la ordenación rápida es a menudo el algoritmo de elección (sobre, por ejemplo, la ordenación por combinación) porque ordena en su lugar (y aún da tiempo de ejecución O (nlogn)).
BoltzmannBrain

3
if end is None:va a ser revisado muchas veces, y solo una vez True. Debe poner esto en una función contenedora para que solo se llame una vez.
Gillespie

Ackchyually, bruhs, @mksteve tiene razón y esta línea es incorrecta. Además, array[pivot], array[begin] = array[begin], array[pivot]debe reemplazarse begincon end.
Ryan J McCall

2
Aunque en el lugar es bueno, esto es lento y genera errores debido a alcanzar la profundidad máxima de recursividad cuando hay muchos elementos. ver repl.it/@almenon/quicksorts?language=python3
Almenon

@mksteve y Ryan, probé estos cambios y rompe las clasificaciones
aljgom

68

Hay otra versión concisa y hermosa.

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

Déjame explicarte los códigos anteriores para obtener más detalles.

  1. elige el primer elemento de la matriz arr[0]como pivote

    [arr[0]]

  2. qsort aquellos elementos de la matriz que son menos de pivote con List Comprehension

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

  3. qsort aquellos elementos de la matriz que son más grandes que pivote con List Comprehension

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


15
@zangw posibles razones para un voto negativo: 1) Tiempo de ejecución cuadrático en matrices ya ordenadas o invertidas 2) La solución no está en su lugar. Por lo tanto, una implementación terrible, lo siento.
alisianoi

16
no es legible en absoluto, ¿realmente está tratando de minimizar el número de líneas? El código es interpretado por máquinas, pero entendido por humanos.
jsmedmar

4
@AlfredoGallegos, el objetivo de quicksort es que ocurre en su lugar, también puede implementar merge-sort si va a hacer esto.
Padraic Cunningham

14
¿Estos comentarios son reales? Si desea rendimiento, use sorted, esto es claramente con fines educativos. Y es legible, más legible que la respuesta aceptada.
Nobilis

4
FWIW, pensé que esta era la implementación más legible de todas. Muestra la estructura recursiva del algoritmo mejor que cualquier otra respuesta. Por supuesto, el rendimiento no será demasiado bueno.
SolveIt

37

Esta respuesta es un QuickSort in situ para Python 2.x. Mi respuesta es una interpretación de la solución en el lugar de Rosetta Code que también funciona para 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)

Y si está dispuesto a renunciar a la propiedad in situ, a continuación se muestra otra versión que ilustra mejor las ideas básicas detrás de la ordenación rápida. Además de la legibilidad, su otra ventaja es que es estable (los elementos iguales aparecen en la lista ordenada en el mismo orden que solían tener en la lista sin clasificar). Esta propiedad de estabilidad no se mantiene con la implementación local que consume menos memoria y que se presentó anteriormente.

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

Gracias por compartir esta solución. ¿Puede ayudarnos a comprender la complejidad del tiempo? Veo que la recursividad lo llamará 15 veces. De estos, 8 son llamadas válidas a la función. ¿Significa eso que la complejidad del tiempo es O (n) para la primera solución y la complejidad del espacio es O (1) como su tipo en el lugar?
ForeverLearner

@Tammy, parece que no has entendido bien la notación de O grande. Además, realmente no entiendo tu pregunta. ¿Podrías preguntarlo por separado? Finalmente, Quicksort como algoritmo se ejecuta en tiempo O (n logn) y espacio O (n).
alisianoi

3
Culpa mía. ¿Por qué diablos estaba contando recursiones? :-) Bueno, 15 recursiones son [1 llamada (Nivel 0) + 2 llamadas (Partición de Nivel 1) + 4 llamadas (Partición de Nivel 2) + 8 llamadas (Partición de Nivel 3 o nodos Hoja). Entonces, todavía tenemos la altura como (lg8 + 1) = lgn. El cálculo total en cada nivel es para c1 (algún costo) * n. Por tanto, O (n lgn). Complejidad del espacio, para un intercambio in situ = O (1). Por tanto, para n elementos = O (n). Gracias por la anotación.
ForeverLearner

3
esta es de lejos la mejor selección rápida de Python en Internet, y es triste verla enterrada bajo tantas soluciones espaciales O (n) :(
Sasha Kondrashov

Gracias por las amables palabras @Timofey. Es posible que desee echar un vistazo a mi repositorio de algoritmos, tiene otros tipos de versiones, algoritmos de gráficos, etc., etc. github.com/alisianoi/algos-py
alisianoi

23

Clasificación rápida con Python

En la vida real, siempre deberíamos usar el tipo integrado proporcionado por Python. Sin embargo, comprender el algoritmo de clasificación rápida es instructivo.

Mi objetivo aquí es desglosar el tema de tal manera que el lector pueda entenderlo y reproducirlo fácilmente sin tener que volver a los materiales de referencia.

El algoritmo de clasificación rápida es esencialmente el siguiente:

  1. Seleccione un punto de datos de pivote.
  2. Mueva todos los puntos de datos menores (debajo) del pivote a una posición debajo del pivote; mueva los puntos mayores o iguales (arriba) del pivote a una posición por encima de este.
  3. Aplicar el algoritmo a las áreas por encima y por debajo del pivote

Si los datos se distribuyen aleatoriamente, seleccionar el primer punto de datos como pivote equivale a una selección aleatoria.

Ejemplo legible:

Primero, veamos un ejemplo legible que usa comentarios y nombres de variables para señalar valores intermedios:

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

Para reformular el algoritmo y el código que se muestran aquí, movemos los valores por encima del pivote a la derecha y los valores por debajo del pivote a la izquierda, y luego pasamos esas particiones a la misma función para que se clasifiquen más.

Jugado al golf:

Esto se puede jugar al golf hasta 88 caracteres:

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

Para ver cómo llegamos allí, primero tome nuestro ejemplo legible, elimine los comentarios y las cadenas de documentos, y busque el pivote en su lugar:

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

Ahora busque abajo y arriba, en el lugar:

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

Ahora, sabiendo que anddevuelve el elemento anterior si es falso, de lo contrario si es verdadero, evalúa y devuelve el siguiente elemento, tenemos:

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

Dado que las lambdas devuelven una sola expresión, y lo hemos simplificado a una sola expresión (aunque se está volviendo más ilegible) ahora podemos usar una 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]]))

Y para reducir a nuestro ejemplo, acorte los nombres de las funciones y variables a una letra y elimine los espacios en blanco que no son necesarios.

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

Tenga en cuenta que esta lambda, como la mayoría de los códigos de golf, tiene un estilo bastante malo.

Ordenación rápida en el lugar, utilizando el esquema de particionamiento Hoare

La implementación anterior crea muchas listas adicionales innecesarias. Si podemos hacer esto en el lugar, evitaremos desperdiciar espacio.

La siguiente implementación usa el esquema de partición de Hoare, sobre el cual puede leer más en wikipedia (pero aparentemente hemos eliminado hasta 4 cálculos redundantes por partition()llamada usando la semántica de bucle while en lugar de do-while y moviendo los pasos de reducción al final de el bucle externo 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

No estoy seguro si lo probé lo suficientemente a fondo:

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]

Conclusión

Este algoritmo se enseña con frecuencia en cursos de informática y se solicita en entrevistas de trabajo. Nos ayuda a pensar en la recursividad y el divide y vencerás.

Quicksort no es muy práctico en Python ya que nuestro algoritmo de ordenación de tiempos incorporado es bastante eficiente y tenemos límites de recursividad. Esperaríamos ordenar listas en el lugar con list.sorto crear nuevas listas ordenadas con sorted, las cuales toman un argumento keyy reverse.


Su partitionfunción parece no funcionar correctamente para: partition([5,4,3,2,1], 0, 4). El índice de rendimiento esperado es 4, mientras que devuelve 3.
matino

@matino ¿De dónde salió esa expectativa? Creo que simplifiqué el algoritmo (como se indica en wikipedia al escribir esta respuesta), aunque podría estar equivocado o quizás menos eficiente. Si pudiera encontrar un caso de prueba en el que falle toda la función de ordenación rápida, sería útil.
Aaron Hall

@AaronHall cuando elegí pivot = a_list [high] pero simplemente no puedo entender cómo hacerlo funcionar. Puede usted ayudar ?
Qiulang

@matino tuve la misma confusión! La función de partición está bien, el invariante que satisface es más débil de lo que esperas; no tiene que encontrar el lugar exacto que separa la izquierda y la derecha a más pequeño y más grande que el pivote. Solo garantiza una partición no trivial y que todo el índice devuelto a la izquierda sea más pequeño que el índice devuelto a la derecha.
Dror Speiser

21

Ya hay muchas respuestas a esto, pero creo que este enfoque es la implementación más limpia:

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

Por supuesto, puede omitir el almacenamiento de todo en variables y devolverlas de inmediato de esta manera:

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
¡EN!)? ¿Es esto un 'slowSort'?
Scott 混合 理论

3
Creo que en el primer ejemplo de código debería ser 'menor' y 'mayor' en lugar de '[menor]' y '[mayor]'; de lo contrario, terminará con listas anidadas en lugar de una plana.
Alice

@Scott 混合 理论 Todavía estoy aprendiendo la complejidad del tiempo, ¿puedes explicar por qué es esta implementación O(N!)? Suponiendo que la lista anidada [lesser]y [greater]hay errores tipográficos, ¿no sería un promedio O(3N logN)que se reduciría al promedio esperado O(N logN)? De acuerdo, las 3 composiciones de la lista hacen un trabajo innecesario.
Chrispy

@Chrispy, ¿qué pasa si ordena una lista de orden invertida, como 5,4,3,2,1?
Scott 混合

@Scott 混合 理论 tiene razón en que el tiempo de ejecución del peor caso de ordenación rápida es lento Θ (n ^ 2), pero de acuerdo con la "introducción al algoritmo", el tiempo de ejecución promedio del caso es Θ (n lg n). Y, lo que es más importante, la ordenación rápida generalmente supera a la ordenación por montón en la práctica
Lungang Fang

6

enfoque funcional:

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)

la lista está reservada en python 3. vea la versión modificada de su código aquí: gist.github.com/kunthar/9d8962e1438e93f50dc6dd94d503af3d
Kunthar

akarca y @Kunthar Ambas soluciones en python2 o python3 sacarán un elemento de la lista cada vez que se ejecute, por lo tanto, destruirán la lista. Aquí hay una versión fija: gist.github.com/joshuatvernon/634e0d912401899af0fdc4e23c94192b
joshuatvernon

4

enfoque de programación funcional

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

Fácil implementación a partir de algoritmos 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)

3

Creo que ambas respuestas aquí funcionan bien para la lista proporcionada (que responde a la pregunta original), pero se romperían si se pasa una matriz que contenga valores no únicos. Entonces, para completar, solo señalaría el pequeño error en cada uno y explicaría cómo solucionarlo.

Por ejemplo, intentar ordenar la siguiente matriz [12,4,5,6,7,3,1,15,1] (Tenga en cuenta que 1 aparece dos veces) con el algoritmo de Brionius ... en algún momento terminará con la matriz less vacía y la matriz igual con un par de valores (1,1) que no se pueden separar en la siguiente iteración y el len ()> 1 ... por lo tanto, terminará con un bucle infinito

Puede solucionarlo devolviendo la matriz si menos está vacío o mejor no llamando a sort en su matriz igual, como en la respuesta de zangw

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 solución más elegante también se rompe, pero por una causa diferente, falta la devolución. cláusula en la línea de recursividad, lo que hará que en algún momento devuelva None e intente agregarlo a una lista ...

Para solucionarlo, solo agregue un retorno a esa línea

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

Por cierto, la versión concisa tiene menos rendimiento que la larga, ya que está iterando la matriz dos veces en la lista de comprensiones.
FedeN

3

Esta es una versión de la ordenación rápida que usa el esquema de partición Hoare y con menos intercambios y 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)

3

Partición : divide una matriz mediante un pivote para que los elementos más pequeños se muevan hacia la izquierda y los elementos mayores se muevan hacia la derecha o viceversa. Un pivote puede ser un elemento aleatorio de una matriz. Para hacer este algoritmo, necesitamos saber qué es el índice inicial y final de una matriz y dónde está un pivote. Luego configure dos punteros auxiliares L, R.

Entonces tenemos un usuario de matriz [..., begin, ..., end, ...]

La posición inicial de los punteros L y R
[..., begin, next, ..., end, ...]
     R L

while L <end
  1. Si un usuario [pivote]> usuario [L], entonces mueva R en uno e intercambie usuario [R] con usuario [L]
  2. mueva L en uno

Después de while, intercambiar usuario [R] con usuario [pivote]

Clasificación rápida : use el algoritmo de partición hasta que cada parte siguiente de la división por un pivote tenga un índice inicial mayor o igual que el índice final.

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)

explique su código / adiciones para que OP y las vistas futuras puedan beneficiarse más.
Omar Einea

2

O si prefiere un resumen que también ilustre el equivalente en Python de varags de C / C ++, expresiones lambda y expresiones 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])

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

Sé que muchas personas han respondido correctamente a esta pregunta y disfruté leyéndolas. Mi respuesta es casi la misma que la de zangw, pero creo que los colaboradores anteriores no hicieron un buen trabajo al explicar visualmente cómo funcionan las cosas realmente ... así que aquí está mi intento de ayudar a otros que puedan visitar esta pregunta / respuestas en el futuro sobre un solución simple para la implementación de clasificación rápida.

Como funciona ?

  1. Básicamente, seleccionamos el primer elemento como nuestro pivote de nuestra lista y luego creamos dos sublistas.
  2. Nuestra primera sublista contiene los elementos que son menos dinámicos
  3. Nuestra segunda sublista contiene nuestros elementos que son excelentes o iguales a pivot
  4. Luego, clasificamos rápidamente cada uno de ellos y los combinamos el primer grupo + pivote + el segundo grupo para obtener el resultado final.

Aquí hay un ejemplo junto con visual para acompañarlo ... (pivote) 9,11,2,0

promedio: n logaritmo de n

peor caso: n ^ 2

ingrese la descripción de la imagen aquí

El código:

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

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


2

El algoritmo contiene dos límites, uno con elementos menores que el pivote (seguido por el índice "j") y el otro con elementos mayores que el pivote (seguido por el índice "i").

En cada iteración, se procesa un nuevo elemento incrementando j.

Invariante:-

  1. todos los elementos entre pivote e i son menores que el pivote, y
  2. todos los elementos entre i y j son mayores que el pivote.

Si se viola el invariante, los elementos i y j se intercambian y se incrementa i.

Después de que se hayan procesado todos los elementos, y todo después de que se haya particionado el pivote, el elemento pivote se intercambia con el último elemento más pequeño que él.

El elemento pivote ahora estará en su lugar correcto en la secuencia. Los elementos anteriores serán menores que él y los posteriores serán mayores que él, y no estarán clasificados.

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

Seleccionar un pivote

Un pivote "bueno" dará como resultado dos subsecuencias de aproximadamente el mismo tamaño. De manera determinista, un elemento pivote puede seleccionarse de manera ingenua o calculando la mediana de la secuencia.

Una implementación ingenua de seleccionar un pivote será el primer o último elemento. El peor tiempo de ejecución en este caso será cuando la secuencia de entrada ya esté ordenada o ordenada al revés, ya que una de las subsecuencias estará vacía, lo que provocará que solo se elimine un elemento por llamada recursiva.

Se logra una división perfectamente equilibrada cuando el pivote es el elemento mediano de la secuencia. Hay un número igual de elementos mayores y menores que él. Este enfoque garantiza un mejor tiempo de funcionamiento general, pero requiere mucho más tiempo.

Una forma no determinista / aleatoria de seleccionar el pivote sería elegir un elemento uniformemente al azar. Este es un enfoque simple y ligero que minimizará el peor de los casos y también conducirá a una división más o menos equilibrada. Esto también proporcionará un equilibrio entre el enfoque ingenuo y el enfoque medio de seleccionar el pivote.


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)

Si bien este código puede proporcionar una solución al problema, se recomienda encarecidamente que brinde un contexto adicional sobre por qué y / o cómo este código responde a la pregunta. Las respuestas de solo código generalmente se vuelven inútiles a largo plazo porque los futuros espectadores que experimentan problemas similares no pueden comprender el razonamiento detrás de la solución.
palacio

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
Incluya una explicación de lo que hace su código y cómo responde a la pregunta. Especialmente, ¿cómo se relaciona con el código publicado en la pregunta? La respuesta debe brindar orientación al OP y a los futuros visitantes sobre cómo depurar y solucionar su problema. Señalar cuál es la idea detrás de su código es de gran ayuda para comprender el problema y aplicar o modificar su solución. Stack Overflow no es un servicio de escritura de código, es un lugar de enseñanza y aprendizaje.
Palec

1

Una implementación "verdadera" en el lugar [Algoritmos 8.9, 8.11 del Libro de Aplicaciones y Diseño de Algoritmos de Michael T. Goodrich y 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()

1

El algoritmo consta de 4 sencillos pasos:

  1. Divida la matriz en 3 partes diferentes: izquierda, pivote y derecha, donde pivote tendrá solo un elemento. Elijamos este elemento pivote como el primer elemento de la matriz
  2. Agregue elementos a la parte respectiva comparándolos con el elemento pivote. (explicación en comentarios)
  3. Recurra este algoritmo hasta que todos los elementos de la matriz hayan sido ordenados
  4. Finalmente, une las partes izquierda + pivote + derecha

Código para el algoritmo 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])

Continúe con este algoritmo de forma recursiva con las partes izquierda y derecha.


1

Otra implementación de clasificación rápida:

# 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

Para la versión Python 3.x : un operatormódulo de uso de estilo funcional , principalmente para mejorar la legibilidad.

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

y se prueba como

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

Agradable (en lo que respecta al código indocumentado ), si bien similar a las de acarca , las respuestas de Arnaldo P. Figueira Figueira y Birger de años pasados. No estoy seguro de que sea una respuesta cuando se lee la pregunta … complete my code?. Usar lambdapara definir sublist()no parece estrictamente necesario.
greybeard

@greybeard En realidad, el código de Arnaldo no se compiló en Python 3. Además, ¿cómo se sublistpuede definir solo usando filter? ¿Es eso siquiera posible?
balance de vida

1
(Comentario temporal: pensando en def- no empecé a retocar todavía, ya que estoy tratando de averiguar si hay una forma más eficiente de "distribuir" los elementos de un iterable en listas separadas (o, alternativamente, una lista con una " categoría "después de la otra).)
barba gris

1

Aquí hay una implementación fácil: -

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. Primero declaramos que el primer valor de la matriz es pivot_value y también establecemos las marcas izquierda y derecha
  2. Creamos el primer ciclo while, este ciclo while está ahí para decirle al proceso de partición que se ejecute de nuevo si no satisface la condición necesaria
  3. luego aplicamos el proceso de partición
  4. después de que se hayan ejecutado ambos procesos de partición, verificamos si cumple con la condición adecuada. Si es así, lo marcamos como hecho, si no cambiamos los valores izquierdo y derecho y lo aplicamos nuevamente
  5. Una vez hecho, cambie los valores izquierdo y derecho y devuelva el split_point

¡Adjunto el código a continuación! Esta clasificación rápida es una gran herramienta de aprendizaje debido a la ubicación del valor de pivote . Dado que está en un lugar constante, puede recorrerlo varias veces y comprender realmente cómo funciona todo. En la práctica, es mejor aleatorizar el pivote para evitar el tiempo de ejecución O (N ^ 2).

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

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

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

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

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

Otras 18 respuestas, más de la mitad de las cuales responden a la pregunta original de OP de "cómo concatenar las tres matrices". ¿Tu respuesta agrega algo nuevo?
Teepeemm

0

Ejemplo completo con variables impresas en el paso de partición:

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
proporcione alguna explicación
Rai

0

Este algoritmo no usa funciones recursivas.

Sea Ncualquier lista de números con len(N) > 0. Configure K = [N]y ejecute el siguiente programa.

Nota: este es un algoritmo de clasificación estable .

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)

SALIDA DEL PROGRAMA:

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

Probablemente solo sea una cuestión de preferencia, pero me gusta agregar validación a la parte superior de mis funciones.

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)
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.