Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Списки на СИ++_ Указания к л.р..doc
Скачиваний:
5
Добавлен:
09.11.2019
Размер:
102.4 Кб
Скачать

2.2.Рекурсия при обработке односвязного списка.

Даже такая простая структура, как динамический односвязный список, представляет собой конструкцию вложенных списков, как показано на рисунке 2. На нем собственно данные или содержательные элементы обозначены D, а связи между записями или указатели обозначены L.

список

список

список

список

. . .

Рисунок 2 Рекурсивное представление односвязного списка

Вложенные структуры удобно обрабатывать с помощью рекурсивных процедур. Рассмотрим ту же задачу об односвязном списке, но оформим формирование списка и вывод списка на экран дисплея в виде рекурсивных функций. Сделаем это с целью показать на этом простом примере особенности рекурсивной обработки динамических информационных структур.

В каждом звене списка содержатся полезная информация (данные) и ссылка (адрес) на следующее звено списка. Если такая ссылка нулевая, то список пройден до конца. Для начала просмотра списка нужно знать только адрес его первого элемента.

Рассмотрим, какие действия должны выполнять функция рекурсивного формирования списка и функция его рекурсивного просмотра с выводом данных о звеньях списка на экран дисплея.

Функция рекурсивного заполнения списка ниже в программе имеет прототип:

struct cell * input (void) ;

Она возвращает указатель на сформированный динамический список, заполненный вводимыми с клавиатуры дисплея данными. Предполагается, что при каждом вызове этой функции формируется новый список. Функция заканчивает работу, если для очередного звена списка введено нулевое значение переменной weight. В противном случае заполненная с клавиатуры структура подключается к списку, а ее элементу struct cell *рс присваивается значение, которое вернет функция input(), рекурсивно вызванная из тела функции. После этого функция возвращает адрес очередного звена списка, т.е. значение указателя struct cell * рс.

Прежде чем рассматривать текст функции input( ), помещенный в приведенной ниже программе, еще раз сформулируем использованные в ней конструктивные решения.

1. При каждом обращении к функции input( ) в основной памяти формируется новый список, указатель на начало которого возвращает функция.

2. Выделение памяти для звеньев списка и заполнение значениями элементов звеньев (структур) зависит от тех значений, которые пользователь присваивает элементам.

3. Если для очередного звена списка вводится нулевое значение элемента weight, то звено не подключается к списку, процесс формирования списка заканчивается. Такой набор данных, введенных для элемента очередного звена, считается терминальным.

4. Если нулевое значение элемента weight введено для первого звена списка, то функция input( ) возвращает значение NULL.

5. Список определяется рекурсивно как первое (головное) звено, за которым следует присоединяемый к нему список.

6. Псевдокод рекурсивного алгоритма формирования списка:

Если введены терминальные данные для звена

Освободить память, выделенную для звена

Вернуть нулевой указатель

Иначе:

Элементу связи звена присвоить результат формирования продолжения списка (использовать тот же алгоритм)

Все-если

Вернуть указатель на звено.

Обратите особое внимание на то, что первым оператором рекурсивных алгоритмов является проверка на окончание рекурсии; без этого программа с рекурсией неизбежно будет зацикливаться.

Текст функции input() на языке Си почти полностью соответствует приведенному псевдокоду и учитывает перечисленные конструктивные решения. Для структуры типа struct cell выделяется память. После того как пользователь введет значения данных, выполняется их анализ. В случае терминальных значений библиотечная функция free(p) освобождает память от ненужного звена списка и выполняется оператор return NULL;. В противном случае элементу связи (указатель struct cell * р) присваивается результат рекурсивного вызова функции input(). Далее все повторяется для нового экземпляра этой функции. Когда при очередном вызове input() пользователь введет терминальные данные, функция input() вернет значение NULL, которое будет присвоено указателю связи предыдущего звена списка, и выполнение функции будет завершено.

Функция рекурсивного просмотра и печати списка имеет та­кой прототип:

void output (struct cell * р) ;

Исходными данными для ее работы служит адрес начала списка (или адрес любого его звена), передаваемый как значение указателя - параметра struct cell * р. Если параметр имеет значение NULL - список исчерпан, исполнение функции прекращается. В противном случае выводятся на .экран значения элементов той структуры, которую адресует параметр, и функция output() рекурсивно вызывается с параметром р->рс. Тем самым выполняется продвижение к следующему элементу списка. Конец известен - функция печатает "Список исчерпан!" и больше не вызывает сама себя, а завершает работу.

Текст программы с рекурсивными функциями:

/* Определение структурного типа "Звено списка";*/

#include <iostream>

#include <conio.h>

using namespace std;

//описание структуры "Звено списка"

struct cell

{ char sign[10];

int weight;

cell *pc;

};

//функция ввода и формирования списка

cell *f_input(void)

{ //выделить память для очередного звена списка

cell *p = new cell;

//ввести значения элементов звена

cout << "sign = ";

cin >> p->sign;

cout << "weight = ";

cin >> p->weight;

if(p->weight == 0)

{ delete [] p;

return NULL;

}

p->pc = f_input(); //рекурсивный вызов

return p;

}

//функция печати списка

void f_output(cell *p)

{ if(p == NULL)

{ cout << "\nEnd of listing!"; //Список исчерпан

return;

}

cout << "\nsign = " << p->sign

<< "\tweight = " << p->weight;

f_output(p->pc); //рекурсивный вызов

}

void main()

{ cell *beg = NULL; //начало списка

cout << "Input value struct\n"; //введите значения списка

beg = f_input(); //ввод списка

cout << "\nContent of list"; //Содержимое списка

f_output(beg);

getch();

}

Возможный результат выполнения программы:

Введите данные структур:

Sign=Zoro

weight=l938

Sign=Zodiac

weight=1812

Sign=0

weight=0

Содержимое списка:

Sign==Zoro weight=1938

Sign=Zodiac weight=1812

Список исчерпан!