Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

C _Учебник_МОНУ

.pdf
Скачиваний:
206
Добавлен:
12.05.2015
Размер:
11.12 Mб
Скачать

Динамічні структури даних

459

Форма додатка з результатами роботи матиме вигляд

Файл Unit2.cpp:

// Створення першого елемента списку

Element* fir(int x)

{Element *c=new Element; c->d=x;

c->next=0; return c;

}

// Функція долучення елемента у кінець списку

void add(Element* fir, Element** las, int x)

{Element *c=new Element; c->d=x;

c->next=0;

(*las)->next = c; *las = c;

}

// Функція вставлення елемента до списку

void vstavka(Element* c1, Element** las, int x)

{Element* c=new Element; c->d=x; c->next=c1->next;

460

Розділ 13

c1->next=c; if(c1==(*las)) (*las)=c;

}

// Функція вилучення елемента зі списку

void del_el(Element* c1, Element** las)

{Element* c=c1->next; c1->next=c->next;

if(c==(*las)) (*las)=c1; delete c;

}

// Функція звільнення пам‟яті від списку void ochistka(Element *fir)

{Element *c=fir; while (fir!=0) { fir=fir->next;

delete c; c=fir;

}

}

Файл Unit1.cpp: #include "Unit2.h"

//Функція виведення списку до StringGrid

//(параметри – вказівник на початок списку і StringGrid, до якого виводиться список) void setka(Element *fir, TStringGrid *sg)

{Element *c=fir; int n=0; while(c!=0)

{sg->Cells[n][0]=c->d; n++;

c=c->next;

}

sg->ColCount=n;

}

// Функція обчислювання середнього арифметичного додатних елементів списку float sr_ar(Element * fir)

{int sum=0, kol=0; Element *c=fir; while(c!=0)

{if(c->d>0) { sum+=c->d; kol++; } c=c->next;

}

return 1.0*sum/kol;

}

// Функція вилучення зі списку елементів з числовим значенням менше за –1 void vyluch (Element ** fir, Element** las)

{Element* c1=(*fir); bool ok=false;

while((*fir)->d<-1) //Якщо значення першого елемента є менше за –1, { (*fir)=(*fir)->next; //першим стає той, що був другим

 

Динамічні структури даних

461

delete c1;

// і вилучається той, що спочатку був першим

 

ok=true;

 

 

}

while (c1->next!=0)

{if(c1->next->d < -1)

{del_el(c1, las); ok=true; }

else c1=c1->next;

}

if(ok==false)

{ShowMessage("Елементів < -1 нема"); return;}

}

//-------------------------------------------------------------

// При запуску форми StringGrid1 заповнюється випадковими числами void __fastcall TForm1::FormCreate(TObject *Sender)

{randomize();

for(int i=0; i<4; i++) for(int j=0; j<5; j++)

StringGrid1->Cells[j][i]=IntToStr(random(21)-10);

}

//-------------------------------------------------------------

// Кнопка “Розв‟язок”

void __fastcall TForm1::Button1Click(TObject *Sender)

{Element *first=0,*last=0, *ce; int i,j,k=0,sum=0;

for(i=0; i<4;i++) for(j=0; j<5; j++)

if(abs(StrToInt(StringGrid1->Cells[j][i]))<=5)

{if (first==0)

{first=fir(StrToInt(StringGrid1->Cells[j][i]));

last=first;

}

else add(first,&last,StrToInt(StringGrid1->Cells[j][i]));

}

 

setka(first, StringGrid2);

// Виведення списку у StringGrid2

Edit1->Text=FormatFloat("0.00",sr_ar(first));

vyluch(&first, &last);

// Вилучення елементів зі списку

setka(first, StringGrid3);

// Виведення списку до StringGrid3

int x=StrToInt(Edit2->Text);

 

int y=StrToInt(Edit3->Text);

 

ce=first;

 

while(ce!=0)

 

{ if(ce->d==y)

 

vstavka(ce, &last, x);

// Вставлення нового елемента до списку

ce=ce->next;

 

}

 

setka(first, StringGrid4);

// Виведення списку до StringGrid4

ochistka(first);

 

}

462

Розділ 13

13.5 Різновиди списків

За кількістю зв‟язків між елементами списки бувають:

однозв‟язні;

двозв‟язні.

За топологією списки можуть бути:

лінійні;

циклічні.

Список, який було розглянуто у цьому розділі, є лінійний однозв‟язний (однонапрямлений). У такому списку кожний попередній елемент посилається на наступний, і за таким списком можна рухатися лише у напрямку від першого елемента до останнього (лінійно). Інакше кажучи, елементи лінійного списку і зв‟язки поміж ними можна розташувати у пряму лінію, окрім того, зв‟язки поміж елементами мають чіткий і незмінний напрямок.

Інколи виникає потреба проходити за списком в обох напрямках і “знати” не лише наступний елемент, а й попередній. У такому разі до елемента слід долучити ще одне поле – адресу попереднього елемента, тобто кожний елемент матиме два зв‟язки з сусідніми елементами. На рисунку це позначається двома стрілками: вліво (зв‟язок елемента з попереднім) і вправо (зв‟язок елемента з наступним). Такий список називається лінійним двозв‟язним (двонапрямленим), оскільки зв‟язки мають два напрямки: від першого елемента до останнього і від останнього до першого. Схематично зобразити цей список можна так:

 

 

first

 

 

 

 

 

 

 

last

0

 

4

 

–5

 

0

 

1

 

8

0

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Кожний елемент, окрім поля з даними, має два додаткових поля, які містять адреси попереднього й наступного елементів. Унаслідок цього виникає можливість проходження за списком у якому завгодно напрямку.

Оголошення лінійного двозв‟язного списку має вигляд:

struct Element

{ int d; Element* next; Element* prev; };

Функція створення першого елемента масиву: void fir(int x)

{first=new Element; first->d=x; first->next=0; first->prev=0;

last=first;

}

Функція долучення елемента до списку після останнього елемента: void add_end(int x)

{Element* c=new Element; c->d=x;

c->next=0; c->prev=last;

Динамічні структури даних

463

last->next=c; last=c;

}

Функція долучення елемента до списку перед першим елементом: void add_beg(int x)

{Element* c=new Element; c->d=x;

c->prev=0; c->next=first; first->prev=c; first=c;

}

Функція вставлення нового елемента зі значенням х після елемента с1: void insert(Element *c1, int x)

{Element *c=new Element, *c2=c1->next; c->d=x; c->next=0; c->prev=0; c->prev=c1;

c1->next=c;

if(c2!=0){ c->next=c2; c2->prev=c; } else last=c;

}

Функція вилучення елемента с: void del_el(Element* c)

{Element* c1,*c2; c1=c->prev; c2=c->next;

if(c1!=0) c1->next=c2; else first=c2; if(c2!=0) c2->prev=c1; else last=c1;

delete c;

}

c2->prev=c1

с1

c

с2

3

c1->next=c2

Приклад 13.7 Створити список з інформацією про маршрут руху автобуса: назва станції й час, який минув від виїзду з кінцевого пункту до приїзду на зазначену станцію. Перевірити, чи є у списку інформація про зворотний рух (назва першої станції збігається з назвою останньої, назва другої – з назвою передостанньої тощо). Визначити назву кінцевої станції та час у дорозі.

Розв‟язок. Оскільки для порівняння назв станцій треба буде йти за списком водночас з обох кінців, список має бути двонапрямленим лінійним.

464

Розділ 13

З умови завдання невідомо, чи уведено станції до списку за зростанням часу, який минув від моменту відправлення. Тому при створюванні списку слід долучати кожний новий елемент так, щоб наприкінці введення список був відсортованим за зростанням часу руху.

Форма додатка з результатами роботи матиме вигляд

Текст програми:

__fastcall TForm1::TForm1(TComponent* Owner) : TForm(Owner)

{

StringGrid1->RowCount=2; StringGrid1->Cells[0][0]="Назва станції"; StringGrid1->Cells[1][0]="Час у дорозі";

StringGrid2->RowCount=2; StringGrid2->Cells[0][0]="Назва станції"; StringGrid2->Cells[1][0]="Час у дорозі";

}

//-------------------------------------------------------

//Оголошення елемента списку struct Element

{ char naz[20];

char tim[10];

Element* next;

Element* prev;

};

 

Element* first=0, *last=0;

//Створення першого елемента. Параметри функції – рядки: назва станції та час void fir(char *naz, char* tim)

{first=new Element; strcpy(first->naz,naz); strcpy(first->tim,tim); first->next=0; first->prev=0;

last=first;

}

Динамічні структури даних

465

//Долучення елемента до початку списку.

//Параметри функції – рядки: назва станції та час void add_beg(char *naz, char* tim)

{Element* c=new Element; strcpy(c->naz,naz); strcpy(c->tim,tim); c->prev=0; c->next=first;

first->prev=c; first=c;

}

//Долучення елемента до кінця списку.

//Параметри функції – рядки: назва станції та час void add_end(char *naz, char* tim)

{ Element* c=new Element; strcpy(c->naz,naz); strcpy(c->tim,tim); c->next=0; c->prev=last; last->next=c;

last=c;

}

//Виведення елементів списку з першого до останнього.

//Параметр функції – компонент StringGrid

void print_beg(TStringGrid*sg)

{int i=1; sg->RowCount=i+1; Element* c=first;

if(first==0) ShowMessage("Порожній"); while(c!=0)

{sg->RowCount=i+1; sg->Cells[0][i]=AnsiString(c->naz); sg->Cells[1][i]=AnsiString(c->tim);

c=c->next; i++;

}

}

//Виведення елементів списку з останнього до першого.

//Параметр функції – компонент StringGrid

void print_end(TStringGrid*sg)

{int i=1; sg->RowCount=i+1; Element* c=last;

if (last==0) ShowMessage("Empty2"); while(c!=0)

{sg->RowCount=i+1; sg->Cells[0][i]=AnsiString(c->naz); sg->Cells[1][i]=AnsiString(c->tim); c=c->prev; i++;

}

}

466

Розділ 13

//Вставлення нового елемента після елемента с1

void insert(Element *c1, char *naz, char* tim) { Element *c=new Element, *c2=c1->next;

strcpy(c->naz,naz);

// Поля даних нового елемента

strcpy(c->tim,tim);

 

c->next=0; c->prev=0;

c->prev=c1;

// Зв‟язування нового елемента з попереднім елементом

c1->next=c;

 

if(c2!=0)

// Якщо є наступний елемент,

{ c->next=c2;

// новий елемент зв‟язується з наступним,

c2->prev=c;

 

}

 

else last=c;

// інакше новий елемент стає останнім.

}

 

// Вилучення елемента c

 

void del_el(Element* c)

{ Element* c1,*c2;

 

c1=c->prev;

// Попередній елемент

c2=c->next;

// Наступний елемент

if(c1!=0)

// Якщо є попередній елемент,

c1->next=c2;

// він зв‟язується з наступним елементом,

else first=c2;

// інакше наступний стає першим.

if(c2!=0)

// Якщо є наступний елемент,

c2->prev=c1;

// попередній елемент зв‟язується з наступним,

else last=c1;

// інакше попередній елемент стає останнім.

delete c;

// Звільнення пам‟яті

}

 

// Вставлення нового елемента з сортуванням за часом void sort(char *naz, char* tim)

{ Element *c=first;

TTime tm=TTime(tim); // Час у елементі, який вставляється. if(tm<TTime(first->tim))// Якщо час нового елемента менше за час першого,

{ add_beg(naz, tim);

// новий елемент вставляється перед першим

return; }

 

//Проходження за списком, допоки час tm є більше за час елементів

//(відшукування місця для вставлення)

while(c->next!=0 && tm>TTime(c->next->tim)) c=c->next;

if(c->next==0)

// Якщо елемент с є останнім у списку,

add_end(naz, tim);

// новий елемент вставляється після останнього,

else

// інакше новий елемент

insert(c,naz,tim);

// вставляється після знайденого елемента

}

// Очищення списку з кінця void ochistka()

{ Element* c;

while(last!=0) { c=last; last=last->prev; delete c; } first=0;

}

Динамічні структури даних

467

// Кнопка “Долучення до списку”

void __fastcall TForm1::Button1Click(TObject *Sender) { if(first==0)

fir(Edit1->Text.c_str(), Edit2->Text.c_str()); else

sort(Edit1->Text.c_str(), Edit2->Text.c_str());

}

//-------------------------------------------------------

// Кнопка “Переглядання списку”

void __fastcall TForm1::Button2Click(TObject *Sender) { print_beg(StringGrid1);

print_end(StringGrid2);

}

//-------------------------------------------------------

// Кнопка “Розв‟язок”

void __fastcall TForm1::Button3Click(TObject *Sender) { Element* c1=first; Element* c2=last;

bool ok=true; if(strcmp(c1->naz, c2->naz)==0)

ShowMessage("Повернулися до початкової станції.

Час руху=" + AnsiString(c2->tim));

else

{ ShowMessage("Не повернулися до початкової станції"); return;

}

// Проходження за списком з обох кінців і порівнювання станцій while(TTime(c1->tim)<=TTime(c2->tim))

{ if(strcmp(c1->naz, c2->naz)!=0) { ok=false;

ShowMessage("Зворотний маршрут є інший"); break;

}

c1=c1->next; c2=c2->prev;

}

if(ok) ShowMessage("Зворотний маршрут є такий самий");

}

//-------------------------------------------------------

// Кнопка “Очищення”

void __fastcall TForm1::Button4Click(TObject *Sender) { ochistka();

Edit1->Clear(); Edit2->Clear();

for (int i=1; i<StringGrid1->RowCount; i++) for (int j=0; j<2; j++)

StringGrid1->Cells[j][i]="";

for (int i=1; i<StringGrid2->RowCount; i++) for (int j=0; j<2; j++)

StringGrid2->Cells[j][i]="";

}

468

Розділ 13

У циклічному однозв‟язному списку за останнім елементом знов іде перший елемент. Це означає, що у полі next останнього елемента записано адресу першого елемента, тобто

last->next=first;

Схематично такий список можна зобразити як

first

 

 

 

 

 

 

 

last

4

 

–5

 

0

 

1

 

8

 

 

 

 

 

 

 

 

 

 

 

 

 

адреса

 

адреса

 

адреса

адреса

 

адреса

 

 

 

5

 

0

 

1

 

8

 

4

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

чи як

–5

0

first

4

last 1

8

Оголошення циклічного однозв‟язного списку буде таким самим, як оголошення лінійного однозв‟язного списку.

Функція створювання першого елемента:

void fir(int x)

 

{ first=new Element;

first

 

first->d=x;

4

first->next=first;

last=first;

 

 

}

 

Функція долучення нового елемента до списку (між останнім і першим):

void add_el(int x)

 

 

 

 

first

{ Element* c=new Element;

 

 

 

 

c

 

 

 

4

 

 

c->d=x;

 

 

 

 

 

 

 

 

 

 

 

c->next=first;

1

 

 

 

 

 

 

last->next=c;

 

 

 

 

 

 

 

 

 

 

 

 

 

last=c;

 

 

 

 

 

 

 

 

 

 

 

 

 

 

}

 

 

 

 

 

 

–5

Функція виведення списку до Memo:

 

0

 

 

 

 

 

 

 

 

 

 

 

 

void print_el(TMemo* memo)

{ Element* c=first; last if(first==0) {ShowMessage ("Empty"); return;} do{

memo->Lines->Add(IntToStr(c->d)); c=c->next;

}while(c!=first); // Допоки не дістались першого елемента

}

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]