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

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

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

Файли

439

27) Наведіть фрагмент коду, який записує елементи дійсного масиву (вектора) у текстовий файловий потік у стилі С++:

а) всі елементи в один рядок через кому; б) кожний елемент окремим рядком.

28)Наведіть команди для редагування 4-ої структури бінарного файлового потоку у стилі С++.

29)Наведіть і порівняйте команди для обнуління файла при опрацюванні

устилі С, С++ та через дескриптор.

30)Наведіть фрагмент коду, який записує елементи дійсної матриці 3 5

утекстовий файловий потік у стилі С++.

Розділ 13

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

Динамічна структура даних – це набір однотипних елементів, розміщуваних у пам‟яті динамічно, тобто у процесі виконування програми за допомогою операції new() чи то функції malloc(). Прикладом динамічної структури даних є динамічний масив (див. розд. 6). Окрім динамічних масивів, широко застосовуються інші динамічні структури даних – списки, стеки, черги і бінарні дерева.

13.1 Поняття списку

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

У пам‟яті одновимірний масив з 5-ти елементів (приміром з цілих чисел 4, –5, 0, 1, 8) розміщується в такий спосіб:

4 –5 0 1 8

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

Елементи списку розташовуються у пам‟яті хаотично. Це надає можливість безперешкодно долучати чи то вилучати яку завгодно кількість елементів. Наприклад, список з тими самими значеннями може бути розташований у пам‟яті в такий спосіб:

first

 

 

0

 

4

 

 

 

 

last

 

 

 

8 0

1

–5

Стрілки на рисунку зазначають порядок проходження елементів. Для того щоб мати можливість працювати з елементами як зі зв‟язною структурою, треба знати, де саме в пам‟яті розташовано кожний з елементів, тобто знати їхні адреси. Як відомо, адреси змінних зберігаються у вказівниках. Виявляється, що немає потреби зберігати одразу всі адреси. Цілком достатньо зберігати лише адресу першого елемента (вказівник на перший елемент first). Кожному елементові треба “знати”, де розташовано елемент, який слідує за ним, і необов‟язково “знати”, де розташовано решту. Тоді до кожного елемента можна буде діставатися послідовно: від першого до другого, від другого до третього і т. д.

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

441

У наведеному прикладі списку елемент зі значенням 1 “знає”, де розташовано елемент зі значенням 8 і не “знає”, де розташовано елемент зі значенням 0 і решта. Щоб “дістатися” елемента зі значенням 1, треба звернутися до першого елемента (4), довідатися в нього адресу другого (–5), у другого

– довідатися адресу третього (0), а вже у третього – довідатися адресу четвертого (1). Безпосередньо одразу до четвертого звернутися неможливо.

Список для зручності й наочності можна зобразити в такий спосіб:

first

 

 

 

 

 

 

 

last

 

4

 

–5

 

0

 

1

 

8

0

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Отже, окрім даних, кожен елемент повинен зберігати ще одне значення: адресу наступного елемента. Природно є подати елемент списку у вигляді структури, яка містить одне чи то кілька інформаційних полів (полів даних) і вказівник на наступний елемент (який на рисунку зображується стрілкою). Після останнього елемента більше елементів немає, це означає, що вказівник на наступний елемент після останнього є порожнім (NULL), інакше кажучи, дорівнює 0. Це можна зобразити в такий спосіб:

first

 

 

 

 

 

 

 

last

 

4

 

–5

 

0

 

1

 

8

NULL

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Порожній

 

адреса

 

адреса

 

адреса

 

адреса

 

 

 

 

 

 

вказівник

 

-5

 

0

 

1

 

8

 

= 0

 

 

 

 

 

 

 

 

 

 

 

У верхній частині кожного елемента зображено його поле даних (число), а в нижній – поле next (адреса наступного елемента в пам‟яті).

Оголошення типу “елемент списку” має такий загальний вигляд:

struct <елемент списку> { <оголошення полів даних>

<елемент списку>* <вказівник на наступний елемент>;

};

Тип елемента наведеного вище списку може бути оголошено у формі struct Element

{ int d; // ціле число

Element *next; // вказівник на наступний елемент

};

Вказівник на перший елемент цього списку оголошується як

Element *first=0;

Його початкове значення завжди дорівнює 0 (тобто списку покищо немає). Після створення списку first набуває певного ненульового значення (адреса пам‟яті, у якій розташовано перший елемент).

Знаючи вказівник на перший елемент, до другого елемента можна звернутися в такий спосіб: first->next, а до третього: first->next->next і т. д. Числове значення першого елемента: first->d.

442

Розділ 13

Зверніть увагу на те, що first є не власне елементом списку, а лише вказівником на нього, тобто є вказівником на структуру. Тому звертання до полів структури відбувається не через крапку, а через стрілочку -> (символи “–” й “>”).

Для початкового створення списку слід створити перший елемент:

1)виділити під нього місце у пам‟яті: first=new Element;

2)занести в нього числове значення: first->d=4;

3)обнулити вказівник на наступний елемент: first->next=NULL;

4)вказівнику на останній елемент last присвоїти адресу створеного тільки що першого елемента, оскільки інших елементів поки немає:

last=first;

Для долучення нового елемента c в кінець списку слід виконати такі дії:

1)виділити місце в пам‟яті під новий елемент: c=new Element;

2)занести до нього поля даних (приміром ціле значення 5): c->d=5;

3)вказівник на наступний елемент є пустий, а, оскільки наступних елементів покищо немає, йому слід присвоїти значення NULL (0):

c->next=NULL;

Схематично це можна зобразити у формі

first

 

 

 

 

 

 

 

last

0

 

0

4

 

-5

 

0

 

1

 

8

5

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

4) створений елемент слід долучити до списку після останнього елемента, на який указує last. Тобто елемент с повинен стати наступним після last. Для цього адресу нового елемента слід записати у поле next останнього елемента:

last->next=c;

 

first

 

 

 

 

 

 

 

 

 

 

 

 

 

 

last

0

 

 

 

0

 

4

 

 

-5

 

 

0

 

 

1

 

 

8

 

 

5

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

5) присвоїти вказівникові на останній елемент last вказівник на новий

елемент c:

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

last=c;

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

first

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

last

 

 

last

0

 

 

 

4

 

 

 

-5

 

 

 

0

 

 

 

1

 

 

 

8

 

 

5

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

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

443

Якщо треба долучити новий елемент перед першим, вище наведені пункти 3 – 5 слід змінити на два інших:

3) новий елемент долучити до списку, тобто в його поле next записати адресу першого елемента:

c->next=first;

 

 

first

 

 

 

 

 

 

 

last

0

5

 

4

 

-5

 

0

 

1

 

8

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

4)вказівникові first присвоїти вказівник на новий елемент: first=c;

first

first

 

 

 

 

 

 

 

last

0

5

 

4

 

-5

 

0

 

1

 

8

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Проходження за списком виконується переважно у циклі while. Допоки не дісталися потрібного елемента чи то допоки список не завершено, переходимо до наступного елемента за його адресою. Для цього використовується допоміжний вказівник с, який оголошується як

Element *c;

Спочатку треба переміститися до першого елемента: c=first;

У циклі, допоки список не завершено, тобто допоки елемент с не дорівнює 0, з черговим елементом списку виконується потрібна дія, після чого здійснюється перехід до наступного елемента списку:

while(c!=0) // Допоки список не завершено, { . . . // виконання дії з c->d

c=c->next; // й перехід до наступного елемента списку.

}

При вилученні першого елемента списку його адреса запам‟ятовується в допоміжному вказівнику с, а вказівник first переміщується на другий елемент. Після цього пам‟ять від елемента с очищується:

с

first

 

 

 

 

 

 

 

last

0

5

 

4

 

–5

 

0

 

1

 

8

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Вставлення й вилучення елементів з середини списку виконується простим присвоюванням адрес і буде розглянуто пізніше разом з прикладами програм. Найбільш простими різновидами списків є СТЕК і ЧЕРГА.

444

Розділ 13

13.2 Стек

Стек – це список, елементи якого можна долучати й вилучати лише з одного кінця. Щоб зрозуміти, що таке стек, слід уявить собі трубку, один з кінців якої запаяно. Будемо закочувати до неї кульки. Всі кульки всередині трубки, окрім останньої, стають неприступними. Щоб вийняти, приміром, третю зверху кульку, треба спочатку вийняти (тобто вилучити зі стека) дві верхніх кульки. Після цього третя кулька стане верхньою і її можна буде витягти. Для стека існує спеціальний термін – FILO (“First In – Last Out”, тобто “першим прийшов – підеш останнім”).

Зобразити стек можна як

root

4

–5

 

0

 

 

 

 

 

 

 

 

 

 

 

 

 

чи як

root

 

 

 

 

 

 

 

 

0

 

 

 

 

 

 

 

 

 

 

 

 

4

 

-5

 

0

 

1

 

8

 

1

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

8

0

Для роботи зі стеком слід зберігати вказівник на вершину стека (root). Кожен елемент повинен зберігати адресу наступного елемента (тобто елемента, який розташований нижче за нього й було долучено до стека раніш за нього).

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

struct Elem

{ int d;

// d – числове поле

Elem *next;

// next – вказівник на наступний елемент

};

 

Початкове оголошення вершини стека:

Elem *root=0;

У стеку на останньому рисунку елемент зі значенням 8 було долучено першим, а вилучено може бути лише після того, як усю решту елементів буде вилучено. Елемент зі значенням 4 було долучено останнім, а вилучено може бути лише першим. Новий елемент до стека може бути долучено лише вище (ліворуч) від 4. При долученні нового елемента до цього стека поле next нового

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

445

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

Так само, як і зі списком, для роботи зі стеком використовується проміжний вказівник с.

З елементами стека можна виконувати такі дії: долучення нового елемента до стека (аналогічно до долучення нового елемента до списку перед першим елементом), вилучення верхнього елемента стека (аналогічно до вилучення першого елемента списку), переглядання елементів стека (аналогічно до переглядання елементів списку).

Приклад 13.1 Створити стек, який міститиме цілі числа, і передбачити такі можливості:

долучення елемента до стека; вилучення верхнього елемента стека; виведення вмісту стека в Memo;

обчислення середнього арифметичного парних значень елементів стека; очищення пам‟яті від стека.

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

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

struct Elem

// Оголошення елемента стека

{ int x; Elem *next;

};

 

Elem *root=0;

// Оголошення вершини стека і початкове обнулення

void add (int d)// Долучення елемента зі значенням d до стека

{// Оголошення й розміщення у пам‟яті допоміжного елемента

Elem *c=new Elem; c->x=d;

c->next=0;

// Попередній елемент покищо не визначено.

c->next=root;

// Новий елемент пов‟язується зі старою вершиною стека

root=c;

// і після цього він стає новою вершиною.

}

// Допоки стек є непорожній (вершина не 0), // вилучення вершини.

446

Розділ 13

void del()

// Вилучення верхнього елемента стека

{ Elem *c=root;

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

root=root->next; // Другий елемент тепер стає першим

 

// (root->next – це звертання до другого елемента).

delete c;

// Звільнення пам‟яті від елемента (тобто вилучення його)

}

 

void print()

// Виведення елементів стека до Memo

{Form1->Memo2->Clear(); Elem *c=root;

while (c!=0)

{ Form1->Memo2->Lines->Add(IntToStr(c->x));

c=c->next; // Перехід до наступного елемента стека

}

}

// Обчислення середнього арифметичного парних значень елементів стека float srednee()

{int s=0, k=0; float sr; Elem *c=root; while(c!=0)

{ if(c->x % 2==0) // Перевірка, якщо значення елемента стека є парне,

{ s+=c->x;

// долучення його до суми

k++;

// і збільшення кількості на 1.

}

 

c=c->next;

// Перехід до наступного елемента

}

if(k!=0) sr=(float)s/k;

return sr; // Повернення середнього арифметичного

}

//Очищення пам‟яті від стека

//(використовується функція вилучення вершини стека) void clean()

{ while(root!=0) del();

}

//Кнопка “Долучити”

void __fastcall TForm1::Button1Click(TObject *Sender) { add(StrToInt(Edit1->Text));

}

// Кнопка “Створити”

void __fastcall TForm1::Button2Click(TObject *Sender)

{int n=Memo1->Lines->Count; for(int i=0; i<n; i++)

add(StrToInt(Memo1->Lines->Strings[i]));

}

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

447

// Кнопка “Вилучити”

void __fastcall TForm1::Button3Click(TObject *Sender)

{del(); print();

}

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

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

}

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

void __fastcall TForm1::Button5Click(TObject *Sender)

{if(root==0){ ShowMessage("Стек є порожній"); return; }

Edit2->Text=FloatToStr(srednee()); while(root != 0 && root->x <= 0) del();

}

// Кнопка “Очистити”

void __fastcall TForm1::Button6Click(TObject *Sender) { while(root!=0) del(); // Очищення пам‟яті

Memo1->Clear(); Memo2->Clear();

// Очищення форм

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

 

}

 

13.3 Черга

Черга – це різновид списку, який має таку особливість: нові елементи можна долучати лише в кінець черги, а вилучати можна лише перший елемент черги. Уявімо собі чергу до каси магазину. Нові покупці стають у кінець черги (після останнього покупця), а розплачується і йде спочатку той, хто стоїть першим. Для черги існує спеціальний термін FIFO (“first in – first out”, тобто “першим прийшов – першим пішов”).

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

Приклад 13.2 Створити чергу, елементи якої містять інформацію про назву міста і його населення. Передбачити такі можливості:

долучення елемента до черги;

вилучення першого елемента черги;

виведення вмісту черги до Memo;

очищення пам‟яті від черги;

вилучення з черги інформації про всі міста до міста з максимальним населенням.

448

Розділ 13

Форма після введення даних матиме вигляд:

Після натискання на кнопку “Розв‟язок” форма матиме вигляд:

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

struct Elem

// Оголошення елемента черги

{ AnsiString gorod;

// Рядок – назва міста

int nas;

// Ціле число – кількість населення міста

Elem *next;

// Вказівник на наступний елемент черги (адреса)

};

 

// Оголошення вказівників на початок і кінець черги та їхнє початкове обнулення

Elem *first=0, *last=0;

void add (AnsiString S, int d) // Долучення елемента до черги

{ Elem *c=new Elem; // Виділення пам‟яті під новий елемент черги

c->gorod=S;

// Занесення до нового елемента назви міста

c->nas=d;

// Занесення до нового елемента кількості населення

c->next=0;

// Після нового елемента інших елементів покищо немає

if(first==0)first=c;// Якщо черги ще нема, то новий елемент стає першим

else

// Інакше елемент долучається після останнього,

last->next=c;

// записуючи в last адресу нового елемента

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