Сортировка массивов:Изучаем алгоритмы
 Пузырьковая сортировка: медленно, грустно, зато понятно
Есть алгоритмы, от которых веет скоростью и изяществом (привет,
quick sort).А есть bubble sort — максимально наглядный, максимально неторопливый.
Но даже у него есть своё предназначение. И в этой статье:
- Покажем, как он работает
 - Напишем реализацию на C и Python
 - Разберём сложность и когда всё же можно применять
 
 Как работает пузырёк?
Представь, что у тебя есть массив чисел. И ты идёшь по нему, сравниваешь пары соседей и меняешь их местами, если они стоят "неправильно". И так — снова, и снова, пока всё не станет отсортировано.
Вот так числа как бы «всплывают» вверх — отсюда и название.
 Реализация на C
	
	
		C:
	
	void bubble_sort(int arr[], int size) {
    for (int i = 0; i < size - 1; i++) {
        for (int j = 0; j < size - i - 1; j++) {
            if (arr[j] > arr[j + 1]) {
                int temp = arr[j];
                arr[j] = arr[j + 1];
                arr[j + 1] = temp;
            }
        }
    }
}
	🛠 Тут два вложенных цикла:
- внешний контролирует количество «проходов»,
 - внутренний — сравнивает пары и «всплывает» большие числа вправо.
 
 Реализация на Python
		Python:
	
	def bubble_sort(arr):
    n = len(arr)
    for i in range(n - 1):
        for j in range(n - i - 1):
            if arr[j] > arr[j + 1]:
                arr[j], arr[j + 1] = arr[j + 1], arr[j]
	Python-версия выглядит проще, но делает те же самые действия.
swapped — чтобы закончить раньше, если всё уже отсортировано.
 Сложность алгоритма
| Сценарий | Сложность |
|------------------------- |--------------|
| Лучший случай | O ( n ) |
| Средний и худший | O(n²) |
| Память | O(1) |
Но при большом
n — всё, привет тормоза.- Каждый элемент участвует в многочисленных сравнениях
 - Неэффективен при больших объёмах
 - Сильно уступает 
merge sortиquick sort 
 Вывод
- понятный,
 - легко реализуемый,
 - полезен в олимпиадной практике и тестах.
 
Если нужно быстро что-то отсортировать на 10–15 элементах — он сойдёт.
 Другие алгоритмы сортировки: сравнение на Python и C
Пузырёк — самый первый алгоритм сортировки, который изучают.
Но что дальше? Где скорость? Где надёжность?
Давайте изучем и сравним:
- Insertion Sort
 - Merge Sort
 - Quick Sort
 
 Insertion Sort — вставками
 Перебираем элементы один за другим- ⬅ Сдвигаем все большие элементы вправо
 
 Вставляем текущий на своё место
		Python:
	
	def insertion_sort(arr):
    for i in range(1, len(arr)):
        key = arr[i]
        j = i - 1
        while j >= 0 and arr[j] > key:
            arr[j + 1] = arr[j]
            j -= 1
        arr[j + 1] = key
	
		C:
	
	void insertion_sort(int arr[], int size) {
    for (int i = 1; i < size; i++) {
        int key = arr[i];
        int j = i - 1;
        while (j >= 0 && arr[j] > key) {
            arr[j + 1] = arr[j];
            j--;
        }
        arr[j + 1] = key;
    }
}
	---
 Merge Sort — сортировка слиянием
Он разбивает массив на две части, сортирует каждую из них и затем сливает обратно в один отсортированный массив.
- Разделить массив пополам
 - Рекурсивно отсортировать каждую часть
 - Аккуратно объединить их в один отсортированный результат
 
---
		Python:
	
	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)
def merge(left, right):
    result = []
    i = j = 0
    # Сливаем два отсортированных массива
    while i < len(left) and j < len(right):
        if left[i] < right[j]:
            result.append(left[i])
            i += 1
        else:
            result.append(right[j])
            j += 1
    # Добавляем оставшиеся элементы
    result.extend(left[i:])
    result.extend(right[j:])
    return result
	---
		C:
	
	void merge(int arr[], int left, int mid, int right) {
    int n1 = mid - left + 1;
    int n2 = right - mid;
    // Временные массивы
    int L[n1], R[n2];
    for (int i = 0; i < n1; i++) L[i] = arr[left + i];
    for (int j = 0; j < n2; j++) R[j] = arr[mid + 1 + j];
    int i = 0, j = 0, k = left;
    // Сливаем L[] и R[] обратно в arr[]
    while (i < n1 && j < n2) {
        if (L[i] <= R[j])
            arr[k++] = L[i++];
        else
            arr[k++] = R[j++];
    }
    // Добавляем оставшиеся элементы
    while (i < n1) arr[k++] = L[i++];
    while (j < n2) arr[k++] = R[j++];
}
void merge_sort(int arr[], int left, int right) {
    if (left < right) {
        int mid = left + (right - left) / 2;
        merge_sort(arr, left, mid);
        merge_sort(arr, mid + 1, right);
        merge(arr, left, mid, right);
    }
}
	Пример использования:
		C:
	
	int main() {
int arr[] = {42, 15, 3, 23, 8, 5};
int n = sizeof(arr) / sizeof(arr[0]);
merge_sort(arr, 0, n - 1);
printf("Отсортированный массив: ");
for (int i = 0; i < n; i++) {
    printf("%d ", arr[i]);
}
printf("\n");
return 0;
}
	Пример итерационного подхода, без рекурсии:
		C:
	
	#include <stdio.h>
/* ――― вспомогательная функция слияния двух отсортированных отрезков ――― */
void merge(int a[], int left_start, int left_end,
           int right_start, int right_end)
{
    int n_left  = left_end  - left_start + 1;
    int n_right = right_end - right_start + 1;
    /* временные массивы для копирования левой и правой частей */
    int L[n_left], R[n_right];
    for (int i = 0; i < n_left;  ++i) L[i] = a[left_start  + i];
    for (int j = 0; j < n_right; ++j) R[j] = a[right_start + j];
    /* сливаем обратно в a[left_start … right_end] */
    int i = 0, j = 0, k = left_start;
    while (i < n_left && j < n_right) {
        if (L[i] <= R[j])
            a[k++] = L[i++];
        else
            a[k++] = R[j++];
    }
    /* дописываем «хвосты», если остались */
    while (i < n_left)  a[k++] = L[i++];
    while (j < n_right) a[k++] = R[j++];
}
/* ――― итеративная (без рекурсии) сортировка слиянием ――― */
void merge_sort(int a[], int n)
{
    /* длина сливаемых блоков: 1, 2, 4, 8, … */
    for (int block = 1; block < n; block *= 2) {
        /* перебираем пары блоков по всему массиву */
        for (int left_start = 0; left_start < n; left_start += 2 * block) {
            int left_end    = left_start + block - 1;
            int right_start = left_end + 1;
            int right_end   = right_start + block - 1;
            /* корректируем правую границу, чтобы не выйти за массив */
            if (right_start >= n)           continue;   /* второй блока нет */
            if (right_end   >= n) right_end = n - 1;
            /* объединяем два соседних блока */
            merge(a,
                  left_start,  left_end,
                  right_start, right_end);
        }
    }
}
/* ――― демонстрация ――― */
int main(void)
{
    int data[] = {42, 15, 3, 23, 8, 5};
    int n = sizeof data / sizeof data[0];
    printf("До  : ");
    for (int i = 0; i < n; ++i) printf("%d ", data[i]);
    puts("");
    merge_sort(data, n);
    printf("После: ");
    for (int i = 0; i < n; ++i) printf("%d ", data[i]);
    puts("");
    return 0;
}
	---
---
🛠 Когда использовать Merge Sort:
- Когда важна стабильность сортировки (сохраняет порядок равных элементов)
 - Когда нужно предсказуемое время выполнения, независимо от входа
 - Когда работаешь с связанными структурами (может быть реализован без выделения дополнительной памяти)
 
---
 Quick Sort — быстрая сортировка
 Рекурсивное разбиение массива
 В среднем O(n log n), в худшем —O(n²)- ⏱ Часто используется как дефолтная сортировка в стандартных библиотеках
 
		Python:
	
	def quick_sort(arr):
    if len(arr) <= 1:
        return arr
    pivot = arr[len(arr) // 2]
    left = [x for x in arr if x < pivot]
    mid  = [x for x in arr if x == pivot]
    right = [x for x in arr if x > pivot]
    return quick_sort(left) + mid + quick_sort(right)
	
		C:
	
	void quick_sort(int arr[], int left, int right) {
    if (left >= right) return;
    int pivot = arr[(left + right) / 2];
    int i = left, j = right;
    while (i <= j) {
        while (arr[i] < pivot) i++;
        while (arr[j] > pivot) j--;
        if (i <= j) {
            int temp = arr[i];
            arr[i] = arr[j];
            arr[j] = temp;
            i++; j--;
        }
    }
    quick_sort(arr, left, j);
    quick_sort(arr, i, right);
}
	Пример итерационного подхода, без рекурсии:
		C:
	
	#include <stdio.h>
/* свап без temp-макросов для простоты */
static inline void swap(int *a, int *b)
{
    int t = *a; *a = *b; *b = t;
}
/* итеративный quick sort */
void quick_sort_iter(int a[], int n)
{
    /* стек хранит пары [left, right] */
    int stack[n];
    int top = -1;
    /* помещаем первый диапазон (весь массив) */
    stack[++top] = 0;
    stack[++top] = n - 1;
    while (top >= 0)
    {
        /* извлекаем правую и левую границу */
        int right = stack[top--];
        int left  = stack[top--];
        if (left >= right) continue;
        /* стандартное разбиение */
        int pivot = a[(left + right) / 2];
        int i = left, j = right;
        while (i <= j)
        {
            while (a[i] < pivot) i++;
            while (a[j] > pivot) j--;
            if (i <= j)
            {
                swap(&a[i], &a[j]);
                i++;  j--;
            }
        }
        /* класть в стек сначала большую половину — экономнее по памяти */
        if (i < right) {          /* правая часть */
            stack[++top] = i;
            stack[++top] = right;
        }
        if (left < j) {           /* левая часть */
            stack[++top] = left;
            stack[++top] = j;
        }
    }
}
/* демонстрация */
int main(void)
{
    int data[] = {42, 15, 7, 23, 3, 9, 31};
    int n = sizeof data / sizeof data[0];
    printf("До   : ");
    for (int k = 0; k < n; ++k) printf("%d ", data[k]);
    puts("");
    quick_sort_iter(data, n);
    printf("После: ");
    for (int k = 0; k < n; ++k) printf("%d ", data[k]);
    puts("");
    return 0;
}
	---
 Вывод
		Код:
	
	| Алгоритм           | Когда использовать                          |
|------------------  |----------------------------------------------|
| Insertion Sort     | маленькие массивы, почти отсортированные     |
| Merge Sort         | нужен гарантированный результат и стабильность |
| Quick Sort         | максимальная скорость при средних данных     |
	
	