Quicksort з Python


93

Я абсолютно новачок у python, і я намагаюся реалізувати в ньому швидку сортування. Чи може хтось допомогти мені заповнити код?

Я не знаю, як об'єднати три масиви та роздрукувати їх.

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)

Для об'єднання списків можна використовувати оператор плюс my_list = list1 + list2 + .... Або розпакуйте списки до нового спискуmy_list = [*list1, *list2]
Марк Мішин,

Відповіді:


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

8
Ви також можете поміняти ifelifelse
місцями

13
Це звучить як сортування злиттям, а не швидке сортування
Емад Мохтар

47
Це насправді найкращий та найбільш читабельний код python, який я знайшов для швидкого сортування де завгодно . Без індексів, без допоміжних функцій, чітко видно суть алгоритму (розділяй і володарюй). (Значення за замовчуванням для масиву досить непотрібне)
cmantas

19
@jsmedmar він буде використовувати більше пам'яті, ніж версія на місці, див. відповідь suquant для швидкого сортування на місці.
Джон

14
Дуже читабельно, але чи не перемагає це мета швидкого сортування, оскільки це не дозволить досягти сортування на місці? @RasmiRanjanNayak sort тут - це визначена користувачем функція (її рекурсивний виклик), а не будь-яка вбудована функція.
Maksood

161

Швидке сортування без додаткової пам'яті (на місці)

Використання:

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
Це має бути вибрана відповідь, оскільки швидке сортування часто є алгоритмом вибору (наприклад, за допомогою сортування злиттям), оскільки воно сортує на місці (і все ще дає час виконання O (nlogn)).
BoltzmannBrain

3
if end is None:перевірятимуть багато разів, і лише один раз це буде True. Ви повинні помістити це у функцію обгортки, щоб вона викликалася лише один раз.
Гіллеспі,

На жаль, брух, @mksteve має рацію, і цей рядок неправильний. Крім того, array[pivot], array[begin] = array[begin], array[pivot]слід замінити beginна end.
Ryan J McCall

2
Незважаючи на те, що на місці це добре, це повільно, і воно помиляється через досягнення максимальної глибини рекурсії, коли елементів багато. див. repl.it/@almenon/quicksorts?language=python3
Альменон

@mksteve та Ryan, я протестував ці зміни, і це порушує сортування
aljgom

68

Є ще один лаконічний і красивий варіант

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

Дозвольте пояснити наведені вище коди для деталей

  1. виберіть перший елемент масиву arr[0]як опорну

    [arr[0]]

  2. qsort ті елементи масиву, які менше, ніж зведення List Comprehension

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

  3. qsort ті елементи масиву, які більше, ніж pivot with List Comprehension

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


15
@zangw можливі причини відхилення: 1) Квадратичне виконання вже відсортованих або перевернутих масивів 2) Рішення не на місці. Тому жахлива реалізація, вибачте.
alisianoi

16
не читається взагалі, чи справді ви намагаєтеся мінімізувати кількість рядків? Код інтерпретується машинами, але розуміється людьми.
jsmedmar

4
@AlfredoGallegos, вся суть швидкого сортування в тому, що це відбувається на місці, ви також можете застосувати злиття-сортування, якщо збираєтеся це зробити.
Падрайк Каннінгем,

14
Чи справді ці коментарі? Якщо ви хочете продуктивності, використовуйте sorted, це явно для навчальних цілей. І це читається, читається більше, ніж прийнята відповідь.
Nobilis

4
FWIW, я вважав, що це найбільш читабельна реалізація з усіх них. Він показує рекурсивну структуру алгоритму краще, ніж будь-яка інша відповідь. Звичайно, продуктивність не буде надто чудовою.
Вирішити це

36

Ця відповідь є швидким сортуванням на місці Python 2.x. Моя відповідь - інтерпретація рішення на місці з Розеттського кодексу, яке Python 3теж працює :

import random

def qsort(xs, fst, lst):
    '''
    Sort the range xs[fst, lst] in-place with vanilla QuickSort

    :param xs:  the list of numbers to sort
    :param fst: the first index from xs to begin sorting from,
                must be in the range [0, len(xs))
    :param lst: the last index from xs to stop sorting at
                must be in the range [fst, len(xs))
    :return:    nothing, the side effect is that xs[fst, lst] is sorted
    '''
    if fst >= lst:
        return

    i, j = fst, lst
    pivot = xs[random.randint(fst, lst)]

    while i <= j:
        while xs[i] < pivot:
            i += 1
        while xs[j] > pivot:
            j -= 1

        if i <= j:
            xs[i], xs[j] = xs[j], xs[i]
            i, j = i + 1, j - 1
    qsort(xs, fst, j)
    qsort(xs, i, lst)

І якщо ви готові відмовитись від властивості на місці, нижче є ще одна версія, яка краще ілюструє основні ідеї швидкого сортування. Окрім читабельності, інша його перевага полягає в тому, що він стабільний (рівні елементи з’являються у відсортованому списку в тому ж порядку, що і раніше в несортованому списку). Ця властивість стабільності не виконується при менш голодному впровадженні пам'яті, представленому вище.

def qsort(xs):
    if not xs: return xs # empty sequence case
    pivot = xs[random.choice(range(0, len(xs)))]

    head = qsort([x for x in xs if x < pivot])
    tail = qsort([x for x in xs if x > pivot])
    return head + [x for x in xs if x == pivot] + tail

Дякуємо, що поділилися цим рішенням. Чи можете ви допомогти нам зрозуміти складність часу? Я бачу, що рекурсія буде викликати це 15 разів. З них 8 є дійсними викликами функції. Чи означає це, що складність у часі дорівнює O (n) для першого рішення, а складність простору - O (1) як його сорт на місці?
ForeverLearner

@Tammy, схоже, ти неправильно розумієш нотацію великих цифр. Більше того, я не дуже розумію ваше запитання. Чи не могли б ви запитати його як окремий? Нарешті, Quicksort як алгоритм працює в O (n logn) часі та O (n) просторі.
alisianoi

3
Моє ліжко. Чому бог я рахував рекурсії ?? :-) Ну, 15 рекурсій - це [1 виклик (рівень 0) + 2 виклик (розділ рівня 1) + 4 виклик (розділ рівня 2) + 8 виклик (розділ рівня 3 або вузли Leaf). Отже, ми все ще маємо висоту як (lg8 + 1) = lgn. Загальне обчислення на кожному рівні для c1 (деяка вартість) * n. Звідси O (n lgn). Складність простору для одного обміну на місці = O (1). Отже, для n елементів = O (n). Дякую за вказівник.
ForeverLearner

3
це далеко не найкращий пітон-швидкий сорт в Інтернеті, і прикро бачити, як він похований під такою кількістю O (n) космічних рішень :(
Саша Кондрашов

Дякую за теплі слова @Timofey. Можливо, ви захочете поглянути на моє репозиторій
alisianoi

23

Quicksort з Python

У реальному житті ми завжди повинні використовувати вбудований сорт, наданий Python. Однак розуміння алгоритму швидкого сортування є повчальним.

Моя мета тут - розбити тему таким чином, щоб вона була легко зрозумілою і відтвореною читачем без необхідності повертатися до довідкових матеріалів.

Алгоритм швидкого сортування є, по суті, таким:

  1. Виберіть опорну точку даних.
  2. Перемістіть усі точки даних, менші за (під) поворотом, у позицію під стержнем - перемістіть ті, що більші або рівні (над) зведеною, у позицію над нею.
  3. Застосуйте алгоритм до областей, розташованих зверху та нижче стовбура

Якщо дані розподіляються випадковим чином, вибір першої точки даних як опорної точки еквівалентний випадковому виділенню.

Приклад читання:

Спочатку давайте розглянемо приклад для читання, який використовує коментарі та імена змінних для вказівки на проміжні значення:

def quicksort(xs):
    """Given indexable and slicable iterable, return a sorted list"""
    if xs: # if given list (or tuple) with one ordered item or more: 
        pivot = xs[0]
        # below will be less than:
        below = [i for i in xs[1:] if i < pivot] 
        # above will be greater than or equal to:
        above = [i for i in xs[1:] if i >= pivot]
        return quicksort(below) + [pivot] + quicksort(above)
    else: 
        return xs # empty list

Щоб повторити продемонстрований тут алгоритм і код - ми переміщуємо значення над зведеним вправо, а значення під зведеним вліво, а потім передаємо ці розділи тій самій функції для подальшої сортування.

У гольф:

Це можна перетворити на 88 символів:

q=lambda x:x and q([i for i in x[1:]if i<=x[0]])+[x[0]]+q([i for i in x[1:]if i>x[0]])

Щоб побачити, як ми туди потрапили, спочатку візьміть наш читабельний приклад, видаліть коментарі та текстові рядки та знайдіть опору на місці:

def quicksort(xs):
    if xs: 
        below = [i for i in xs[1:] if i < xs[0]] 
        above = [i for i in xs[1:] if i >= xs[0]]
        return quicksort(below) + [xs[0]] + quicksort(above)
    else: 
        return xs

Тепер знайдіть нижче і вище, на місці:

def quicksort(xs):
    if xs: 
        return (quicksort([i for i in xs[1:] if i < xs[0]] )
                + [xs[0]] 
                + quicksort([i for i in xs[1:] if i >= xs[0]]))
    else: 
        return xs

Тепер, знаючи, що andповертає попередній елемент, якщо false, інакше якщо це істина, він обчислює і повертає наступний елемент, маємо:

def quicksort(xs):
    return xs and (quicksort([i for i in xs[1:] if i < xs[0]] )
                   + [xs[0]] 
                   + quicksort([i for i in xs[1:] if i >= xs[0]]))

Оскільки лямбди повертають одне вираз, і ми спростили його до одного виразу (хоча він стає все більш нечитабельним), тепер ми можемо використовувати лямбду:

quicksort = lambda xs: (quicksort([i for i in xs[1:] if i < xs[0]] )
                        + [xs[0]] 
                        + quicksort([i for i in xs[1:] if i >= xs[0]]))

А щоб звести до нашого прикладу, скоротіть імена функцій та змінних до однієї літери та усуньте пробіли, які не потрібні.

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

Зверніть увагу, що ця лямбда, як і більшість кодових гольфів, є досить поганим стилем.

Швидкий сорт на місці, використовуючи схему розділення Хоара

Попередня реалізація створює багато непотрібних додаткових списків. Якщо ми зможемо зробити це на місці, ми уникнемо марнотратства.

Наведена нижче реалізація використовує схему розділення Хоара, про яку ви можете детальніше прочитати у wikipedia (але ми, очевидно, видалили до 4 надлишкових обчислень за partition()виклик, використовуючи семантику циклу while замість do-while та рухаючи кроки звуження до кінця зовнішній цикл while.).

def quicksort(a_list):
    """Hoare partition scheme, see https://en.wikipedia.org/wiki/Quicksort"""
    def _quicksort(a_list, low, high):
        # must run partition on sections with 2 elements or more
        if low < high: 
            p = partition(a_list, low, high)
            _quicksort(a_list, low, p)
            _quicksort(a_list, p+1, high)
    def partition(a_list, low, high):
        pivot = a_list[low]
        while True:
            while a_list[low] < pivot:
                low += 1
            while a_list[high] > pivot:
                high -= 1
            if low >= high:
                return high
            a_list[low], a_list[high] = a_list[high], a_list[low]
            low += 1
            high -= 1
    _quicksort(a_list, 0, len(a_list)-1)
    return a_list

Не впевнений, чи перевірив я його досить ретельно:

def main():
    assert quicksort([1]) == [1]
    assert quicksort([1,2]) == [1,2]
    assert quicksort([1,2,3]) == [1,2,3]
    assert quicksort([1,2,3,4]) == [1,2,3,4]
    assert quicksort([2,1,3,4]) == [1,2,3,4]
    assert quicksort([1,3,2,4]) == [1,2,3,4]
    assert quicksort([1,2,4,3]) == [1,2,3,4]
    assert quicksort([2,1,1,1]) == [1,1,1,2]
    assert quicksort([1,2,1,1]) == [1,1,1,2]
    assert quicksort([1,1,2,1]) == [1,1,1,2]
    assert quicksort([1,1,1,2]) == [1,1,1,2]

Висновок

Цей алгоритм часто викладають на курсах інформатики та запитують на співбесіді. Це допомагає нам думати про рекурсію та поділ і перемогу.

Quicksort не надто практичний у Python, оскільки наш вбудований алгоритм тимчасового сортування є досить ефективним, і у нас є межі рекурсії. Ми очікували б сортувати списки на місці list.sortабо створювати нові відсортовані списки за допомогоюsorted - обидва з яких прийняти keyі reverseаргумент.


Ваша partitionфункція , схоже , неправильно працювати: partition([5,4,3,2,1], 0, 4). Очікуваний показник повернення дорівнює 4, тоді як повертається 3.
Матіно

@matino Звідки взялося це сподівання? Я вважаю, що спростив алгоритм (як зазначено у wikipedia під час написання цієї відповіді), хоча я міг помилитися чи, можливо, менш ефективним. Якщо ви зможете знайти тест-кейс, для якого не працює вся функція швидкого сортування, це було б корисно.
Аарон Холл

@AaronHall, коли я вибрав pivot = a_list [high], але я просто не можу зрозуміти, як змусити його працювати тоді. Ви можете допомогти ?
Qiulang

@matino У мене була така ж плутанина! Функція розділу чудова, інваріант, який вона задовольняє, слабший, ніж ви очікуєте - їй не потрібно знаходити точне місце, яке розділяє ліворуч та праворуч на менші та більші, ніж опорні точки. Це гарантує лише нетривіальний розділ і те, що всі ліві частини поверненого індексу менше, ніж право поверненого індексу.
Dror Speiser

21

На це вже є багато відповідей, але я вважаю, що такий підхід є найбільш чистою реалізацією:

def quicksort(arr):
    """ Quicksort a list

    :type arr: list
    :param arr: List to sort
    :returns: list -- Sorted list
    """
    if not arr:
        return []

    pivots = [x for x in arr if x == arr[0]]
    lesser = quicksort([x for x in arr if x < arr[0]])
    greater = quicksort([x for x in arr if x > arr[0]])

    return lesser + pivots + greater

Звичайно, ви можете пропустити зберігання всього у змінних і повернути їх одразу так:

def quicksort(arr):
    """ Quicksort a list

    :type arr: list
    :param arr: List to sort
    :returns: list -- Sorted list
    """
    if not arr:
        return []

    return quicksort([x for x in arr if x < arr[0]]) \
        + [x for x in arr if x == arr[0]] \
        + quicksort([x for x in arr if x > arr[0]])

11
O (N!)? це "повільне сортування"?
Скотт 混合 理论

3
Я вважаю, що в першому прикладі коду він повинен бути "меншим" і "більшим" замість "[менший]" і "[більший]" - інакше ви отримаєте вкладені списки замість простого.
Аліса

@Scott 混合 理论 Я все ще вивчаю складність часу, чи можете ви пояснити, чому така реалізація O(N!)? Якщо припустити вкладений список [lesser]та [greater]помилки друку, чи не буде це середнім значенням, O(3N logN)яке зменшиться до очікуваного середнього O(N logN)? Звичайно, 3 компілятори зі списку роблять непотрібну роботу ..
Кріспі

@Chrispy а що, якщо відсортувати перевернутий список замовлення, наприклад 5,4,3,2,1
Скотт 混合 理论

@Scott 混合 理论 ви маєте рацію, що час найшвидшого запуску швидкого сортування є повільним Θ (n ^ 2), але згідно з "введенням в алгоритм" середній час роботи становить Θ (n lg n). І, що ще важливіше, швидке сортування на практиці перевершує сортування купи
Lungang Fang

6

функціональний підхід:

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)

список зарезервовано в python 3. див. змінену версію вашого коду тут: gist.github.com/kunthar/9d8962e1438e93f50dc6dd94d503af3d
Кунтар

akarca та @Kunthar Обидва ці рішення як у python2, так і в python3 будуть виводити елемент зі списку кожного разу, коли він буде запущений, отже, знищуючи список. Ось виправлена ​​версія: gist.github.com/joshuatvernon/634e0d912401899af0fdc4e23c94192b
joshuatvernon

4

підхід до функціонального програмування

smaller = lambda xs, y: filter(lambda x: x <= y, xs)
larger = lambda xs, y: filter(lambda x: x > y, xs)
qsort = lambda xs: qsort(smaller(xs[1:],xs[0])) + [xs[0]] + qsort(larger(xs[1:],xs[0])) if xs != [] else []

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

4

Проста реалізація за допомогою алгоритмів 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

Я думаю, що обидві відповіді тут добре спрацьовують для наданого списку (який відповідає на вихідне запитання), але порушує, якщо передається масив, що містить не унікальні значення. Тому для повноти я б просто вказав на невелику помилку в кожному і пояснив, як їх виправити.

Наприклад, спроба відсортувати наступний масив [12,4,5,6,7,3,1,15,1] (зауважте, що 1 з'являється двічі) за допомогою алгоритму Бріоніуса .. в якийсь момент масив буде менше, порожнім і рівний масив з парою значень (1,1), які не можна розділити в наступній ітерації та len ()> 1 ... отже, у вас вийде нескінченний цикл

Ви можете це виправити, повернувши масив, якщо менше порожнього, або краще, не викликаючи сортування у вашому рівному масиві, як у відповіді 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

Вирішальне рішення також ламається, але з іншої причини йому не вистачає пропозиції return у рядку рекурсії, що в певний момент призведе до повернення None та спроби додати його до списку ....

Щоб виправити це, просто додайте повернення до цього рядка

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

3

Це версія швидкого сортування, що використовує схему розділів Хоара і з меншою кількістю свопів і локальних змінних

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

Розділ - розділіть масив на шарнір, при якому менші елементи рухаються вліво, а більші елементи рухаються вправо або навпаки. Зворот може бути випадковим елементом з масиву. Для створення цього алгоритму нам потрібно знати, що таке індекс початку та кінця масиву, а де стержень. Потім встановіть два допоміжні покажчики L, R.

Отже, у нас є користувач масиву [..., begin, ..., end, ...]

Початкове положення покажчиків L та R
[..., початок, наступний, ..., кінець, ...]
     R L

в той час як L <кінець
  1. Якщо користувач [pivot]> користувач [L], то перемістіть R на одного і поміняйте користувача [R] на користувача [L]
  2. перемістіть L на один

Через деякий час поміняти користувача [R] на користувача [pivot]

Швидке сортування - Використовуйте алгоритм розділу, доки кожна наступна частина розбиття на зведення не матиме індексу початку більшим або рівним індексу кінця.

def qsort(user, begin, end):

    if begin >= end:
        return

    # partition
    # pivot = begin
    L = begin+1
    R = begin
    while L < end:
        if user[begin] > user[L]:
            R+=1
            user[R], user[L] = user[L], user[R]
        L+= 1
    user[R], user[begin] = user[begin], user[R]

    qsort(user, 0, R)
    qsort(user, R+1, end)

tests = [
    {'sample':[1],'answer':[1]},
    {'sample':[3,9],'answer':[3,9]},
    {'sample':[1,8,1],'answer':[1,1,8]},
    {'sample':[7,5,5,1],'answer':[1,5,5,7]},
    {'sample':[4,10,5,9,3],'answer':[3,4,5,9,10]},
    {'sample':[6,6,3,8,7,7],'answer':[3,6,6,7,7,8]},
    {'sample':[3,6,7,2,4,5,4],'answer':[2,3,4,4,5,6,7]},
    {'sample':[1,5,6,1,9,0,7,4],'answer':[0,1,1,4,5,6,7,9]},
    {'sample':[0,9,5,2,2,5,8,3,8],'answer':[0,2,2,3,5,5,8,8,9]},
    {'sample':[2,5,3,3,2,0,9,0,0,7],'answer':[0,0,0,2,2,3,3,5,7,9]}
]

for test in tests:

    sample = test['sample'][:]
    answer = test['answer']

    qsort(sample,0,len(sample))

    print(sample == answer)

поясніть, будь ласка, свій код / ​​доповнення, щоб OP та майбутні подання могли отримати більше користі.
Omar Einea

2

Або якщо ви віддаєте перевагу однорядковому вкладенню, яке також ілюструє еквівалент Python C / C ++-варагів, лямбда-вирази та вирази 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

Я знаю, що багато людей правильно відповіли на це запитання, і я із задоволенням їх читав. Моя відповідь майже така ж, як і zangw, але я думаю, що попередні автори не зробили хорошої роботи, щоб візуально пояснити, як це насправді працює ... так ось моя спроба допомогти іншим, хто може відвідати це питання / відповіді в майбутньому про просте рішення для швидкої реалізації.

Як це працює ?

  1. В основному ми вибираємо перший пункт як наш опорний пункт із нашого списку, а потім створюємо два підсписки.
  2. Наш перший підсписок містить елементи, які менше, ніж опорні
  3. Наш другий підсписок містить наші предмети, які є великими або дорівнюють опорним
  4. Потім ми швидко сортуємо кожну з них і об’єднуємо їх перша група + опора + друга група, щоб отримати кінцевий результат.

Ось приклад разом із visual, щоб піти з цим ... (півот) 9,11,2,0

середнє: n журнал n

гірший випадок: n ^ 2

введіть тут опис зображення

Кодекс:

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

Алгоритм містить дві межі, одна з яких має елементи, менші за опору (відстежується за індексом "j"), а інша - за елементами, більшими від осі (за індексом "i").

У кожній ітерації новий елемент обробляється збільшенням j.

Інваріант: -

  1. всі елементи між поворотом і i менші за поворот і
  2. всі елементи між i та j більші за опору.

Якщо інваріант порушено, i-й та j-й елементи міняються місцями, а i збільшується.

Після того, як всі елементи оброблені, і все після того, як півот був розділений, елемент шарніру замінюється на останній елемент, менший за нього.

Тепер елемент повороту буде знаходитись у правильному місці в послідовності. Елементи до нього будуть меншими за нього, а елементи після нього будуть більшими за нього, і вони будуть відсортовані.

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

Вибір стержня

"Хороший" стрижень призведе до двох підпослідовностей приблизно однакового розміру. Детерміновано, опорний елемент може бути обраний наївним чином або шляхом обчислення медіани послідовності.

Наївна реалізація вибору стержня буде першим або останнім елементом. Найгірший час виконання в цьому випадку буде тоді, коли вхідна послідовність вже відсортована або відсортована в зворотному порядку, оскільки одна з послідовностей буде порожньою, що призведе до видалення лише одного елемента за рекурсивний виклик.

Ідеально збалансований розкол досягається, коли опорна точка є медіанним елементом послідовності. Є рівна кількість елементів, більших за нього і менших за нього. Такий підхід гарантує кращий загальний час роботи, але набагато трудомісткіший.

Недетермінованим / випадковим способом вибору опори буде вибір елемента рівномірно навмання. Це простий та легкий підхід, який зведе до мінімуму найгірший сценарій, а також призведе до приблизно збалансованого розколу. Це також забезпечить баланс між наївним підходом та медіанним підходом до вибору опори.


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)

Хоча цей код може запропонувати вирішення проблеми, настійно рекомендується надати додатковий контекст щодо того, чому та / або як цей код відповідає на питання. Відповіді лише на код, як правило, стають марними в довгостроковій перспективі, оскільки майбутні глядачі, які стикаються з подібними проблемами, не можуть зрозуміти аргументацію рішення.
палан

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
Будь ласка, включіть пояснення, що робить ваш код і як він відповідає на запитання. Особливо, як це стосується коду, розміщеного у питанні. Відповідь повинна дати ОП та майбутнім відвідувачам вказівки щодо того, як налагодити та виправити свою проблему. Вказуючи, що ідея вашого коду, дуже допомагає зрозуміти проблему та застосувати або змінити ваше рішення. Stack Overflow - це не служба написання коду, це місце для навчання та навчання.
Palec

1

"Справжня" реалізація на місці [Алгоритми 8.9, 8.11 з Книги про розробку та застосування алгоритмів Майкла Т. Гудріча та Роберто Тамасії]:

from random import randint

def partition (A, a, b):
    p = randint(a,b)
    # or mid point
    # p = (a + b) / 2

    piv = A[p]

    # swap the pivot with the end of the array
    A[p] = A[b]
    A[b] = piv

    i = a     # left index (right movement ->)
    j = b - 1 # right index (left movement <-)

    while i <= j:
        # move right if smaller/eq than/to piv
        while A[i] <= piv and i <= j:
            i += 1
        # move left if greater/eq than/to piv
        while A[j] >= piv and j >= i:
            j -= 1

        # indices stopped moving:
        if i < j:
            # swap
            t = A[i]
            A[i] = A[j]
            A[j] = t
    # place pivot back in the right place
    # all values < pivot are to its left and 
    # all values > pivot are to its right
    A[b] = A[i]
    A[i] = piv

    return i

def IpQuickSort (A, a, b):

    while a < b:
        p = partition(A, a, b) # p is pivot's location

        #sort the smaller partition
        if p - a < b - p:
            IpQuickSort(A,a,p-1)
            a = p + 1 # partition less than p is sorted
        else:
            IpQuickSort(A,p+1,b)
            b = p - 1 # partition greater than p is sorted


def main():
    A =  [12,3,5,4,7,3,1,3]
    print A
    IpQuickSort(A,0,len(A)-1)
    print A

if __name__ == "__main__": main()

1

Алгоритм має 4 простих кроки:

  1. Поділіть масив на 3 різні частини: ліву, поворотну і праву, де в повороті буде лише один елемент. Давайте виберемо цей опорний елемент як перший елемент масиву
  2. Додайте елементи до відповідної частини, порівнюючи їх з поворотним елементом. (пояснення в коментарях)
  3. Повторіть цей алгоритм, поки всі елементи масиву не будуть відсортовані
  4. Нарешті, з’єднайте ліву + шарнірну + праву частини

Код алгоритму в python:

def my_sort(A):

      p=A[0]                                       #determine pivot element. 
      left=[]                                      #create left array
      right=[]                                     #create right array
      for i in range(1,len(A)):
        #if cur elem is less than pivot, add elem in left array
        if A[i]< p:
          left.append(A[i])         
          #the recurssion will occur only if the left array is atleast half the size of original array
          if len(left)>1 and len(left)>=len(A)//2:          
              left=my_sort(left)                            #recursive call
        elif A[i]>p: 
          right.append(A[i])                                #if elem is greater than pivot, append it to right array
          if len(right)>1 and len(right)>=len(A)//2:        # recurssion will occur only if length of right array is atleast the size of original array
              right=my_sort(right)
     A=left+[p]+right                                        #append all three part of the array into one and return it
     return A

my_sort([12,4,5,6,7,3,1,15])

Продовжуйте цей алгоритм рекурсивно з лівою та правою частинами.


1

Ще одна реалізація швидкого сортування:

# 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

Для версії Python 3.x : operatorмодуль функціонального стилю , в першу чергу для поліпшення читабельності.

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

і перевіряється як

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

Хороший (що стосується недокументованого коду ), якщо подібний до відповідей акарки , відповідей Арнальдо П. Фігейри Фігейри та Біргера з минулих років. Не впевнений, що це відповідь, коли запитання читається … complete my code?. Використання lambdaдля визначення sublist()не виглядає суворо необхідним.
greybeard

@greybeard Насправді код Арналдо не скомпільований у Python 3. Крім того, як це sublistможна визначити лише за допомогою filter? Це взагалі можливо?
lifebalance

1
(Тимчасовий коментар: продумати def- ще не почав займатися, оскільки я намагаюся зрозуміти, чи існує більш ефективний спосіб "розподілити" елементи ітерабелу в окремі списки (або, як варіант, один список з одним) категорія "після іншого).)
greybeard

1

Ось проста реалізація: -

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. Спочатку ми оголошуємо перше значення в масиві pivot_value, а також встановлюємо ліву та праву позначки
  2. Ми створюємо перший цикл while, цей цикл while існує, щоб сказати, що процес розділу повинен запускатися знову, якщо він не відповідає необхідній умові
  3. тоді ми застосовуємо процес розділення
  4. після запуску обох процесів розділів ми перевіряємо, чи відповідає він належній умові. Якщо це так, ми позначаємо це як зроблене, якщо ні, ми перемикаємо ліве і праве значення і застосовуємо знову
  5. Після завершення переключіть ліве та праве значення та поверніть split_point

Я додаю код нижче! Ця швидка сортування є чудовим інструментом навчання завдяки розташуванню значення опори . Оскільки воно знаходиться в постійному місці, ви можете пройти його кілька разів і по-справжньому зрозуміти, як це все працює. На практиці найкраще рандомізувати опору, щоб уникнути часу виконання O (N ^ 2).

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

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

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

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

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

18 інших відповідей, більше половини з яких відповідають на оригінальне запитання OP "як об'єднати три масиви". Ваша відповідь додає щось нове?
Teepeemm

0

Повний приклад із друкованими змінними на кроці розділу:

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
надайте пояснення
Рай,

0

Цей алгоритм не використовує рекурсивні функції.

Нехай Nбуде будь-який список чисел з len(N) > 0. ВстановитиK = [N] та запустіть наступну програму.

Примітка: Це стабільний алгоритм сортування.

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)

ВИХІД ПРОГРАМИ:

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

Можливо, це лише переваги, але я люблю додавати перевірку вгорі своїх функцій.

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)
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.