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

Пособие

.pdf
Скачиваний:
66
Добавлен:
22.03.2015
Размер:
1.35 Mб
Скачать

list *delbeg(list *first)

{

list *n_sp; n_sp=first->next; free(first); return n_sp;

}

3. Включення нового елемента у список.

Рис. 20.4. Схема включення нового елемента у список

list *add(list *pred, elemtype x)

{

list *n_sp;

n_sp = (list *) malloc(sizeof(list)); n_sp->val=x;

n_sp->next=pred->next; pred->next=n_sp; return n_sp;

}

4. Видалення елемента зі списку.

Рис. 20.5. Схема вилучення елемента зі списку

151

elemtype del(list *pred)

{

elemtype x; list *n_sp;

n_sp=pred->next; pred->next=pred->next->next; x=n_sp->val;

free(n_sp); return x;

}

5. Друк значень списку.

void print(list *first)

{

list *n_sp; n_sp=first; while (n_sp)

{

printf("%i\n",n_sp->val); n_sp=n_sp->next;

}

}

6. Перевірка, чи порожній список

int pust(list *first)

{

return !first;

}

7. Знищення списку

list *kill(list *first)

{

while (!pust(first)) first=delbeg(first); return first;

}

В якості прикладу, розглянемо організацію двоспрямованого лінійного список. Для формування списку і роботи з ним потрібно мати принаймні один покажчик - на початку списку.

152

Зручно завести ще один покажчик - на кінець списку. Для простоти припустимо, що список складається з цілих чисел, тобто опис елемента списку виглядає наступним чином:

struct Node

{

int d;

Node *next; Node *prev:

};

Нижче наведена програма, що формує список з 5 чисел, додає число в список, видаляє число зі списку і виводить список на екран. Покажчик на початок списку позначений pbeg, на кінець списку - pend, допоміжні покажчики pv і ркеу.

#include <stdio.h> #include <conio.h> #include <malloc.h> struct Node

{

int d;

Node *next; Node *prev;

};

Node * first(int d);

void add(Node **pend, int d);

Node * find(Node * const pbeg, int i);

bool remove(Node **pbeg, Node **pend, int key);

Node * insert(Node * const pbeg, Node **pend, int key, int d);

int main()

{

Node *pbeg = first(1);

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

Node *pend = pbeg; //Список закінчується

//Додавання в кінець списку //чотирьох елементів

for (int i=2; i<6; i++)

153

add(&pend, i);

//Вставка елемента 200 після елемента 2 insert(pbeg,&pend, 2, 200);

//Видалення елемента 5: if(!remove (&pbeg, &pend, 5))

printf ("Не знайдено"); Node *pv = pbeg;

while (pv)\

{//друк списку на екран printf("%d",pv->d );

pv = pv->next;

}

_getch(); return 0;

}

// Формування першого елемента

Node * first(int d)

{

Node *pv = (Node*) malloc(sizeof(Node)); pv->d = d;

pv->next = 0; pv->prev = 0; return pv;

}

//Додавання в кінець списку void add(Node **pend, int d)

{

Node *pv = (Node*) malloc(sizeof(Node)); pv->d = d;

pv->next = 0; pv->prev = *pend;

(*pend)->next = pv; *pend = pv;

}

// Пошук елемента за ключем

Node * find(Node * const pbeg, int d)

{

Node *pv = pbeg; while (pv)

{

154

if(pv->d == d)break; pv = pv->next;

}

return pv;

}

// Видалення елемента

bool remove(Node **pbeg, Node **pend, int key)

{

if(Node *pkey = find(*pbeg, key)) //1

{

if (pkey == *pbeg) //2

{

*pbeg = (*pbeg)->next; (*pbeg)->prev =0;

}

else

if (pkey == *pend) //3

{

*pend = (*pend)->prev; (*pend)->next =0;

}

else //4

{

(pkey->prev)->next = pkey->next; (pkey->next)->prev = pkey->prev;

}

delete pkey; return true; //5

}

return false; //6

}

// Вставка елемента

Node * insert (Node * const pbeg, Node **pend, int key, int d)

{

if(Node *pkey = find(pbeg, key))

{

Node *pv = (Node*)

155

malloc(sizeof(Node)); pv->d = d;

//1 - встановлення зв’язку //нового вузла з наступним pv->next = pkey->next;

//2 - встановлення зв’язку //нового вузла з попереднім pv->prev = pkey;

//3 - встановлення зв’язку //попереднього вузла з новим pkey->next = pv;

//4 - встановлення зв’язку //наступного вузла з новим if( pkey != *pend)

(pv->next)->prev = pv; //Оновлення вказівника на //кінець списку //якщо вузол вставляється в кінець else *pend = pv;

return pv;

}

return 0;

}

Результат роботи програми:

1 2 200 3 4

Всі параметри, що не змінюються всередині функцій, повинні передаватися з модифікатором const. Покажчики, що можуть змінитися (наприклад, при видаленні зі списку останнього елемента покажчик на кінець списку потрібно скорегувати), передаються за адресою.

Розглянемо докладніше функцію видалення елемента зі списку remove. Її параметрами є покажчики на початок і кінець списку, і ключ елемента, що підлягає видаленню. У рядку 1 виділяється пам’ять під локальний покажчик ркеу, котрому присвоюється результат виконання функції пошуку елемента по ключу fіnd. Ця функція повертає вказівник на елемент у випадку успішного пошуку і 0, якщо елемента з таким ключем у списку немає. Якщо ркеу отримує нульове значення, умова в операторі іf стає істинною (елемент іс-

156

нує), і управління передається оператору 2, якщо ні - виконується повернення з функції зі значенням false (оператор 6).

Видалення зі списку відбувається по-різному в залежності від того, знаходиться елемент на початку списку, в середині або в кінці. В операторі 2 перевіряють, чи знаходиться елемент, що видаляється, на початку списку. У цьому випадку слід скорегувати покажчик pbeg на початок списку так, щоб він вказував на наступний елемент у списку, адреса якого знаходиться в полі next першого елемента. Новий початковий елемент списку повинен мати в своєму полі покажчика на попередній елемент значення 0.

Якщо видаляється елемент, що знаходиться в кінці списку (оператор 3), потрібно змістити покажчик pend кінця списку на попередній елемент, адресу якого можна отримати з поля prev останнього елемента. Крім того, потрібно присвоїти нуль покажчику на наступний елемент нового останнього елемента. Якщо видалення відбувається з середини списку, то єдине, що треба зробити, - забезпечити двосторонній зв’язок попереднього і наступного елементів. Після коригування покажчиків пам’ять з-під елемента звільняється, і функція повертає значення true.

Сортування зв’язного списку полягає в зміні зв’язків між елементами. Алгоритм полягає в тому, що вихідний список проглядається, і кожен елемент вставляється в новий список на місце, що визначається значенням його ключа.

20.3 Стеки Стек - це окремий випадок односпрямованого списку, додавання

елементів в який і вибірка з якого виконуються з одного кінця, що називається вершиною стека. Інші операції зі стеком не визначені. При вибірці елемент виключається зі стека. Кажуть, що стек реалізує принцип обслуговування LIFO (last in - first out, останнім прийшов - першим пішов). Стек найпростіше уявити собі як закриту з одного кінця вузьку трубу, в яку кидають м’ячі. Дістати перший кинутий м’яч можна лише після того, як вийняті всі інші. До речі, сегмент стека названий так саме тому, що пам’ять під локальні змінні виділяється за принципом LIFO. Стеки широко застосовуються в системному програмному забезпеченні, компіляторах, в різних рекурсивних алгоритмах.

Нижче наведена програма, що формує стек з п’яти цілих чисел (1,2, 3, 4, 5) і виводить його на екран. Функція додавання елемента в

157

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

#include <stdio.h> #include <conio.h> #include <malloc.h> struct Node

{

int d; Node *p;

};

Node * first(int d);

void push(Node **top, int d); int pop(Node **top);

int main()

{

Node *top = first(1);

for (int i = 2; i<6; i++) push(&top, i);

while (top)

printf("%d", pop(&top)); _getch();

return 0;

}

//Початкове формування стеку

Node * first(int d)

{

Node *pv = (Node*) malloc(sizeof(Node)); pv->d = d;

pv->p = 0; return pv;

}

//Занесення в стек

void push(Node **top, int d)

{

Node *pv = (Node*) malloc(sizeof(Node)); pv->d = d;

pv->p = *top; *top = pv;

}

158

// Вибірка зі стека int pop (Node **top)

{

int temp = (*top)->d; Node *pv = (*top);

(*top) = (*top)->p; delete pv;

return temp;

}

Результат роботи програми:

5 4 3 2 1

20.4 Черги Черга - це окремий випадок односпрямованого списку, додаван-

ня елементів в який виконується в один кінець, а вибірка - з іншого кінця. Інші операції з чергою не визначені. При вибірці елемент виключається з черги. Кажуть, що черга реалізує принцип обслуговування FIFO (first in - first out, першим прийшов - першим пішов). Чергу найпростіше уявити собі, постоявши в ній годи- ну-другу. У програмуванні черги застосовуються, наприклад, при моделюванні, диспетчеризації задач операційною системою.

Нижче наведена програма, що формує чергу з п’яти цілих чисел і виводить її на екран. Функція додавання елемента в кінець черги називається add, а вибірки - del. Покажчик на початок черги називається pbeg, покажчик на кінець - pend.

#include <stdio.h> #include <conio.h> #include <malloc.h> struct Node

{

int d; Node *p;

};

Node * first(int d);

void add(Node **pend, int d); int del(Node **pbeg);

int main()

{

159

Node *pbeg = first(1); Node *pend = pbeg;

for (int i = 2; i<6; i++) add(&pend, i);

while (pbeg) printf("%d",del(&pbeg));

_getch(); return 0;

}

//Початкове формування черги

Node *first(int d)

{

Node *pv = (Node*) malloc(sizeof(Node)); pv->d = d;

pv->p = 0; return pv;

}

//Додавання в кінець

void add(Node **pend, int d)

{

Node *pv = (Node*) malloc(sizeof(Node)); pv->d = d;

pv->p = 0; (*pend)->p = pv; *pend = pv;

}

// Вибірка

int del(Node **pbeg)

{

int temp = (*pbeg)->d; Node *pv = *pbeg; *pbeg = (*pbeg)->p; delete pv;

return temp;

}

Результат роботи програми:

1 2 3 4 5

160