Search
Write a publication
Pull to refresh

Алгоритмы базовых сортировок

Level of difficultyEasy

В каждой сфере есть базовые знания, которыми должен обладать специалист. Для химиков, база - это знание неорганической химии, для писателя - знание грамматики, а для программиста - базовые алгоритмы. В данной статье их будет 4: пузырьковая сортировка, сортировка вставками, сортировка слиянием и быстрая сортировка.

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

Пузырьковая сортировка

Идея пузырьковой сортировки: даётся массив чисел А, каждый элемент которого сравнивается со следующим ( A[i] ? A[i+1] ), и если следующий элемент меньше( A[i] > A[i+1]), то элементы меняются местами.

Иллюстрация пузырьковой сортировки
Иллюстрация пузырьковой сортировки

Сложность такого алгоритма О(n2) - в худшем случае (если список отсортирован по убыванию) и O(n), если список уже отсортирован и мы сделали лишь проверку.

Память О(n)

def bambl_sorted(n_list, permutation):
    if permutation == 0:  # проверка, была ли перестановка в прошлой итерации
        return n_list
    
    else:
        permutation = 0
        i = 0
        for j in range(len(n_list)-1):
            if n_list[i] > n_list[i+1]:
                oper = n_list[i] # вводим переменную для смены двух элементов
                n_list[i] = n_list[i+1]
                n_list[i+1] = oper
                permutation = 1
            
            
            i += 1

        bambl_sorted(n_list, permutation)
        return n_list

n_list = list(map(int, input().split()))

print(*bambl_sorted(n_list, 1), sep=' ') # делаем красивый вывод

Пояснение к коду:

  1. Объявляем функцию bambl_sorted с 2 аргументами (исходный список и количество перестановок, которые можно заменить на булевы значения - True/False).

  2. Если количество перестановок равно 0, то возращаем список (он уже отсортирован).

  3. Иначе, объявляем, что перестановок 0 и i = 0 (индекс равен нулю).

  4. Проходим циклом по списку и сравниваем элементы, если итерируемый элемент (A[i]) больше следующего (A[i+1]), то тогда меняем их местами и объявляем, что перестановок 0.

  5. После окончания списка вновь вызываем функцию bambl_sorted.

  6. Возращаем отсортированный список.

  7. Стандартный ввод и вывод.

Сортировка вставками

Идея ставок очень похожа на пузырьковую сортировку и отдалённо напоминает бинарный_поиск, только вместо того, чтобы сравнивать элемент с последующим, он сравнивается с элементами предыдущего отсортированного подмассива до тех пор, пока не дойдет до A[i-1] >= A[i] >= A[i+1].

Иллюстрация сортировки вставками
Иллюстрация сортировки вставками

Сложность алгоритма O(n2)

Память O(n)

Более наглядная иллюстрация (взято с википедии)
Более наглядная иллюстрация (взято с википедии)
def insertion_sort(n_list):
    for i in range(1, len(n_list)):
        oper = n_list[i]
        j = i - 1
        while (j >= 0 and oper < n_list[j]):
            n_list[j + 1] = n_list[j]
            j = j - 1
        n_list[j + 1] = oper

    return n_list

n_list = list(map(int, input().split()))
print(*insertion_sort(n_list), sep=' ')

Разберём основные фрагменты кода:

  1. Объявляем функцию insertion_sort с исходным списком в аргументе.

  2. Внутри функции проходимся циклом по всему списку.

  3. Запустим цикл, который будет находить место каждому элементу.

  4. Возращаем отсортированный массив и выходим из функции.

  5. Стандартный ввод и вывод.

Сортировка слиянием

Идея сортировки слиянием: делим массив на множество подмассивов длинной от 1 до 2 элементов, сортируем, а далее собираем в итоговый массив подмассивы, попутно сортируя их.

Авторство: Swfung8. Собственная работа, CC BY-SA 3.0, https://commons.wikimedia.org/w/index.php?curid=14961648
Авторство: Swfung8. Собственная работа, CC BY-SA 3.0, https://commons.wikimedia.org/w/index.php?curid=14961648

Сложность алгоритма O(n log n)

Память O(n log n)

def menge_sort(a_list):
    if len(a_list) > 2:
        mid = len(a_list)// 2

        b = a_list[:mid]
        c = a_list[mid:]

        del a_list

        b_list = menge_sort(b)
        b = len(b) - 1
        c_list = menge_sort(c)
        c = len(c) - 1

    else:
        mid = len(a_list)// 2

        b_list = a_list[:mid]
        b = len(b_list) - 1
        c_list = a_list[mid:]
        c = len(c_list) - 1
        del a_list

    i1 = 0
    i2 = 0
    a_list = []

    while i1 <= b and i2 <= c:
        if b_list[i1] > c_list[i2]:
            a_list.append(c_list[i2])
            i2 += 1
        else:
            a_list.append(b_list[i1])
            i1 += 1

    while i1 <= b:
        a_list.append(b_list[i1])
        i1 += 1

    while i2 <= c:
        a_list.append(c_list[i2])
        i2 += 1

    del b_list, c_list, b, c

    return a_list

n_list = list(map(int, input().split()))

print(*menge_sort(n_list), sep=' ')

Пояснение к коду:

  1. Объявляем функцию menge_sort с массивом А в аргументе.

  2. Делим массив на 2 подмассива до тех пор пока он больше 2.

  3. Сортируем подмассивы (те маленькие, которые длинной меньше или равна 2).

  4. "Сливаем" эти подмассивы в один, сортируя их.

  5. Возращаем итоговый массив.

Быстрая сортировка

Идея быстрой сортировки: даётся массив А, берём медиану (число которое находится в середине, если массив упорядочен) из 3 чисел А[0], A[(o+n-1)/2], A[n-1] - это будет опорный элемент. Делаем 3 указателя: между подмассивом, где меньше опорного элемента и равны; подмассивом от равных до бОльших; рассматриваемый элемент. Далее, рекурсией применяем такую же операцию к подмассивам, где все элементы меньше опорного и больше, пока их длина больше 2.

Иллюстрация быстрой сортировки (опорный элемент равен 3)
Иллюстрация быстрой сортировки (опорный элемент равен 3)

Сложность алгоритма O(n log n)

Память O(n)

def median(a, b, c):
    if a < b:
        if a > c:
            if b > c:
                return b
            else:
                return c
        else:
            return a
        
    else:
        if b > c:
            if a > c:
                return a
            else:
                return c
        else:
            return b

def partition(n_list, beginning, end):    
        
        x = median(n_list[beginning], n_list[(beginning + end) // 2], n_list[end])
        n, e, g = beginning, beginning, beginning

        while end >= n:

            if n_list[n] < x:
                y = n_list[n]
                n_list[n] = n_list[g]
                n_list[g] = n_list[e]
                n_list[e] = y
                e += 1
                g += 1

            elif n_list[n] == x:
                y = n_list[n]
                n_list[n] = n_list[g]
                n_list[g] = y
                g += 1

            n += 1

        if e < 2:
            return [0, g]
        else:
            return [e-1, g]

def quick_sort(n_list, beginning, end):
    if (end-beginning) > 1:
        separating_indexes = partition(n_list, beginning, end)

        quick_sort(n_list, beginning, separating_indexes[0])
        quick_sort(n_list, separating_indexes[1], end)

    else:
        if end > beginning:
                
            if n_list[beginning] > n_list[end]:
                n_list[beginning], n_list[end] = n_list[end], n_list[beginning]
        
    return n_list

n_list = list(map(int, input().split()))

if len(n_list) <= 1:
    print(*n_list, sep=' ')
else:
    print(*quick_sort(n_list, 0, len(n_list)-1), sep=' ')

Разберём основные фрагменты кода (тут разберём по функциям):

  1. Функция Median

    Тут только ищем среднее значение :)

  2. Функция Partition с аргументами: массив, индекс начала подмассива, индекс конца подмассива:

    Делаем 3 указателя: между частью, где меньше опорного элемента и равны; подмассивом от равных до бОльших; рассматриваемый элемент. Сортируем относительно опорного элемента и возращаем индексы неотсортированного.

  3. Функция Quick_sort с аргументами: массив, индекс начала подмассива, индекс конца подмассива:

    Если длина подмассива больше 1 (в одном случае может быть 2), применяем функцию Partition, далее рекурсивно вызываем Quick_sort для неотсортированных частей массива. Если длина больше или равна 1, сортируем крайние элементы. Возращаем отсортированный список.

  4. Стандартный ввод и вывод (если длина массива не больше 1, то нет смысла сортировать).


Надеюсь статья была вам полезна.

Также для лучшего изучения алгоритмов советую прочитать А. Бхаргава "Грокаем алгоритмы" и Т. Рафгарден "Совершенный алгоритм. Основы".

Tags:
Hubs:
You can’t comment this publication because its author is not yet a full member of the community. You will be able to contact the author only after he or she has been invited by someone in the community. Until then, author’s username will be hidden by an alias.