Quicksort com Python

92

Eu sou totalmente novo em python e estou tentando implementar o quicksort nele. Alguém poderia me ajudar a completar meu código?

Não sei como concatenar os três arrays e imprimi-los.

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
fonte
Para combinar listas, você pode usar o operador plus my_list = list1 + list2 + .... Ou descompacte listas para uma nova listamy_list = [*list1, *list2]
Mark Mishyn

Respostas:

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
fonte
8
Você também pode trocar o segundo ifs no loop for com elife elsepara evitar fazer comparações desnecessárias
SlimPDX
13
Isso soa como classificação por mesclagem, não classificação rápida
Emad Mokhtar
47
Na verdade, é o melhor e mais legível código python que encontrei para quicksort em qualquer lugar . Sem índices, sem funções auxiliares, mostra claramente a essência do algoritmo (dividir e conquistar). (O valor padrão para a matriz é bastante desnecessário)
cmantas
19
@jsmedmar usará mais memória do que uma versão no local, consulte a resposta do suquant para uma classificação rápida no local.
John
14
Muito legível, mas isso não anula o propósito da classificação rápida, uma vez que não alcançará a classificação "no local"? @RasmiRanjanNayak sort aqui é a função definida pelo usuário (é uma chamada recursiva), não qualquer função embutida.
Maksood
161

Classificação rápida sem memória adicional (no local)

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)
suquant
fonte
23
Esta deve ser a resposta selecionada, pois quicksort é frequentemente o algoritmo de escolha (por exemplo, merge sort) porque classifica no local (e ainda fornece tempo de execução O (nlogn)).
BoltzmannBrain
3
if end is None:vai ser verificado várias vezes, e apenas uma vez True. Você deve colocar isso em uma função de wrapper para que seja chamado apenas uma vez.
Gillespie
Ackchyually, bruhs, @mksteve está certo e esta linha está incorreta. Além disso, array[pivot], array[begin] = array[begin], array[pivot]deve substituir beginpor end.
Ryan J McCall
2
Embora o local seja bom, ele é lento e comete erros devido a atingir a profundidade máxima de recursão quando há muitos itens. consulte repl.it/@almenon/quicksorts?language=python3
Almenon
@mksteve e Ryan, eu testei essas mudanças e quebrou a classificação
aljgom
68

Existe outra versão concisa e bonita

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

Deixe-me explicar os códigos acima para obter detalhes

  1. escolha o primeiro elemento da matriz arr[0]como pivô

    [arr[0]]

  2. qsort aqueles elementos da matriz que são menos do que pivô com List Comprehension

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

  3. qsort aqueles elementos da matriz que são maiores do que o pivô com List Comprehension

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

zangw
fonte
15
@zangw razões possíveis para uma votação negativa: 1) Tempo de execução quadrático em matrizes já classificadas ou invertidas 2) A solução não está pronta. Portanto, uma implementação terrível, desculpe.
alisianoi
16
não legível de todo, você está realmente tentando minimizar o número de linhas? O código é interpretado por máquinas, mas entendido por humanos.
jsmedmar
4
@AlfredoGallegos, o objetivo do quicksort é que ele acontece no local, você também pode implementar o merge-sort se for fazer isso.
Padraic Cunningham
13
Esses comentários são reais? Se você deseja desempenho, use sorted, isso é claramente para fins educacionais. E é legível, mais legível do que a resposta aceita.
Nobilis
3
FWIW, achei que essa era a implementação mais legível de todas. Mostra a estrutura recursiva do algoritmo melhor do que qualquer outra resposta. Claro, o desempenho não vai ser muito bom.
SolveIt
35

Esta resposta é um QuickSort local para Python 2.x. Minha resposta é uma interpretação da solução in-loco do Rosetta Code que também 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)

E se você estiver disposto a abrir mão da propriedade no local, abaixo está outra versão que ilustra melhor as idéias básicas por trás do quicksort. Além da legibilidade, sua outra vantagem é que ele é estável (elementos iguais aparecem na lista classificada na mesma ordem que costumavam aparecer na lista não classificada). Esta propriedade de estabilidade não se mantém com a implementação local que consome menos memória apresentada acima.

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
fonte
Obrigado por compartilhar esta solução. Você pode nos ajudar a entender a complexidade do tempo? Vejo que a recursão vai chamá-lo 15 vezes. Destas, 8 são chamadas válidas para a função. Isso significa que a complexidade do tempo é O (n) para a primeira solução e a complexidade do espaço é O (1) como sua classificação no local?
ForeverLearner de
@Tammy, parece que você entendeu mal a notação big-O. Além disso, eu realmente não entendo sua pergunta. Você poderia, talvez, perguntar em separado? Finalmente, Quicksort como um algoritmo é executado em tempo O (n logn) e espaço O (n).
alisianoi
3
Foi mal. Por que diabos eu estava contando recursões ?? :-) Bem, 15 recursões são [1 chamada (Nível 0) + 2 chamadas (partição Nível 1) + 4 chamadas (partição Nível 2) + 8 chamadas (partição Nível 3 ou nós Folha). Portanto, ainda temos altura como (lg8 + 1) = lgn. O cálculo total em cada nível é para c1 (algum custo) * n. Portanto, O (n lgn). Complexidade do espaço, para uma troca no local = O (1). Portanto, para n elementos = O (n). Obrigado pelo ponteiro.
ForeverLearner
3
este é de longe o melhor quicksort python na internet, e é triste vê-lo enterrado sob tantas soluções O (n) espaciais :(
Sasha Kondrashov
Obrigado pelas amáveis ​​palavras @Timofey. Você pode querer dar uma olhada no meu repositório de algoritmos, ele tem outras versões de tipos, algoritmos de gráfico, etc. etc. github.com/alisianoi/algos-py
alisianoi
23

Quicksort com Python

Na vida real, devemos sempre usar a classificação embutida fornecida pelo Python. No entanto, compreender o algoritmo de classificação rápida é instrutivo.

Meu objetivo aqui é quebrar o assunto de forma que seja facilmente entendido e replicável pelo leitor sem ter que retornar aos materiais de referência.

O algoritmo quicksort é essencialmente o seguinte:

  1. Selecione um ponto de dados pivô.
  2. Mova todos os pontos de dados abaixo (abaixo) do pivô para uma posição abaixo do pivô - mova aqueles maiores ou iguais (acima) do pivô para uma posição acima dele.
  3. Aplique o algoritmo às áreas acima e abaixo do pivô

Se os dados forem distribuídos aleatoriamente, selecionar o primeiro ponto de dados como o pivô é equivalente a uma seleção aleatória.

Exemplo legível:

Primeiro, vamos olhar um exemplo legível que usa comentários e nomes de variáveis ​​para apontar para valores intermediários:

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 reafirmar o algoritmo e o código demonstrado aqui - movemos os valores acima do pivô para a direita e os valores abaixo do pivô para a esquerda e, em seguida, passamos essas partições para a mesma função para serem classificados posteriormente.

Jogado golfe:

Isso pode ser definido para 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 como chegamos lá, primeiro pegue nosso exemplo legível, remova comentários e docstrings e encontre o pivô no local:

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

Agora encontre abaixo e acima, no local:

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

Agora, sabendo que andretorna o elemento anterior se falso, senão, se for verdadeiro, avalia e retorna o seguinte elemento, temos:

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

Já que lambdas retornam uma única epressão, e nós simplificamos para uma única expressão (embora esteja ficando mais ilegível), agora podemos usar um 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]]))

E para reduzir ao nosso exemplo, reduza os nomes das funções e variáveis ​​para uma letra e elimine os espaços em branco desnecessários.

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

Observe que este lambda, como a maioria dos códigos de golfe, é um estilo bastante ruim.

Quicksort no local, usando o esquema de particionamento Hoare

A implementação anterior cria muitas listas extras desnecessárias. Se pudermos fazer isso no local, evitaremos o desperdício de espaço.

A implementação abaixo usa o esquema de particionamento Hoare, sobre o qual você pode ler mais na wikipedia (mas aparentemente removemos até 4 cálculos redundantes por partition()chamada usando a semântica while-loop em vez de do-while e movendo as etapas de redução para o final de o loop while externo.).

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

Não tenho certeza se testei exaustivamente:

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]

Conclusão

Esse algoritmo é freqüentemente ensinado em cursos de ciência da computação e solicitado em entrevistas de emprego. Isso nos ajuda a pensar sobre recursão e dividir para conquistar.

Quicksort não é muito prático em Python, pois nosso algoritmo timsort embutido é bastante eficiente e temos limites de recursão. Esperaríamos classificar as listas no local com list.sortou criar novas listas classificadas com sorted- ambas as quais recebem um argumento keye reverse.

Aaron Hall
fonte
Sua partitionfunção parecem não funcionar corretamente para: partition([5,4,3,2,1], 0, 4). O índice de retorno esperado é 4, enquanto retorna 3.
matino
@matino De onde veio essa expectativa? Eu acredito que simplifiquei o algoritmo (conforme declarado na wikipedia ao escrever esta resposta), embora eu possa estar errado, ou talvez menos eficiente. Se você pudesse encontrar um caso de teste em que toda a função quicksort falhe, isso seria útil.
Aaron Hall
@AaronHall quando escolhi pivot = a_list [high], mas não consigo descobrir como fazê-lo funcionar. Você pode ajudar ?
Qiulang
@matino Tive a mesma confusão! A função de partição é boa, o invariante que ela satisfaz é mais fraco do que o que você esperava - ele não precisa encontrar o lugar exato que separa a esquerda e a direita em menor e maior que o pivô. Garante apenas uma partição não trivial e que toda a esquerda do índice retornado seja menor que a direita do índice retornado.
Dror Speiser,
21

Já existem muitas respostas para isso, mas acho que essa abordagem é a implementação mais limpa:

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

Você pode, é claro, pular o armazenamento de tudo em variáveis ​​e retorná-las imediatamente assim:

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
fonte
11
EM!)? este é um 'slowSort'?
Scott 混合 理论
3
Eu acredito que no primeiro exemplo de código deve ser 'menor' e 'maior' em vez de '[menor]' e '[maior]' - caso contrário, você acabará com listas aninhadas em vez de uma plana.
Alice
@Scott 混合 理论 Ainda estou aprendendo a complexidade do tempo. Você pode explicar o porquê dessa implementação O(N!)? Supondo que a lista aninhada [lesser]e [greater]erros de digitação, não seria a média O(3N logN)que se reduziria à média esperada O(N logN)? Concedido, os 3 componentes de lista fazem um trabalho desnecessário ..
Chrispy
@Chrispy e se você classificar uma lista de ordem invertida, como 5,4,3,2,1
Scott 混合 理论
@Scott 混合 理论 você está certo que o tempo de execução do pior caso de classificação rápida é lento Θ (n ^ 2), mas de acordo com a "introdução ao algoritmo", o tempo médio de execução do caso é Θ (n lg n). E, mais importante, a classificação rápida geralmente supera a classificação de heap na prática
Lungang Fang
6

abordagem 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)
Akarca
fonte
lista é reservada em python 3. veja a versão modificada de seu código aqui: gist.github.com/kunthar/9d8962e1438e93f50dc6dd94d503af3d
Kunthar
akarca e @Kunthar Ambas as soluções em python2 ou python3 irão retirar um elemento da lista sempre que for executado, destruindo, portanto, a lista. Aqui está uma versão corrigida: gist.github.com/joshuatvernon/634e0d912401899af0fdc4e23c94192b
joshuatvernon
4

abordagem de programação 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]
Arnaldo P. Figueira Figueira
fonte
4

Fácil implementação 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)
Alice
fonte
3

Acho que ambas as respostas aqui funcionam bem para a lista fornecida (que responde à pergunta original), mas quebraria se uma matriz contendo valores não exclusivos for passada. Portanto, para completar, gostaria apenas de apontar o pequeno erro em cada um e explicar como corrigi-los.

Por exemplo, tentando classificar a seguinte matriz [12,4,5,6,7,3,1,15,1] (Observe que 1 aparece duas vezes) com o algoritmo de Brionius .. em algum ponto terminará com menos matriz vazia e a matriz igual com um par de valores (1,1) que não podem ser separados na próxima iteração e len ()> 1 ... portanto, você acabará com um loop infinito

Você pode consertar retornando array se less estiver vazio ou melhor não chamando sort em seu array igual, como em 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

A solução mais sofisticada também quebra, mas por uma causa diferente, está faltando o retorno cláusula de na linha de recursão, o que fará com que em algum ponto retorne Nenhum e tente anexá-lo a uma lista ....

Para corrigir, basta adicionar um retorno a essa linha

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
fonte
A propósito, a versão concisa tem menos desempenho do que a longa, já que está iterando o array duas vezes para nas compreensões da lista.
FedeN
3

Esta é uma versão do quicksort usando o esquema de partição Hoare e com menos trocas e variáveis ​​locais

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
fonte
3

Partição - Divide uma matriz por um pivô que os elementos menores se movem para a esquerda e os elementos maiores se movem para a direita ou vice-versa. Um pivô pode ser um elemento aleatório de uma matriz. Para fazer esse algoritmo, precisamos saber o que é o índice inicial e final de uma matriz e onde está um pivô. Em seguida, defina dois ponteiros auxiliares L, R.

Portanto, temos um usuário de array [..., começo, ..., fim, ...]

A posição inicial dos ponteiros L e R
[..., começar, próximo, ..., fim, ...]
     R L

enquanto L <fim
  1. Se um usuário [pivô]> usuário [L] então mova R por um e troque o usuário [R] pelo usuário [L]
  2. mova L por um

Depois de algum tempo, troque o usuário [R] com o usuário [pivot]

Classificação rápida - Use o algoritmo de partição até que cada parte seguinte da divisão por um pivô tenha um índice inicial maior ou igual ao í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)
Grzegorz Eleryk
fonte
explique seu código / adições para que OP e visualizações futuras possam se beneficiar mais.
Omar Einea
2

Ou se você preferir uma linha que também ilustra o equivalente em Python de varags C / C ++, expressões lambda e expressões 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
fonte
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
fonte
2

Sei que muitas pessoas responderam a essa pergunta corretamente e gostei de lê-las. Minha resposta é quase a mesma que zangw, mas acho que os contribuidores anteriores não fizeram um bom trabalho explicando visualmente como as coisas realmente funcionam ... então aqui está minha tentativa de ajudar outras pessoas que podem visitar esta pergunta / respostas no futuro sobre um solução simples para implementação de quicksort.

Como funciona ?

  1. Basicamente, selecionamos o primeiro item como nosso pivô em nossa lista e, em seguida, criamos duas sub-listas.
  2. Nossa primeira sublista contém os itens que são menores que pivô
  3. Nossa segunda sublista contém nossos itens que são maiores ou iguais ao pivô
  4. Em seguida, classificamos rapidamente cada um deles e os combinamos o primeiro grupo + pivô + o segundo grupo para obter o resultado final.

Aqui está um exemplo junto com o visual para acompanhar ... (pivô) 9,11,2,0

média: n log de n

pior caso: n ^ 2

insira a descrição da imagem aqui

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

itens = [9,11,2,0] imprimir (classificação rápida (itens))

grepit
fonte
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
fonte
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
fonte
1
Inclua uma explicação sobre o que seu código faz e como ele responde à pergunta. Especialmente como isso se relaciona com o código postado na pergunta. A resposta deve fornecer ao OP e aos futuros visitantes orientação sobre como depurar e corrigir o problema. Indicar qual é a ideia por trás do seu código ajuda muito a entender o problema e a aplicar ou modificar sua solução. Stack Overflow não é um serviço de escrita de código, é um local de ensino e aprendizagem.
Palec
1

Uma implementação "verdadeira" no local [Algoritmos 8.9, 8.11 do Livro de Design e Aplicações de Algoritmos de Michael T. Goodrich e 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
fonte
1

O algoritmo possui 4 etapas simples:

  1. Divida a matriz em 3 partes diferentes: esquerda, pivô e direita, onde pivô terá apenas um elemento. Vamos escolher este elemento pivô como o primeiro elemento da matriz
  2. Anexe elementos à respectiva parte comparando-os ao elemento pivô (explicação nos comentários)
  3. Percorra novamente este algoritmo até que todos os elementos da matriz tenham sido classificados
  4. Finalmente, junte as partes esquerda + pivô + direita

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

Continue com este algoritmo recursivamente com as partes esquerda e direita.

Mamta Kanvinde
fonte
1

Outra implementação de quicksort:

# 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
fonte
1

Para a versão Python 3.x : um operatormódulo de uso de estilo funcional , principalmente para melhorar a legibilidade.

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

e é testado como

print (qsort([3,1,4,2,5]) == [1,2,3,4,5])
equilíbrio de vida
fonte
Legal (no que diz respeito ao código indocumentado ), ainda que parecido com o da acarca , as respostas de Arnaldo P. Figueira Figueira e Birger de anos atrás . Não tenho certeza se isso é uma resposta quando a pergunta é lida … complete my code?. Usar lambdapara definir sublist()não parece estritamente necessário.
Barba Cinzenta
@greybeard Na verdade, o código de Arnaldo não compilou no Python 3. Além disso, como pode sublistser definido apenas usando filter? É mesmo possível?
Lifebalance de
1
(Comentário temporário: pensando em def- não comecei a mexer ainda enquanto estou tentando descobrir se há uma maneira mais eficiente de "distribuir" os elementos de um iterável para separar listas (ou, alternativamente, uma lista com uma " categoria "após o outro).)
barba cinza
1

Esta é uma implementação 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]))
abhishek4996
fonte
1

O algoritmo contém dois limites, um com elementos menores que o pivô (rastreado pelo índice "j") e o outro tendo elementos maiores que o pivô (rastreado pelo índice "i").

Em cada iteração, um novo elemento é processado incrementando j.

Invariante:-

  1. todos os elementos entre o pivô e i são menores que o pivô, e
  2. todos os elementos entre i e j são maiores que o pivô.

Se o invariante for violado, os elementos i e j são trocados e i é incrementado.

Depois que todos os elementos foram processados ​​e tudo após o pivô ter sido particionado, o elemento pivô é trocado pelo último elemento menor do que ele.

O elemento pivô agora estará em seu lugar correto na sequência. Os elementos anteriores serão menores do que ele e os posteriores serão maiores do que ele e não serão classificados.

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

Selecionando um pivô

Um pivô "bom" resultará em duas subseqüências de aproximadamente o mesmo tamanho. Deterministicamente, um elemento pivô pode ser selecionado de uma maneira ingênua ou calculando a mediana da sequência.

Uma implementação ingênua de selecionar um pivô será o primeiro ou o último elemento. O pior caso de tempo de execução, neste caso, será quando a sequência de entrada já estiver classificada ou classificada reversamente, pois uma das subsequências estará vazia, o que fará com que apenas um elemento seja removido por chamada recursiva.

Uma divisão perfeitamente equilibrada é obtida quando o pivô é o elemento mediano da sequência. Há um número igual de elementos maior e menor do que ele. Essa abordagem garante um melhor tempo de execução geral, mas é muito mais demorada.

Uma maneira não determinística / aleatória de selecionar o pivô seria escolher um elemento uniformemente ao acaso. Esta é uma abordagem simples e leve que minimizará o pior cenário e também levará a uma divisão aproximadamente equilibrada. Isso também fornecerá um equilíbrio entre a abordagem ingênua e a abordagem mediana de seleção do pivô.


fonte
0
  1. Primeiro declaramos o primeiro valor na matriz como sendo o pivot_value e também definimos as marcas esquerda e direita
  2. Nós criamos o primeiro loop while, este loop while está lá para dizer ao processo de partição para rodar novamente se não satisfizer a condição necessária
  3. então aplicamos o processo de partição
  4. após a execução de ambos os processos de partição, verificamos se satisfaz a condição adequada. Em caso afirmativo, marcamos como concluído, caso contrário, alternamos os valores esquerdo e direito e os aplicamos novamente
  5. Uma vez feito isso, troque os valores esquerdo e direito e retorne o split_point

Estou anexando o código abaixo! Este quicksort é uma ótima ferramenta de aprendizado devido à localização do valor pivô . Como ele está em um lugar constante, você pode percorrê-lo várias vezes e realmente pegar o jeito de como tudo funciona. Na prática, é melhor randomizar o pivô para evitar o tempo de execução 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
fonte
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
fonte
18 outras respostas, mais da metade das quais respondem à pergunta original da OP de "como concatenar os três arrays". A sua resposta acrescenta algo novo?
Teepeemm
0

Exemplo completo com variáveis ​​impressas na etapa de partição:

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
fonte
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
fonte
1
forneça alguma explicação
Rai
0

Este algoritmo não usa funções recursivas.

Let NSer qualquer lista de números com len(N) > 0. ConjuntoK = [N] e execute o seguinte programa.

Nota: este é um algoritmo de classificação estável .

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)

SAÍDA DO 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]
CopyPasteIt
fonte
0

Provavelmente apenas uma questão de preferência, mas gosto de adicionar validação ao topo das minhas funções.

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
fonte
0

Minha resposta é muito parecida com a grande do @alisianoi. No entanto, acredito que haja uma ligeira ineficiência em seu código (veja meu comentário), que removi. Além disso, acrescentei mais explicações e fui um pouco mais específico sobre o problema de valores duplicados (pivô).

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
fonte