Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Полный+текст.doc
Скачиваний:
1
Добавлен:
01.04.2025
Размер:
246 Кб
Скачать
☆

2 .Связанные динамические переменные

2.1 Виды динамических структур

Указатели позволяют строить объекты со сложной, меняющейся структурой. Основными динамическими структурами данных являются:

  • списки, структурные свойства которых определяются относительным положением элементов;

  • деревья, образующие сложные иерархические структуры, в которых каждый последующий элемент имеет только один предшествующий элемент;

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

В настоящем пособии рассмотрены два вида динамических структур – списки и деревья

.

2.2. Построение двунаправленного списка

Для построения списка используется такой тип данных как структура. Имя структурного типа обладает всеми правами имен типов, а это означает, что разрешено определять указатели на структуры:

имя_структурного_типа *имя_указателя_на_структуру;

Значением каждого указателя на структуру может быть адрес структуры того же типа (адрес байта, начиная с которого она размещена в памяти). Одновременно с этим структурный тип задает ее размеры. Доступ к полям структуры обеспечивает опера­ция выбора элемента "—>", исходя из того с какой структурой в данный момент связан указатель. Формат выражения имеет вид:

Имя_указателя—>имя_элемента_структуры;

Другая возможность обращения к полю структуры с помощью адресующего ее указателя - это разименовывание указателя и формирование уточненного имени такого вида: (*имя_указателя).имя_поля_структуры. Операция разименовывания "*" должна относиться только к имени указателя поэтому используются круглые скобки. Таким образом следующих выражения эквивалентны: (*record).page

record->page .

Указатель на структуру может входить в определение того же структурного типа:

struct record {char book[20];

char avtor[10];

int year;

record *next;

record *prev; };

Данная структура может использоваться при решении задачи с библиотечной картотекой. Одна структурная единица образует одну библиотечную карточку и может использоваться как узел двунаправленного линейного списка Рис. 1.

Первые три поля каждого узла содержат информационную часть (info) -это название книги, фамилия автора и год издания.

Рис.1

Четвертое и пятое поле это указатели на следующий узел и соответственно на предыдущий узел, связывающие список в двух направлениях. Для каждого нового узла (для каждой новой карточки), включаемого в двунаправленный линейный список будем каждый раз запрашивать необходимый объем памяти. Адрес начала и конца списка (рис.1) будем сохранять в отдельном указателе структурного типа record *begin и record *end соответственно. Указатель *prev для первого узла списка и указатель*next для последнего узла списка имеют значение NULL(pHc.l), поскольку перед первым нет предыдущего, а после последнего нет следующего за ним.

Для построения списка следует выполнить итерационную процедуру связывания (сцепления) структурированных узлов, посредством инициализации полей указателей *prev и *next каждого узла:

void main(void)

{ int number=0; //счетчик узлов списка

record *begin=NULL, *end=NULL;

make _list(&be gin, &end, number); //вызов функции построения списка }

void make_list(record **begin, record **end, int number)

{ int i=0,key;

record *temp=NULL, *current=NULL; //вспомогательные служебные указатели

clrscr();

cout<<"Задайте число узлов в будущем списке ";

cin>>key;

temp=new (record); // (1) выделена память под первый узел списка

//Заполняются информационные поля первого узла

cout« "\NAME BOOK: "; gets(temp->book);

cout«"\NAME AVTOR:"; gets(temp->avtor);

coul«"\YEAR: "; cin»temp->year;

*begin=temp; // (2) устанавливаем указатель начала списка на первый узел

current=temp; // (3) устанавливаем вспомогательный указатель на первый узел

*end=temp; // (4) устанавливаем указатель конца списка на первый узел

temp->next=NULL; temp->prev=NULL;

i++; number++;

if(key==l) return; // если список состоит из одного узла завершаем работу

while(i<=key-l)

{ temp=new (record); //выделить память для следующего узла, теперь указатель

// *temp указывает на второй узел

i++; number+ + ;

cout<<"\NAME BOOK: "; gets(temp->book);

cout<<"\NAME AVTOR:"; gets(temp->avtor); cout<< "\YEAR: "; cin>>(temp->year); cout<<" "<<endl;

current->next=temp; // {6} связываем 1-ый узел со вторым по указателю

*next temp->prev=current; // {7} связываем 2-ой узел с первым по указателю

*prev current=temp;} // (8) перемещаем вспомогательный указатель на 2-ой узел

temp->next=NULL ;

*end=temp; } // устанавливаем указатель конца списка на последний узел

На рис.2 графически интерпретированы действия операторов программы.

(1) *temp (4) *end

(2) *begin

(3) *current

prev = NULL

next = NULL

*end

info

*begin

(8) *current

(7)

info

next

prev = NULL

prev = current

(5) *temp

next = temp

info

(6)

Рис.2 Ввод-вывод содержимого информационных полей списка .

Содержательной частью функции печати print_forward являются операторы печати с

именами информационных полей текущего узла. При вызове функции осуществля­ется передача текущего указателя **cur (указатель по указателю), адрес которого уравнен с адресом указателя &begin. Таким образом текущий указатель *cur установлен на первый узел списка. Далее его последовательное перемещение (перестановка) от узла к узлу позволяет нам получить доступ к информационным полям всех узлов списка. printJbrward(&begin); //вызов функции

void print_forward(record **cur)

{ record *posr;

clrscr();

if(*cur==NULL) {cout<<"\n список пуст"; return; }

else сои1<<"\nПрямая печать списка:"<<endl;

posr=*cur; while (posrl =NULL)

{ cout<<posr->book<<endl;

cout<<posr->year<<endl;

posr=posr->next; // перемещение указателя

} }

Важным критерием правильности построения списка является процедура (функция) обратной печати т.е. от конца(хвоста) к началу(голове), по второму указате­лю prev. Вывод списка в таком порядке подтверждает правильность его построения . Функция печати print_back аналогична функции print_forward за исключением того, что она получает указатель конца списка *end и соответственно перемещение текущего указателя *cur происходит справа налево, от конца списка к его началу.

print_back(&end); //вызов функции

void print_back(record **cur)

{ record *posr;

clrscr();

if(*cur==NULL) {coul< ""\пСписок пуст; return; }

else соunt <<"\пОбратная печать списка''<<endl;

posr=*cur;

while(posr!=NULL)

{ cout<<posr->book<<ndl;

cout< <posr->year«endl;

posr—posr->prev;

}}

Тогда основная программа будет иметь следующий вид:

#indude<stdio.h>

#include<conio.h>

#indude<stdio. h>

#include<iostream.h>

#indude<string. h>

struct record { char book[20];

char avtor[10];

int year;

record *next;

record *prev;

};

void make_list(record **begin, record **end, int number):

void print_forward(record * *current);

void print_back(record * * current);

void delete_list(record **begin, int number);

void add_to_head(record **begin, int number);

void add_after_number(record **begin, int number);

void delete Jirst(record **begin, int number);

void delete jufterjiumber(record **begin, int number);

void main(void)

int number = 0;

record *begin=NULL, *end=NULL; make_list(&begin, &end, number); add_to_head(&begin, number); add_after_number(&begin,number);

delete first(&begin, number);

print forward (& be gin);

print_back(&end);

delete _after_number(&begin,number);

delete list(&begin,number);

}