Курс 3.8 — Алгоритмы · Раздел 05 · Урок 5.3

Сортировка обменом (пузырьком)

Проследим, как соседние обмены постепенно упорядочивают массив.

Проследим, как соседние обмены постепенно упорядочивают массив.
5.3 — Сортировка обменом (пузырьком)

Материал урока

Название этого метода идёт от красивой аналогии с пузырьками воздуха в воде: пузырёк всегда стремится всплыть наверх.

Так же и в массиве: при проходе мы сравниваем соседние элементы и «выталкиваем» наибольший из них в конец массива.

За один такой проход мы гарантированно получим правильный максимальный элемент в самом конце. Далее мы повторяем процесс, но уже на сокращённом массиве, исключив последний элемент.

Идея алгоритма

  1. Начинаем с самого начала массива.
  2. Сравниваем элементы попарно: arr[i] и arr[i+1].
  3. Если они стоят в неправильном порядке (левый больше правого), меняем их местами.
  4. После первого прохода самый большой элемент «всплывает» в конец массива.
  5. Повторяем то же самое, каждый раз уменьшая рассматриваемую часть массива на один элемент.

Так как на каждом шаге мы проверяем пары, алгоритм стабилен: если встречаются равные элементы, их порядок в итоговой последовательности не изменится.

Сложность алгоритма

На первый взгляд пузырьковая сортировка кажется простой. Но если мы внимательно посмотрим, то за каждый проход по массиву мы делаем примерно n сравнений, а самих проходов тоже порядка n.

Поэтому сложность алгоритма — O(n²).

Однако есть оптимизация: если в каком-то проходе мы не сделали ни одного обмена, значит, массив уже отсортирован, и можно завершить работу.

Рекурсивный вариант

def bubble_sort_recursive(arr, length=None):
    """Рекурсивная пузырьковая сортировка"""
    if length is None:
        length = len(arr)
    if length > 1:
        for i in range(length - 1):
            # Меняем местами элементы, если они стоят неправильно
            if arr[i] > arr[i + 1]:
                arr[i], arr[i + 1] = arr[i + 1], arr[i]
        # Рекурсивный вызов на сокращённом массиве
        bubble_sort_recursive(arr, length - 1)

# Пример
nums = [5, 1, 4, 2, 8]
bubble_sort_recursive(nums)
print(nums)  # [1, 2, 4, 5, 8]

Итеративный вариант (с оптимизацией)

def bubble_sort_iter(arr):
    """Итеративная пузырьковая сортировка с проверкой на упорядоченность"""
    n = len(arr)
    for i in range(n):
        sorted_flag = True
        for j in range(n - i - 1):
            if arr[j] > arr[j + 1]:
                arr[j], arr[j + 1] = arr[j + 1], arr[j]
                sorted_flag = False
        # Если обменов не было — массив уже отсортирован
        if sorted_flag:
            break

# Пример
nums = [5, 1, 4, 2, 8]
bubble_sort_iter(nums)
print(nums)  # [1, 2, 4, 5, 8]

Вариант для NumPy

import numpy as np

def bubble_sort_numpy(arr):
    """Итеративная пузырьковая сортировка для numpy.array"""
    arr = arr.copy()  # Чтобы не портить исходный массив
    n = len(arr)
    for i in range(n):
        sorted_flag = True
        for j in range(n - i - 1):
            if arr[j] > arr[j + 1]:
                arr[j], arr[j + 1] = arr[j + 1], arr[j]
                sorted_flag = False
        if sorted_flag:
            break
    return arr

# Пример
arr = np.array([64, 34, 25, 12, 22, 11, 90])
print(bubble_sort_numpy(arr))  # [11 12 22 25 34 64 90]

Пузырьковая сортировка — один из самых простых для понимания алгоритмов, но на практике почти не используется из-за своей квадратичной сложности.

Тем не менее, её важно разобрать:

  • она помогает понять идею пошагового упорядочивания массива,
  • она показывает пример стабильной сортировки,
  • на её основе удобно вводить тему оптимизаций и проверки на упорядоченность.