Вектор. Функциональная спецификация: восемь операций и главный компромисс

Модуль "Файл и вектор" • урок 3 из 6 • ~7 минут

Суть урока. Вектор - последовательность переменной длины с доступом по индексу за O(1) (индексация с нуля). Плата за мгновенный доступ - дорогая операция ИЗМЕНИТЬ_ДЛИНУ за O(N): новый блок памяти плюс копирование. Спецификация - восемь операций: СОЗДАТЬ, ПУСТО, ДЛИНА, ЗАГРУЗИТЬ, СОХРАНИТЬ, ИЗМЕНИТЬ_ДЛИНУ, РАВНЫ, УНИЧТОЖИТЬ.
Вектор. Функциональная спецификация: доступ по индексу за O(1) и изменение длины за O(N)
Вектор: доступ по индексу за O(1) и цена изменения длины

Что это такое

Вектор - последовательность переменной длины: количество элементов не фиксировано и может меняться. Вектор без единого элемента называется пустым. Но, в отличие от файла с его строго последовательным доступом, вектор дает доступ к любому элементу за постоянное время: сотый элемент читается так же быстро, как первый.

Почему индексация с нуля

Множество допустимых индексов вектора длины n - это {0, ..., n - 1}. Ноль в качестве первого индекса выбран не случайно: адрес элемента вычисляется по формуле адрес начала блока + индекс × размер компоненты. Если бы индексация шла с единицы, к каждому обращению добавлялось бы лишнее вычитание смещения - а это замедление самой частой операции.

Длина вектора - важная характеристика: многие математические операции (сложение векторов, скалярное произведение) определены только для векторов равной длины.

Восемь операций спецификации

Вектор. Функциональная спецификация в деталях

Главный компромисс

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

Правила сохранности при изменении длины:

Тренажер: операции вектора по шагам

Создадим вектор длины 3, запишем в него 5, 3, 8, прочитаем по индексу и дважды поменяем длину: сначала на 5, потом на 2. Знак ? - неопределенное значение.

значение определено ячейка текущей операции значение не определено
Длина: 0 Прочитано: - Копирований: 0 Шаг: 0 / 0
А как в Python? Встроенный list внутри CPython - именно вектор: сплошной динамический массив указателей, а не связный список. Поэтому L[i] работает за O(1). А когда при append() резерв памяти кончается, Python выполняет ту самую ИЗМЕНИТЬ_ДЛИНУ: выделяет блок побольше и копирует туда все элементы.

Коротко: главное

  • Вектор - последовательность переменной длины с доступом по индексу за O(1).
  • Индексация с 0: адрес = база + индекс × размер, без лишнего смещения.
  • Восемь операций: СОЗДАТЬ, ПУСТО, ДЛИНА, ЗАГРУЗИТЬ, СОХРАНИТЬ, ИЗМЕНИТЬ_ДЛИНУ, РАВНЫ, УНИЧТОЖИТЬ.
  • Компромисс: мгновенный доступ оплачен дорогим изменением длины - O(N).
  • При изменении длины старые элементы сохраняются, новые - не определены.

Проверьте себя

Почему операция ИЗМЕНИТЬ_ДЛИНУ у вектора работает за O(N), а не за O(1)?

Как вам урок? Спасибо за отзыв!