Список дозволяє вставити новий елемент поміж якими завгодно елементами. Вставлення перед першим і після останнього елементів було розглянуто у підрозд. 13.1.
Для вставлення нового елемента с до списку після елемента, на який ука-
зує вказівник с1, слід виконати такі дії:
1)виділити місце в пам‟яті під новий елемент с і записати до нього дані;
2)пов‟язати новий елемент з тим, який іде після с1 (на нього вказує вказівник с1->next), для чого до поля next елемента с записати адресу елемента
с1->next:
с->next = с1->next;
3) пов‟язати новий елемент с з с1, для чого до поля next елемента с1 записати адресу елемента с:
с1->next = с;
4)якщо елемент с1 був останнім, то тепер останнім буде елемент с: if(с1==last) last = с;
На рисунку дії 2 та 3 позначені відповідними цифрами:
с1
с1->next
1
-3
0
4
c
3
2
5
Динамічні структури даних
451
Якщо у програмі описано функцію вставлення нового елемента після зазначеного, її можна використовувати при створенні списку, коли новий елемент вставляється після останнього.
При вилученні елемента с зі списку треба спочатку знайти вказівник на попередній елемент с1. Після цього слід виконати такі дії:
1) пов‟язати елемент с1 з тим, який розташований після с (на нього вказує вказівник с->next). Для цього до поля next елемента с1 записати адресу елемента с->next:
с1->next = с->next;
2)якщо елемент с був останнім, тепер останнім стає с1: last = с1;
3) звільнити пам‟ять від елемента с: delete с;
На рисунку дії 1 та 3 позначені відповідними цифрами:
с1
c
с->next
1
-3
0
4
3
1
Приклад 13.3 Увести цілі числа до Memo і створити з цих чисел список. Передбачити такі можливості:
вставлення нового елемента зі значенням 55 перед першим елементом
здодатним значенням;
вставлення нового елемента зі значенням 100 після першого елемента
зпарним значенням;
вилучення першого елемента зі значенням 100;
вилучення елемента перед першим елементом зі значенням 99;
вилучення елемента після першого елемента зі значенням 100.
Форма додатка з результатами роботи матиме вигляд
452
Розділ 13
Текст програми:
struct Element//Оголошення елемента списку
{ int d; Element *next; };
//Оголошення вказівників на перший та останній елементи (глобальні змінні)
Element *first=0, *last=0;
//Функція створення першого елемента списку
//Параметр – число, яке буде записано до нового елемента
void fir(int x)
{first =new Element; first->d=x; first->next=0;
}
//Функція виведення списку до Memo
//Параметр memo – компонент Memo, до якого виводитиметься список void print_list(TMemo *memo)
Приклад 13.4 Заповнити список цілими числами з Memo і відсортувати список за зростанням.
Розв‟язок. У підґрунті кожного сортування лежить переставляння значень елементів. Якщо розмір елементів та їхня кількість є надто великі, обмін значеннями займає багато часу. Тому ефективніше при сортуванні не обмінювати значення елементів, а змінювати їхній порядок у списку, тобто змінювати адреси наступних елементів у полі next.
с
с1
3
c2
с2->next
5
8
6
19
2
1
с
с2
c1
5
6
8
19
Форма додатка з результатами роботи матиме вигляд
456
Розділ 13
Оскільки більшість операцій зі списком було розглянуто у прикладі 13.3, наведемо текст лише кнопки “Сортувати”:
Текст програми:
void __fastcall TForm1::Button2Click(TObject *Sender) { Element *c, *c1, *c2;
bool pr; // Ознака, чи відсортовано масив do
{pr=true;
c=first; c1=first->next;
if (c->d > c1->d)//Якщо перший і другий елементи йдуть неправильно,
{ first=c1;
// другий елемент стає першим,
c->next=c1->next;
// той, що був першим, зв‟язується з третім,
first->next=c;
// а той, що був першим, стає другим
pr=false;
// Ознака: зміни відбулися, масив не відсортовано
}
// Запам‟ятовування трійки елементів, які йдуть один за одним. c=first; c1=first->next;c2=c1->next;
while(c2!=0)
// Допоки останній у трійці не 0,
{ if(c1->d > c2->d)
// якщо елементи с1 та с2 йдуть неправильно,
{c1->next=c2->next;
// переставляння елементів
c->next=c2;
// згідно з наведеним рисунком
c2->next=c1;
c=c2; c2=c1->next;
// Присвоювання для наступного кроку
pr=false;// Ознака: зміни відбулися, масив не відсортовано
}
else
{c=c1;
c1=c2; c2=c2->next;
}
// Присвоювання для наступного кроку
}
}while (!pr); // Допоки жодної зміни не відбудеться (масив відсортовано) print_list(Memo3);
}
Приклад 13.5 Увести числа до Memo і створити відсортований за зростанням список.
Розв‟язок. Використовуватимемо алгоритм сортування вставленням. На кожному кроці припускається, що існуючий список вже відсортований і треба вставити новий елемент до списку в такий спосіб, щоб не порушити порядок. Для цього будемо йти за списком і зупинимось, коли попередній елемент буде менш за нове число, а поточний не перевищуватиме (це означає, що треба вставити новий елемент поміж ними).
Основні функції для вставлення елемента до списку було розглянуто у прикладі 13.3. Тому наведемо лише текст кнопки “Створити список”.
Динамічні структури даних
457
Форма додатка з результатами роботи матиме вигляд
Текст програми:
void __fastcall TForm1::Button1Click(TObject *Sender) { first=0; last=0; int i, n=Memo1->Lines->Count;
Приклад 13.6 Заповнити StringGrid цілими випадковими числами. Створити список з чисел, модуль яких є менше за 5. Обчислити середнє арифметичне додатних елементів списку. Вилучити елементи, які є менше за –1.
Розв‟язок. У цьому прикладі оголосимо вказівники на перший та останній елементи локально. Оскільки в такому разі ці вказівники невідомі багатьом функціям, доведеться передавати вказівники на початок і кінець списку як параметри.
У багатьох програмах зі списками доводиться виконувати одні й ті ж самі дії (тобто писати аналогічні функції). Створимо заголовний файл з прототипів згаданих функцій. Файл Unit2.h міститиме оголошення типу Element “елемент списку” і прототипи таких функцій: створення першого елемента fir(), долучення нового елемента у кінець списку add(), вилучення елемента після зазначеного del_el(), вставлення елемента після зазначеного vstavka(), очищення пам‟яті, яку було виділено під елементи списку ochistka().
Оскільки всі зазначені функції розміщено у файлі Unit2.h, змінні first та last, які оголошено в Unit1.cpp, у них є невідомі. Тому вказівники на початок і кінець списку мають бути параметрами функцій.
Функція долучення елемента в кінець списку має тип void. Параметри функції: вказівник на перший елемент, вказівник на вказівник на останній елемент (адреса вказівника на останній елемент) і число, яке буде записано до нового елемента. Перший та останній параметри функція використовуватиме, але не змінюватиме, а ось вказівник на останній елемент списку при долученні нового елемента в кінець неодмінно зміниться (останнім тепер стане новий елемент). Тому функція повинна мати доступ для змінювання цього вказівника. Для цього його має бути передано до функції за адресою. Вказівник на останній елемент має тип Element*. Отже, адреса вказівника на останній елемент має тип вказівник на Element* , тобто Element **.
Оскільки las – це вказівник на вказівник на останній елемент, то слід користуватися операцією розадресації *las для звертання до вказівника на останній елемент.
Параметри функції вилучення елемента зі списку – вказівник с1 на елемент, який передує тому, що буде видалено, і вказівник на останній елемент списку las, який може бути змінено. Аналогічно до функції долучення, треба писати перед las дві “зірочки”, щоб забезпечити доступ для змінювання вказівника на останній елемент списку.
Файл Unit2.h:
struct Element
{ int d; Element *next;}; Element* fir(int x);
void add(Element* fir, Element** las, int x); void del_el(Element* c1, Element ** las);
void vstavka(Element* c1, Element** las, int x); void ochistka(Element *fir);