
- •Теоретический раздел лекции Тема 1. Программирование с использованием рекурсии
- •1.1. Cтратегии решения задачи разбиением ее на подзадачи
- •1.2. Программирование рекуррентных соотношений
- •Var z:extended;
- •1.3. Условия окончания рекурсии
- •1.4. О целесообразности использования рекурсии
- •Var I,X,y,z:word;
- •1.5 Правила выбора программной реализации рекуррентных соотношений
- •Тема 2. Задачи перебора вариантов
- •2.1. Модель дерева решений
- •2.2. Задача оптимального выбора (задача о рюкзаке)
- •2.3. Метод полного перебора двоичного дерева
- •Var wt,ct:extended;
- •Var j,k:byte;
- •If k in s then begin
- •Var j:byte;
- •Var j:byte;
- •Var wt1,oct1:byte;
- •2.4. Метод ветвей и границ
- •Var n,I:byte;
- •Var wt1,oct1:Extended;
- •Include(s,I);
- •If I in Sopt then writeln(I,a[I].W,a[I].C);
- •2.5. Эвристические методы
- •Тема 3. Поиск и сортировка массивов записей
- •3.1. Применимость сортировки и поиска
- •3.2. Массив записей и поиск в нем
- •Var m:word;
- •3.3. Сортировки массивов
- •Var c: mas; I,j,k:word;
- •Var m:word;
- •Var I,j:Word;
- •Var I,j,l,r:Word; X:Tk; w:Tzp;
- •Тема 4. Связанные списки с использованием рекурсивных данных
- •4.1. Список, стек, очередь
- •4.2. Списки на основе динамических массивов
- •Inherited create;
- •Var turn:Tlist; с1,c2:Tinf;
- •4.3. Рекурсивные данные и однонаправленные списки
- •Inherited create;
- •Var stec,st1,turn,tr1:Tlist; inf:Tinf;
- •4.4. Начальное формирование, добавление и удаление элементов однонаправленного списка
- •4.5. Разновидности связанных списков
- •Inf:Tinf;
- •Тема 5. Поиск и сортировки на связанных линейных списках
- •5.1. Поиск в однонаправленных списках
- •5.2. Сортировка однонаправленных списков
- •1 3Var Inf:tInf;
- •Тема 6. Использование линейных связанных списков
- •6.1. Вычисления арифметических выражений
- •Var ch,ch1,ch2,chr:char;
- •I:byte;ch,ch1:char;
- •6.2. Сложение больших целых чисел
- •Var u,V,s,t:byte;
- •6.3. Работа с разреженными матрицами
- •Inf:Tinf;
- •Inf:tInf;
- •Var proot,p:Ptree;
- •Var bl:boolean;
- •7.2. Бинарное дерево поиска
- •7.3. Основные операции с бинарным деревом поиска
- •Inf:tInf;
- •Var d1:Tree; c:Tinf; k:Tkey;
- •Var bl:Boolean;
- •Var m:Word;
- •Var p:Ttree; m:Word;
- •Тема 8. Хеширование
- •8.1. Что такое хеширование
- •8.2. Схема хеширования
- •Interface
- •Inf:Tinf;
- •8.4. Другие способы хеширования
- •Практический раздел Указания по выбору варианта
- •Индивидуальные практические работы и контрольные работы
- •Индивидуальная практическая работа №1. Программирование с использованием рекурсии
- •1.1. Понятие рекурсии
- •1.2. Порядок выполнения работы
- •1.2.1. Пример решения задачи
- •Индивидуальная практическая работа №2. Организация однонаправленного списка на основе рекурсивных типов данных в виде стека
- •2.1. Основные понятия и определения
- •Inf:tInf; // информация
- •Контрольная работа №1. Программирование с использованием деревьев на основе рекурсивных типов данных
- •1.1. Понятие древовидной структуры
- •Inf:tInf;
- •1.2. Компонент tTreeView
- •1.3. Бинарное дерево поиска
- •Основные операции с двоичным деревом поиска
- •1.4. Порядок написания программы
- •Inf:tInf;
- •Inherited Free;
- •Var tr:Ttree;
- •1.5. Индивидуальные задания
- •Курсовая работа
- •Литература
1.2. Программирование рекуррентных соотношений
Программно рекуррентные соотношения можно реализовать с помощью обычных циклов (итерационно) и с помощью рекурсии.
Для программирования с помощью рекурсии необходимо и достаточно иметь в распоряжении подпрограммы, допускающие рекурсию, т.е. в своем описании обращение к самой – себе. В языке Delphi процедуры и функции рекурсию допускают.
Приведем два примера рекурсивных подпрограмм:
Вычисление n! Вначале запишем рекурсивное соотношение
а) 0!=1; б) n!=(n-1)!*n.
После чего составим рекурсивную функцию:
... Листинг1.1
function Fn(i:Word):Word;
begin
if i=0 then Fn:=1
else Fn:=i*Fn(i-1);
end;
Обращение к функции для вычисления 6! – u:=Fn(6);
Теперь запишем более сложную функцию для нахождения максимального элемента в массиве из n чисел:
Листинг1.2
Type mas1=array[1..100] of extended;
Function Mxa(a:mas1;n:word):extended;
Var z:extended;
function Mxr(i:Word):extended;
begin
if i=1 then Mxr:=a[1]
Else begin
Z:=Mxr(i-1);
If z>a[i] then Mxr:=z
else Mxr:=a[i];
end;
end; //конец Mxr
begin
Mxa:=Mxr(n)
End;
Обращение: чтение a[i], i=1..n; ma:=Mxa(a,n);
При выполнении правильно организованной рекурсивной подпрограммы вначале осуществляется многократный переход от некоторого текущего уровня (i) организации алгоритма к нижнему (или верхнему) уровню последовательно, до тех пор, пока, наконец не будет получено тривиальное решение задачи. В вышеприведенных примерах: при i=0 или i=1. При обратном переходе от полученного тривиального решения к решению для заданного i=n выполняется решение последовательности элементарных подзадач с нарастанием i (или убыванием), которые здесь описаны после первого else.
Рекурсивная форма программирования алгоритма обычно выглядит изящнее итерационной и дает более компактный текст программы. Однако, такое изящество дается не бесплатно. При каждом переходе к следующему уровню (рекурсивной активации процедуры) происходит создание и запоминание всех ее локальных и формальных параметров. В результате после n-й активации в памяти будет находиться список из n+1 комплектов таких параметров. Организован этот список таким образом, что комплект параметров, засланный в список последним, будет вызываться первым, и наоборот. Такая структура организации списка данных в памяти получила название стек. Под организацию стека каждой программе системой Delphi выделяется определенный ограниченный ресурс оперативной памяти. В результате рекурсивная версия программирования алгоритма выполняется медленнее чем, например, итерационная и может привести к исключительной ситуации EstackOverFlow – переполнение программного стека.
Для того, чтобы минимизировать размер стека рекомендуется из рекурсивной процедуры убирать все локальные переменные и формальные параметры, оставляя только необходимые. Это сделано при написании функции нахождения максимального значения, в результате в стек заносится только значение i.
Рекурсивный вызов может быть прямым, как в выше приведенном примере и косвенным. В этом случае подпрограмма обращается к себе опосредованно, путем вызова другой подпрограммы, в которой содержится обращение к первой, например:
Листинг 1.3
Procedure B(j:byte);Forward;
Procedure A(i:byte);
begin
...
B(i);
end;
Procedure B;
begin
...
A(j);
end;
В этом примере обращение к процедуре B(i) записано раньше, чем ее описание, что в принципе запрещено. Для разрешения этой ситуации используется опережающее описание с помощью стандартной директивы Forward.