Что такое временная сложность алгоритма (Big O)?

Ответ

Временная сложность алгоритма — это абстрактная мера количества элементарных операций, выполняемых алгоритмом, в зависимости от размера входных данных (обычно обозначаемого как n). Она описывается с помощью асимптотической нотации "О-большое" (Big O), которая показывает верхнюю границу роста времени выполнения при стремлении n к бесконечности.

Основные классы сложности (от лучшего к худшему):

Сложность Название Пример
O(1) Константная Доступ к элементу массива по индексу.
O(log n) Логарифмическая Бинарный поиск в отсортированном массиве.
O(n) Линейная Поиск элемента в неотсортированном массиве (в худшем случае).
O(n log n) Линейно-логарифмическая Эффективные алгоритмы сортировки (например, Merge Sort, QuickSort в среднем).
O(n²) Квадратичная Пузырьковая сортировка, два вложенных цикла.
O(2ⁿ) Экспоненциальная Решение задачи коммивояжера полным перебором.

Примеры кода:

// O(1) - Константная
int getElement(int[] arr, int index) {
    return arr[index]; // Одна операция, не зависит от размера массива
}

// O(n) - Линейная
boolean contains(int[] arr, int target) {
    for (int num : arr) { // Цикл выполнится n раз
        if (num == target) return true;
    }
    return false;
}

// O(n²) - Квадратичная (пузырьковая сортировка)
void bubbleSort(int[] arr) {
    int n = arr.length;
    for (int i = 0; i < n-1; i++) {        // Внешний цикл: O(n)
        for (int j = 0; j < n-i-1; j++) {  // Внутренний цикл: O(n)
            if (arr[j] > arr[j+1]) {
                // swap
            }
        }
    }
} // Итог: O(n) * O(n) = O(n²)

Зачем это нужно? Временная сложность позволяет:

  • Сравнивать алгоритмы независимо от конкретного железа или языка.
  • Прогнозировать поведение на больших объемах данных.
  • Выбирать оптимальный алгоритм для решения задачи.

Ответ 18+ 🔞

А, ну вот, опять про эту вашу сложность алгоритмов, блядь. Сидят, умничают, «о-большое», «асимптотика»… Давайте я вам на пальцах, как для дебилов, объясню, а то вы тут заумничаете, а потом программа на трёх элементах работает, а на трёх миллионах — накрылась медным тазом, пиздец.

Смотрите сюда. Есть у вас задача. Вы её решаете. Временная сложность — это просто ответ на вопрос: «Насколько, блядь, медленнее будет работать твой код, если данных станет овердохуища?».

Вот представьте, вам нужно найти человека в толпе.

  • O(1) — это ты стоишь на трибуне, у тебя в руках мегафон, и ты кричишь: «Васян, выходи!». Васян сразу выходит. Неважно, там тысяча человек или миллион — ты сделал одно действие. Константа, ёпта. Как доступ к элементу массива по индексу — ты сразу знаешь, куда прыгнуть.
  • O(log n) — это ты хитрый. Толпа отсортирована по росту. Ты становишься посередине и кричишь: «Васян, ты выше метра восьмидесяти?». Он говорит «нет». Бля, отлично — ты отбрасываешь половину высоких уродов. И так раз за разом делишь оставшихся пополам. Это бинарный поиск, хитрая жопа. Растёт оч-чень медленно, почти как константа.
  • O(n) — это ты просто идёшь и тыкаешь каждого в толпе пальцем: «Ты Васян? Нет. Ты Васян? Нет». В худшем случае обойдёшь всех. Данных (n) в 10 раз больше — времени в 10 раз больше. Линейно, предсказуемо, скучно.
  • O(n log n) — это когда тебе надо не найти одного Васю, а всю толпу построить по росту. Ты используешь умные методы — делишь на кучки, сортируешь кучки, потом сливаешь. Быстро, эффективно. Merge Sort, QuickSort — вот это всё.
  • O(n²) — это пиздец, товарищи. Это ты для каждого человека в толпе подходишь и жмёшь руку всем остальным. Два вложенных цикла, блядь. На 10 людях — 100 рукопожатий. На 1000 людях — уже миллион, ёбана! Пузырьковая сортировка — классический пример такого распиздяйства. На маленьких массивах сойдёт, а на больших — просто повесится.
  • O(2ⁿ) — это, сука, адский огонь, экспоненциальный пиздец. Это когда ты пытаешься перебрать все возможные комбинации, как в задаче коммивояжёра. Каждый новый элемент удваивает время работы. Добавил 10 элементов — программа будет работать в 1024 раза дольше. Добавил ещё 10 — уже в миллион раз. Это не алгоритм, а самоубийство.

Вот вам примеры кода, смотрите, не трогайте их, они правильные:

// O(1) - Константная. Тыкнул и получил. Волшебство, блядь.
int getElement(int[] arr, int index) {
    return arr[index];
}

// O(n) - Линейная. Идёшь и смотришь, как лох, каждого спросишь.
boolean contains(int[] arr, int target) {
    for (int num : arr) {
        if (num == target) return true;
    }
    return false;
}

// O(n²) - Квадратичная. Тупое, но наглядное рукопожатие.
void bubbleSort(int[] arr) {
    int n = arr.length;
    for (int i = 0; i < n-1; i++) {
        for (int j = 0; j < n-i-1; j++) {
            if (arr[j] > arr[j+1]) {
                // swap
            }
        }
    }
}

А зачем это всё, спрашиваете? Да чтобы не быть идиотом!

  1. Сравнить алгоритмы. Один делает за O(n log n), другой за O(n²). Первый — умный красавчик, второй — тупой валенок. Выбирай первого.
  2. Прикинуть масштаб трагедии. Напишешь квадратичный алгоритм для базы пользователей — а она вырастет в сто раз. И всё, твой сервер ляжет, как сука, с криком «ой, всё!». Прогноз — вещь полезная.
  3. Выбрать инструмент. Гвоздь микроскопом забивать — O(2ⁿ) по идиотизму. Для каждой задачи свой алгоритм, блядь.

Короче, учите матчасть, а то будете как Герасим из «Муму» — сила есть, а ума не приложить куда, и получится O(n²) вместо O(n log n). В рот меня чих-пых!