🔴 Сложный ⏱️ 25 минут

Алгоритмы сортировки: от пузырька до QuickSort

Алгоритмы сортировки: от пузырька до QuickSort

🎯 Зачем это нужно?

Представь, что у тебя 10 миллионов постов в Instagram и нужно показать их по дате - от новых к старым 📱. Или YouTube должен отсортировать видео по количеству просмотров. А Google выдает результаты поиска за 0.2 секунды, хотя проверяет миллиарды страниц!

Где используется:

  • 🔍 Google: сортирует результаты по релевантности
  • 📺 Netflix: сортирует фильмы по рейтингу
  • 🛒 Маркетплейсы: сортируют товары по цене/популярности
  • 📊 Базы данных: индексы работают на сортировке

📚 История вопроса

В 1945 году Джон фон Нейман изобрел merge sort для сортировки данных на первых компьютерах. В 1960-х Тони Хоар создал QuickSort, который до сих пор используется в большинстве языков программирования по умолчанию!

Забавный факт: сортировка пузырьком получила название из-за того, что элементы “всплывают” наверх как пузырьки в газировке 🥤

💡 Интуиция

Сортировка = наведение порядка!

Есть три основных подхода:

  1. Сравнение соседей (Bubble Sort) - как расставить людей в очереди по росту
  2. Разделяй и властвуй (Merge Sort) - как собрать пазл из кусочков
  3. Выбор опорного элемента (QuickSort) - как рассадить учеников по оценкам относительно “троечника”

[МЕДИА: image_01] Описание: Визуализация трех основных подходов к сортировке с аналогиями Промпт: “educational illustration showing three sorting approaches: bubble sort as people in queue, merge sort as puzzle pieces combining, quicksort as students grouped around average grade, modern clean design, suitable for technical audience”

📐 Формальные определения

Задача сортировки: Дан массив A[1…n], нужно переставить элементы так, чтобы A[1] ≤ A[2] ≤ … ≤ A[n].

Сложность алгоритма - количество операций в зависимости от размера данных:

  • O(n²) - квадратичная (плохо для больших данных)
  • O(n log n) - логарифмическая (хорошо!)
  • O(n) - линейная (идеально, но только в особых случаях)

🔍 Примеры с разбором

1. Bubble Sort (Сортировка пузырьком)

Идея: Сравниваем соседние элементы и меняем местами, если они не по порядку.

def bubble_sort(arr):
    n = len(arr)
    for i in range(n):
        for j in range(0, n-i-1):
            if arr[j] > arr[j+1]:
                arr[j], arr[j+1] = arr[j+1], arr[j]  # swap

Пример: [64, 34, 25, 12, 22, 11, 90]

Проход 1:

  • 64 > 34 → меняем: [34, 64, 25, 12, 22, 11, 90]
  • 64 > 25 → меняем: [34, 25, 64, 12, 22, 11, 90]
  • …продолжаем…
  • Результат: [34, 25, 12, 22, 11, 64, 90] (90 “всплыл” в конец!)

Сложность: O(n²) - медленно для больших массивов

2. Quick Sort (Быстрая сортировка)

Идея: Выбираем “опорный” элемент (pivot), делим массив на две части: меньше опорного и больше опорного, рекурсивно сортируем части.

def quicksort(arr):
    if len(arr) <= 1:
        return arr
    
    pivot = arr[len(arr) // 2]  # выбираем средний элемент
    left = [x for x in arr if x < pivot]
    middle = [x for x in arr if x == pivot] 
    right = [x for x in arr if x > pivot]
    
    return quicksort(left) + middle + quicksort(right)

Пример: [3, 6, 8, 10, 1, 2, 1]

  • Pivot = 10
  • Left = [3, 6, 8, 1, 2, 1] (меньше 10)
  • Right = [] (больше 10)
  • Рекурсивно сортируем left…

Сложность: O(n log n) в среднем, O(n²) в худшем случае

[МЕДИА: image_02] Описание: Пошаговая визуализация работы QuickSort с разбиением массива Промпт: “step-by-step QuickSort algorithm visualization, showing array partitioning around pivot element, tree-like recursive structure, arrows indicating data flow, technical illustration style”

3. Merge Sort (Сортировка слиянием)

Идея: Делим массив пополам, рекурсивно сортируем половинки, сливаем отсортированные части.

def merge_sort(arr):
    if len(arr) <= 1:
        return arr
    
    mid = len(arr) // 2
    left = merge_sort(arr[:mid])
    right = merge_sort(arr[mid:])
    
    return merge(left, right)  # сливаем два отсортированных массива

Сложность: Всегда O(n log n) - стабильно быстро!

🎮 Практика

Базовый уровень 🟢

Задание 1: Реализуй пузырьковую сортировку для массива [5, 2, 8, 1, 9]. Покажи состояние массива после каждого прохода.

Задание 2: Для массива [10, 3, 15, 7] выполни один шаг QuickSort с pivot = 7. Какие элементы попадут в левую и правую части?

Задание 3: Сколько сравнений сделает bubble sort для массива из 5 элементов в худшем случае?

Задание 4: Объясни, почему merge sort всегда работает за O(n log n), а quicksort иногда за O(n²).

Продвинутый уровень 🟡

Задание 5: Реализуй функцию merge для слияния двух отсортированных массивов [1, 4, 7] и [2, 5, 8] в один отсортированный.

Задание 6: Для quicksort: какой pivot лучше выбрать для массива [1, 2, 3, 4, 5] и почему?

Задание 7: Оцени количество операций для сортировки 1000 элементов алгоритмом O(n²) и O(n log n). Во сколько раз второй быстрее?

Задание 8: Придумай ситуацию, когда стабильная сортировка важнее быстрой. (Подсказка: сортировка студентов по оценкам, но при равных оценках сохраняем алфавитный порядок)

Челлендж 🔴

Задание 9: Реализуй “умную” версию quicksort: если массив почти отсортирован, переключайся на insertion sort. При каком размере это имеет смысл?

Задание 10: Докажи, что любой алгоритм сортировки, основанный на сравнениях, не может работать быстрее чем O(n log n) в общем случае.

Задание 11: Исследуй counting sort и radix sort. В каких случаях они работают за O(n)?

⚠️ Частые ошибки

Ошибка: “QuickSort всегда быстрее bubble sort” ✅ Правильно: QuickSort быстрее в среднем, но на уже отсортированном массиве может работать за O(n²) 💡 Почему: При плохом выборе pivot каждый раз делим массив 1:(n-1) вместо n/2:n/2

Ошибка: “Сложность O(n log n) означает, что алгоритм всегда работает в log n раз медленнее O(n)” ✅ Правильно: При n=1000, разница между n и n log n составляет примерно 10 раз 💡 Почему: log₂(1000) ≈ 10, поэтому 1000·10 vs 1000

Ошибка: Забывать учитывать дополнительную память ✅ Правильно: Merge sort требует O(n) дополнительной памяти, quicksort - O(log n) 💡 Почему: Merge sort создает копии массивов, quicksort использует только стек рекурсии

🎓 Главное запомнить

Bubble Sort: O(n²), простой но медленный, хорош для понимания принципов ✅ QuickSort: O(n log n) в среднем, используется в большинстве библиотек ✅ Merge Sort: Стабильный O(n log n), но требует дополнительную память ✅ Правило: Для n > 100 элементов всегда используй O(n log n) алгоритмы

🔗 Связь с другими темами

Откуда пришли: Анализ алгоритмов (урок 265) дал нам инструменты для оценки сложности Куда ведет:

  • Структуры данных (деревья поиска основаны на сортировке)
  • Алгоритмы на графах (многие используют сортированные списки)
  • Машинное обучение (k-NN требует сортировки по расстоянию)

Понял тему? Закрепи в боте! 🚀

Попрактикуйся на задачах и получи персональные рекомендации от AI

💪 Начать тренировку
💬 Есть вопрос? Спроси бота!