- •О.Б.Пащенко, н.Ю.Рязанова, м.В.Филиппов
- •О.Б.Пащенко, н.Ю.Рязанова, м.В.Филиппов
- •Указатели , динамические переменные.
- •2 .Связанные динамические переменные
- •2.1 Виды динамических структур
- •2.2. Построение двунаправленного списка
- •2.3 Включение и исключение узлов из списка .
- •2.3.1 Функции включения узловов .
- •2.3.2. Функции исключения элементов .
- •2.4 Односвязные списки
- •3. Двоичные деревья.
- •Список рекомендуемой литературы
- •Оглавление
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);
}
