Проект

Общее

Профиль

Основные алгоритмы сортировки в C

Рассмотрим 5 ключевых алгоритмов сортировки с реализацией на языке C. Каждый алгоритм имеет свои особенности и область применения.


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

Принцип работы:
Попарно сравнивает соседние элементы и меняет их местами, если они не в правильном порядке.

Характеристики:

  • Время: O(n²) в худшем случае
  • Память: O(1)
  • Стабильный

Реализация:

#include <stdio.h>

void bubble_sort(int arr[], int n) {
    for (int i = 0; i < n-1; i++) {
        for (int j = 0; j < n-i-1; j++) {
            if (arr[j] > arr[j+1]) {
                // Меняем элементы местами
                int temp = arr[j];
                arr[j] = arr[j+1];
                arr[j+1] = temp;
            }
        }
    }
}

int main() {
    int arr[] = {64, 34, 25, 12, 22, 11, 90};
    int n = sizeof(arr)/sizeof(arr[0]);
    
    bubble_sort(arr, n);
    
    printf("Отсортированный массив:\n");
    for (int i = 0; i < n; i++)
        printf("%d ", arr[i]);
    
    return 0;
}

2. Сортировка выбором (Selection Sort)

Принцип работы:
На каждом шаге находит минимальный элемент и помещает его в начало.

Характеристики:

  • Время: O(n²)
  • Память: O(1)
  • Нестабильный

Реализация:

#include <stdio.h>

void selection_sort(int arr[], int n) {
    for (int i = 0; i < n-1; i++) {
        int min_idx = i;
        for (int j = i+1; j < n; j++) {
            if (arr[j] < arr[min_idx])
                min_idx = j;
        }
        
        // Меняем найденный минимальный элемент с первым
        int temp = arr[min_idx];
        arr[min_idx] = arr[i];
        arr[i] = temp;
    }
}

int main() {
    int arr[] = {64, 25, 12, 22, 11};
    int n = sizeof(arr)/sizeof(arr[0]);
    
    selection_sort(arr, n);
    
    printf("Отсортированный массив:\n");
    for (int i = 0; i < n; i++)
        printf("%d ", arr[i]);
    
    return 0;
}

3. Сортировка вставками (Insertion Sort)

Принцип работы:
Постепенно строит отсортированную часть массива, вставляя элементы в правильные позиции.

Характеристики:

  • Время: O(n²) в худшем случае, O(n) для почти отсортированных
  • Память: O(1)
  • Стабильный

Реализация:

#include <stdio.h>

void insertion_sort(int arr[], int n) {
    for (int i = 1; i < n; 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;
    }
}

int main() {
    int arr[] = {12, 11, 13, 5, 6};
    int n = sizeof(arr)/sizeof(arr[0]);
    
    insertion_sort(arr, n);
    
    printf("Отсортированный массив:\n");
    for (int i = 0; i < n; i++)
        printf("%d ", arr[i]);
    
    return 0;
}

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

Принцип работы:
Рекурсивно разделяет массив вокруг опорного элемента.

Характеристики:

  • Среднее время: O(n log n)
  • Худший случай: O(n²)
  • Память: O(log n) для рекурсии
  • Нестабильный

Реализация:

#include <stdio.h>

void swap(int* a, int* b) {
    int temp = *a;
    *a = *b;
    *b = temp;
}

int partition(int arr[], int low, int high) {
    int pivot = arr[high];
    int i = (low - 1);
    
    for (int j = low; j <= high-1; j++) {
        if (arr[j] < pivot) {
            i++;
            swap(&arr[i], &arr[j]);
        }
    }
    swap(&arr[i+1], &arr[high]);
    return (i + 1);
}

void quick_sort(int arr[], int low, int high) {
    if (low < high) {
        int pi = partition(arr, low, high);
        
        quick_sort(arr, low, pi-1);
        quick_sort(arr, pi+1, high);
    }
}

int main() {
    int arr[] = {10, 7, 8, 9, 1, 5};
    int n = sizeof(arr)/sizeof(arr[0]);
    
    quick_sort(arr, 0, n-1);
    
    printf("Отсортированный массив:\n");
    for (int i = 0; i < n; i++)
        printf("%d ", arr[i]);
    
    return 0;
}

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

Принцип работы:
Рекурсивно делит массив пополам и сливает отсортированные половины.

Характеристики:

  • Время: O(n log n)
  • Память: O(n)
  • Стабильный

Реализация:

#include <stdio.h>
#include <stdlib.h>

void merge(int arr[], int l, int m, int r) {
    int i, j, k;
    int n1 = m - l + 1;
    int n2 = r - m;
    
    int L[n1], R[n2];
    
    for (i = 0; i < n1; i++)
        L[i] = arr[l + i];
    for (j = 0; j < n2; j++)
        R[j] = arr[m + 1 + j];
    
    i = 0; j = 0; k = l;
    
    while (i < n1 && j < n2) {
        if (L[i] <= R[j]) {
            arr[k] = L[i];
            i++;
        } else {
            arr[k] = R[j];
            j++;
        }
        k++;
    }
    
    while (i < n1) {
        arr[k] = L[i];
        i++; k++;
    }
    
    while (j < n2) {
        arr[k] = R[j];
        j++; k++;
    }
}

void merge_sort(int arr[], int l, int r) {
    if (l < r) {
        int m = l + (r - l)/2;
        
        merge_sort(arr, l, m);
        merge_sort(arr, m+1, r);
        
        merge(arr, l, m, r);
    }
}

int main() {
    int arr[] = {12, 11, 13, 5, 6, 7};
    int n = sizeof(arr)/sizeof(arr[0]);
    
    merge_sort(arr, 0, n-1);
    
    printf("Отсортированный массив:\n");
    for (int i = 0; i < n; i++)
        printf("%d ", arr[i]);
    
    return 0;
}

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

Алгоритм Лучший случай Средний случай Худший случай Память Стабильность
Пузырьковая O(n) O(n²) O(n²) O(1) Да
Выбором O(n²) O(n²) O(n²) O(1) Нет
Вставками O(n) O(n²) O(n²) O(1) Да
Быстрая O(n log n) O(n log n) O(n²) O(log n) Нет
Слиянием O(n log n) O(n log n) O(n log n) O(n) Да

Когда какой алгоритм использовать?

  1. Для маленьких массивов (до 100 элементов):

    • Вставками (если почти отсортирован)
    • Выбором (если важна простота)
  2. Для средних массивов (100-10,000 элементов):

    • Быстрая сортировка (обычно лучший выбор)
    • Слиянием (если нужна стабильность)
  3. Для больших массивов (>10,000 элементов):

    • Слиянием (гарантированная O(n log n))
    • Быстрая (если можно рискнуть худшим случаем)