
- •А. С. Третьяков алгоритмы
- •Структуры данных Оглавление
- •2.1 Введение 20
- •4.1 Введение 69
- •5.1 Введение 87
- •Предисловие
- •Раздел I структуры данных
- •Глава 1. Массивы
- •1.1 Введение
- •1.2.1 Метод деления
- •1.2.2 Метод умножения
- •1.3.1 Открытое хеширование
- •1.3.2 Закрытое хеширование
- •1.4 Ассоциативный массив
- •1.5 Динамический массив
- •Глава 2. Списки
- •2.1 Введение
- •2.2 Связный список
- •2.3 Стек
- •2.4 Очередь
- •2.4.1 Реализация очереди с помощью массива
- •2.4.2 Реализация очереди с помощью указателей
- •Глава 3. Графы
- •3.1 Основные понятия и виды графов
- •3.2 Матрица смежности
- •3.3 Список смежности
- •3.4 Список ребер
- •3.5 Матрица инцидентности
- •Глава 4. Деревья
- •4.1 Введение
- •4.2 Двоичное дерево
- •4.3 Двоичное дерево поиска
- •4.4 Куча
- •4.5.1 Балансировка
- •4.5.2 Добавление узлов
- •4.5.3 Удаление узлов
- •Раздел II алгоритмы
- •Глава 5. Анализ сложности алгоритмов
- •5.1 Введение
- •5.2 Асимптотический анализ
- •Глава 6. Сортировка
- •6.1 Сортировка пузырьком
- •6.2 Сортировка выбором
- •6.3 Сортировка вставками
- •6.4 Сортировка Шелла
- •6.5 Быстрая сортировка
- •6.5.1 Разбиение массива
- •6.5.2 Рекурсивная сортировка частей массива
- •6.6 Сортировка слиянием
- •6.7 Гномья сортировка
- •6.8 Шейкерная сортировка
- •Глава 7. Поиск
- •7.1 Линейный поиск
- •7.2 Двоичный поиск
- •7.3 Интерполяционный поиск
- •Глава 8. Теория чисел
- •8.1 Алгоритм Евклида
- •8.1.1 Метод вычитания
- •8.1.2 Метод деления
- •8.2 Бинарный алгоритм вычисления нод
- •8.3 Быстрое возведение в степень
- •8.4 Решето Эратосфена
- •8.5 Решето Сундарама
- •Глава. 9 Графы
- •9.1 Поиск в ширину
- •9.2 Поиск в глубину
- •9.3 Алгоритм Дейкстры
- •9.4 Алгоритм Флойда – Уоршелла
- •9.5 Алгоритм Беллмана — Форда
1.5 Динамический массив
Массив, размер которого может изменяться во время работы программы, называется динамическим. Иначе говоря, динамический массив – это структура данных переменного размера, позволяющая в процессе выполнения программы автоматически добавлять и удалять элементы. Его максимальный размер может быть определен константой или определяться во время выполнения программы. В первом случае, динамический массив строиться на базе статического массива, а во втором – переменного, то есть такого, размер которому назначается во время работы программы. Бывает, что массив переменной длины принимают за динамический массив. Но часто это не так, поскольку размер второго, как было отмечено, может изменяться автоматически, что неверно для первого. Например, объявлен массив A, размером n, где n – неинициализированная переменная, т. е. на момент начала выполнения программы, n не присвоена никакая величина. Предполагается, что ее значение, а вместе с тем и размер массива, вводиться пользователем. Данная ситуация говорит о том, что A – массив переменной длины, но это не означает, что он обязательно является динамическим, хотя, как уже отмечалось, такое возможно.
Реализация простого динамического массива основывается на следующих манипуляциях. Выделяется массив фиксированного размера. Он делиться на две части: первая хранит элементы динамического массива, а вторая является резервным пространством, которое, по мере необходимости, может быть задействовано. Такая организация, позволяет добавлять элементы в конец динамического массива, а также удалять их, причем делать это за константное время. Если массив заполнен полностью (заполнены обе части исходного массива), то говорят, что исчерпан физический размер; количество элементов, используемых для динамического содержимого массива, называют логическим размером.
Большинство актуальных языков программирования высокого уровня поддерживают динамические массивы. Для работы с последними в них имеются встроенные операторы или функции, а некоторые языки программирования предоставляют несколько вариантов их реализации. Например, C++ позволяет использовать для этого:
функции malloc, calloc, realloc и free;
Пример создания и очистки целочисленного динамического массива, состоящего из 33 элементов:
int *A = malloc(sizeof(int) *33); //создание
free(A); //удаление
операторы new и delete (с их помощью можно только односторонне изменить размер массива – полностью очистить его);
Пример создания и очистки вещественного динамического массива, состоящего из 11 элементов:
float *A=new float[10]; //создание
delete []A; //удаление
класс vector.
Пример создания и очистки целочисленного динамического массива, состоящего из 22 элементов:
vector<int> A(22); //создание
A.clear(); //удаление
Выше, на примерах были продемонстрированы три способа создания и очистки динамического массива в C++. Но это не весь функционал языка. Так, например, класс vector обладает набором методов для доступа к элементам, добавления и удаления их и т. п.
В Pascal динамический массив можно определить двумя способами. Первый предполагает использование процедуры new, а второй процедуры SetLength. В обоих случаях вначале объявляется массив:
var <имя массива>: array of <тип данных>;
После чего, в основном блоке программы используется одна из процедур:
<имя массива>:=new <имя типа>[<значение>]
или
SetLength(<имя массива>, <значение>);
Процедуры SetLength и new резервируют место в памяти под динамическую переменную (в нашем случае под динамический массив).
Обычный статический массив выигрывает у динамического в быстродействии, что компенсируется расширенным функционалом последнего. В общем случае, временная сложность основных операций над элементами динамического массива следующая:
Доступ к элементу по его индексу осуществляется за константное время O(1);
Вставка или удаление элемента:
начало массива: O(n) (линейное время);
середина массива: O(n) (линейное время);
конец массива: O(1) (константное время).