Приведи пример алгоритма и объясни его алгоритмическую сложность.

Ответ

Алгоритм: Бинарный поиск в отсортированном списке.

Сложность: O(log n) по времени в худшем и среднем случае. O(1) по памяти (итеративная реализация).

Реализация на Dart:

/// Возвращает индекс [target] в отсортированном списке [sortedList].
/// Возвращает -1, если элемент не найден.
int binarySearch(List<int> sortedList, int target) {
  int low = 0;
  int high = sortedList.length - 1;

  while (low <= high) {
    // Избегаем переполнения для больших списков.
    int mid = low + ((high - low) ~/ 2);
    int midValue = sortedList[mid];

    if (midValue == target) {
      return mid; // Элемент найден.
    } else if (midValue < target) {
      low = mid + 1; // Ищем в правой половине.
    } else {
      high = mid - 1; // Ищем в левой половине.
    }
  }
  return -1; // Элемент отсутствует.
}

void main() {
  final list = [1, 3, 5, 7, 9, 11, 13];
  print(binarySearch(list, 7)); // 3
  print(binarySearch(list, 10)); // -1
}

Почему O(log n)? Алгоритм на каждом шаге делит область поиска пополам. В худшем случае он сделает столько делений, сколько раз n можно разделить на 2, пока не останется 1 элемент. Это и есть логарифм по основанию 2: log₂(n). Например, для списка из 1024 элементов потребуется не более 10 проверок (т.к. 2¹⁰ = 1024).

Ключевое условие: Список должен быть отсортирован. Для несортированного списка бинарный поиск неприменим, и потребуется линейный поиск со сложностью O(n).

Ответ 18+ 🔞

Давай разберём эту штуку, а то смотришь на код и думаешь — ёпта, что за манда с ушами? Но на самом деле всё гениально просто, если не тупить.

Представь, что у тебя есть телефонная книга. Ты же не будешь её листать с первой страницы, когда ищешь «Яковлев»? Нет, ты раскрываешь где-то посередине, смотришь — о, «Петров». Значит, нужно правее. Снова открываешь середину оставшейся части — бац, «Сидоров». Опять маловато. И так, пока не найдёшь. Вот это и есть бинарный поиск, только для чисел в списке.

В чём прикол? Скорость, блядь! Это не линейный поиск, где ты можешь в худшем случае перебрать овердохуища элементов. Здесь на каждом шаге ты отбрасываешь половину оставшегося списка. Получается, даже для списка в миллион элементов тебе хватит максимум 20 проверок. Ни хуя себе, да? Это потому что сложность O(log n). Логарифм — это просто ответ на вопрос: «Сколько раз нужно разделить число n пополам, чтобы получить 1?». Для 1 000 000 это всего около 20 раз. Волнение ебать!

Но есть важнейшее условие, чувак: список должен быть отсортирован. Иначе этот фокус не сработает. Если ты сунешь в него рандомный набор цифр, алгоритм просто накроется медным тазом и выдаёт хуйню. Придётся использовать линейный поиск и смириться с O(n).

Смотри на код:

int binarySearch(List<int> sortedList, int target) {
  int low = 0;
  int high = sortedList.length - 1;

  while (low <= high) {
    // Вот эта хитрая формула — чтобы не было переполнения, если low и high овердохуищные.
    int mid = low + ((high - low) ~/ 2);
    int midValue = sortedList[mid];

    if (midValue == target) {
      return mid; // Ура, нашли! Возвращаем индекс.
    } else if (midValue < target) {
      low = mid + 1; // Наш target правее, отрезаем левую половину.
    } else {
      high = mid - 1; // Наш target левее, отрезаем правую половину.
    }
  }
  return -1; // Проехали. Элемента нет.
}

Как это работает на примере? Берём [1, 3, 5, 7, 9, 11, 13], ищем 7.

  1. low=0, high=6. mid = 0 + (6-0)/2 = 3. sortedList[3] = 7. Ёбать-копать! Уже нашли! Возвращаем 3.

Ищем 10:

  1. low=0, high=6. mid=3. Число 7 < 10? Да. Значит, low = mid + 1 = 4. Теперь ищем в [9, 11, 13].
  2. low=4, high=6. mid = 4 + (6-4)/2 = 5. sortedList[5] = 11. 11 > 10. Значит, high = mid - 1 = 4.
  3. low=4, high=4. mid = 4. sortedList[4] = 9. 9 < 10. low = mid + 1 = 5.
  4. Теперь low=5, high=4. Условие low <= high ложно. Выходим из цикла. Возвращаем -1. Да похуй, не повезло.

Итог: Алгоритм — пизда рулю. Быстрый, элегантный, но требует порядка в данных. Если список отсортирован — это твой лучший друг. Если нет — хуй с горы, ищи по-старинке.