Основы работы с массивами
Структура данных – способ организации хранения данных.
Тип данных – множество значение и операций на этих значениях.
Массив как фундаментальная структура данных- тип или структура данных в виде набора компонентов, расположенных в памяти непосредственно друг за другом, при этом доступ к отдельным элементам массива осуществляется с помощью индексации, то есть ссылки на массив с указание номера.
Хранение массива в памяти компьютера-элементы в памяти находятся в порядке друг за другом, место выделяется по типу массива(int,char)
Объявление и инициализация массивов
Int a [3]={…..}
void insert(int **pa, int n, int i, int x) – добавление нового элемента
{
int *a = (int*)realloc(*pa, (n + 1)*sizeof(int));
for (int j = n; j > i; --j)
{
a[j] = a[j - 1];
}
a[i] = x;
*pa = a;
}
Освобождение памяти – выполняется с помощью функции free();
ПОИСК В МАССИВЕ: СЛУЧАЙНЫЙ И ЛИНЕЙНЫЙ
Алгоритм линейного поиска предельно ясен. В цикле по числу элементов сравнивается очередной элемент массива с образцом. При нахождении элемента, совпадающего с образцом, поиск прекращается. Если цикл завершается без нахождения совпадений, то это означает, что в массиве нет искомого элемента. Время работы такого алгоритма линейно. В худшем случае придется сравнить образец со всеми элементами, в лучшем - с одним, в среднем - число сравнений равно n/2, где n - число элементов массива. У линейного поиска есть один недостаток: если образец не присутствует в массиве, то без принятия предохранительных мер поиск может выйти за границы массива, вследствие чего может возникнуть исключительная ситуация. В классическом варианте линейного поиска приходится на каждом шаге дополнительно проверять корректность значения текущего индекса.
int index_of(int x, int *a, int n) – поиск элемента в массиве
{
for (int i = 0; i < n; ++i)
{
if (a[i] == x)
{
return i;
}
}
int max_of(int *a, int n) – поиск максимального значения
{
int maxx = a[0];
for (int i = 1; i < n; ++i)
{
if (a[i] > maxx)
{
maxx = a[i];
}
}
return maxx;
}
5.Рекурсия
Рекурсия – определение части функции (метода) через саму себя, то есть это функция, которая вызывает саму себя, непосредственно (в своём теле) или косвенно (через другую функцию). Типичными рекурсивными задачами являются задачи: нахождения n!, числа Фибоначчи.
База рекурсии – это тривиальный случай, при котором решение задачи очевидно, то есть не требуется обращение функции к себе.
ПРОСТАЯ И СЛОЖНАЯ РЕКУРСИИ
Процедура или функция может содержать вызов других процедур или функций. В том числе процедура может вызвать саму себя. Никакого парадокса здесь нет – компьютер лишь последовательно выполняет встретившиеся ему в программе команды и, если встречается вызов процедуры, просто начинает выполнять эту процедуру. Без разницы, какая процедура дала команду это делать
Возможна чуть более сложная схема: функция A вызывает функцию B, а та в свою очередь вызывает A. Это называется сложной рекурсией. При этом оказывается, что описываемая первой процедура должна вызывать еще не описанную. Чтобы это было возможно, требуется использовать опережающее описание.
В программном обеспечении переполнение стека (англ. stack overflow) возникает, когда в стеке вызовов хранится больше информации, чем он может держать. Обычно ёмкость стека задаётся при старте программы/потока. Когда указатель стека выходит за границы, программа аварийно завершает работу. Эта ошибка случается по трём причинам.
ИТЕРАЦИОННЫЕ И РЕКУРСИВНЫЕ ВЫЧИСЛИТЕЛЬНЫЕ ПРОЦЕССЫ
Для решения многих видов вычислительных задач требуется неоднократно повторять одни и те же алгоритмические процессы. Такие процессы могут быть описаны с помощью рекурсивных или итеративных процедур. Рекурсивная процедура или функция – это подпрограмма, которая вызывает сама себя, т.е. выполнению рекурсивной подпрограммы предшествует выполнение ее собственной копии. При выполнении рекурсивной подпрограммы осуществляется многократный переход от некоторого текущего уровня организации алгоритма к нижнему уровню последовательно до тех пор, пока, не будет получено тривиальное решение поставленной задачи. Итерация обычно бывает более эффективной, чем рекурсия, поскольку для завершения шага итерации – в отличие от шага рекурсии – не требуется ожидать результатов выполнения последующих шагов. Использование итерации, следовательно, позволяет избежать расходов, связанных с организацией в период исполнения стека латентных вызовов (вызовов еще ожидающих активации), что невозможно при использовании рекурсии. Рекурсивная форма организации алгоритма обычно выглядит изящнее итерационной и дает более компактный текст программы, но при выполнении, как правило, медленнее и может вызвать переполнение стека латентных вызовов.
Хвостовая рекурсия — частный случай рекурсии, при котором любой рекурсивный вызов является последней операцией перед возвратом из функции. Подобный вид рекурсии примечателен тем, что может быть легко заменён на итерацию путём формальной и гарантированно корректной перестройки кода функции.
Мемоизация - это прием сохранения промежуточных результатов, которые могут еще раз понадобиться в ближайшее время, чтобы избежать их повторного вычисления. Этот прием можно рассматривать как разновидность кеширования.
ОЦЕНКА ВЫЧИСЛИТЕЛЬНОЙ СЛОЖНОСТИ РЕКУРСИВНЫХ АЛГОРИТМОВ
Анализ рекурсивного алгоритма требует подсчета количества операций, необходимых для разбиения задачи на части, выполнения алгоритма на каждой из частей и объединения отдельных результатов для решения задачи в целом. Объединяя эту информацию и информацию о числе частей, мы можем вывести рекурентное соотношение для сложности алгоритма.
Анализируя алгоритм, можно получить представление о том, сколько времени займет решение данной задачи при помощи данного алгоритма. Для каждого алгоритма необходимо оценивать, насколько быстро решается задача на множестве (массиве) входных данных длины N.
Одну и ту же задачу часто можно решить при помощи различных алгоритмов. Анализ алгоритмов дает инструмент для выбора алгоритма.
