Перейти к основному содержимому

Big O для памяти

Big O описывает память так же, как описывает время: сколько дополнительного места нужно алгоритму по мере роста входных данных?

Мы учитываем только дополнительное место — переменные и массивы, которые создаёт алгоритм, — а не сами входные данные. Это дополнительное место называют вспомогательной памятью.

Память O(1): фиксированное число переменных, независимо от входных данных. Память O(n): на каждый элемент входных данных сохраняется что-то новое — копия или ассоциативный массив увиденных значений.


Память O(1)

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

Суммирование, поиск максимума и линейный поиск — всё это время O(n), но память O(1): они читают много, а запоминают почти ничего.

Сумма с одной переменной

Весь алгоритм помнит ровно одно число — $sum. Расход памяти не растёт вместе с массивом: память O(1).

Prompt
Напишите PHP-скрипт, который суммирует массив 3, 1, 4, 1, 5, 9, 2, 6, используя одну дополнительную переменную, и выводит сумму, чтобы показать алгоритм со временем O(n), но с памятью O(1).
Вывод: PHP-скрипт
Sum with one variable

Память O(n)

Как только алгоритм строит что-то с одной записью на каждый элемент входных данных — новый массив, ассоциативный массив, — его память становится O(n): удвойте входные данные, и память удвоится.

Само по себе это не плохо. Это просто цена, которую стоит замечать, потому что её можно обменять.

Построение нового массива

Выходной массив получает по одному элементу на каждый элемент входных данных. 5 на входе, 5 на выходе — память растёт вместе с входными данными: память O(n).

Prompt
Напишите PHP-скрипт, который строит новый массив из удвоенных элементов массива 1, 2, 3, 4, 5, добавляя их в пустой массив, а затем выводит входной массив и выходной массив.
Вывод: PHP-скрипт
Build a new array

Обмен памяти на время

Часто можно потратить память, чтобы сэкономить время. Классический пример — Two Sum: найти в массиве два числа, дающих в сумме заданное значение.

Прямой способ проверяет каждую пару — время O(n²), память O(1). Быстрый способ проходит по массиву один раз и запоминает каждое увиденное число как ключ ассоциативного массива. Для каждого числа он спрашивает: «встречалось ли уже target - n?» — время O(n), память O(n).

Такой обмен обычно оправдан: память в машину добавить можно, время — нет.

Two Sum с двумя циклами

Каждая пара проверяется, пока не найдётся совпадение, — до 15 проверок для 6 элементов, и это число растёт как квадрат размера входных данных. Дополнительная память не используется.

Prompt
Напишите PHP-скрипт, который находит в массиве 2, 11, 15, 1, 8, 7 два числа, дающих в сумме 9, проверяя каждую пару двумя вложенными циклами и подсчитывая проверки. Выведите каждое совпадение вместе с числом проверок на этот момент, а в конце выведите общее число проверок.
Вывод: PHP-скрипт
Two Sum with two loops

Two Sum с ассоциативным массивом

Один проход, 6 проверок. Каждое число ищется с помощью isset() и сохраняется в ассоциативном массиве $seen — работа за константное время на элемент. Этот массив и есть память O(n), которая покупает время O(n).

Prompt
Напишите PHP-скрипт, который за один проход находит в массиве 2, 11, 15, 1, 8, 7 два числа, дающих в сумме 9: храните ассоциативный массив уже увиденных чисел и для каждого числа проверяйте с помощью isset, встречалось ли уже целевое значение минус это число. Выведите каждое совпадение, а в конце выведите, что каждый элемент был проверен один раз.
Вывод: PHP-скрипт
Two Sum with an associative array