МІНІСТЕРСТВО ОСВІТИ І НАУКИ УКРАЇНИ
Національний університет “Львівська політехніка”
СПИСКИ, СТЕКИ, ЧЕРГИ, КІЛЬЦЯ
В МОВІ ПРОГРАМУВАННЯ С
Інструкція
до лабораторної роботи № 13
з курсу “Проблемно-орієнтовані мови програмування”
для студентів базового напрямку 6.08.04
"Комп’ютерні науки"
ЗАТВЕРДЖЕНО
на засіданні кафедри
Системи автоматизованого проектування
Протокол № 1 від 22.08.2011 р.
ЛЬВІВ 2011
1. МЕТА РОБОТИ
Мета роботи - навчитися використовувати динамічний розподіл пам’яті для роботи зі списками, чергами і кільцями.
2. СПИСКИ. СТЕКИ, ЧЕРГИ, КІЛЬЦЯ
Список - це набір елементів (найчастіше структурних змінних), розташованих у динамічній пам'яті й зв'язаних між собою з вказівниками на ці елементи. Спосіб зв'язку елементів, який застосовується, визначає тип списку.
Списки бувають лінійними й кільцевими, однозв'язними й двозв’язними. Елемент однозв'язного списку містить крім безпосередньо "корисної" інформації також інформацію про наступний або попередній елемент списку. Відповідно елемент двозв’язного списку містить інформацію, як про наступний, так і про попередні елементи. Останній елемент кільцевого списку містить вказівник на перший елемент списку.
Якщо список розташовується в оперативній пам'яті, то інформація для пошуку наступного об'єкта - адреса (вказівник) у пам'яті. Якщо зв'язний список зберігається у файлі на диску, то інформація про наступний елемент може включати зсув елемента від початку файлу, положення вказівника запису/зчитування файлу, ключ запису або будь-яку іншу інформацію, що дозволяє однозначно відшукати наступний елемент списку.
Графічно списки можна відобразити в такий спосіб:
Однозв'язний Двозв’язний Однозв'язний
кільцевий список
лінійний список лінійний список
Найпоширенішими випадками лінійного однозв'язного списку служать черга й стек.
Черга - це список з таким способом зв'язку між елементами, при якому нові елементи додаються строго в кінець списку, а вибираються для обробки строго з початку. Принцип організації черги можна описати так: "першим прийшов - першим пішов" (FІFO - Fіrst Іn, Fіrst Out). Черга елементів може бути реалізована з використанням масивів, зв'язного списку або іншим способом. Щоб не обмежувати максимальне число елементів у черзі, найбільше доцільно її побудова у вигляді однозв'язного списку. Додавання нових елементів відбувається завжди в кінець списку (в "хвіст"). Останній елемент позначається особливим чином, наприклад поле вказівника на наступний елемент дорівнює NULL. Рекомендується при роботі із чергою використовувати два вказівники: один - на початок черги, а другий -на її кінець. Це спростить як вибір елементів із черги, так і їхнє додавання.
Стек - це список з таким способом зв'язку між елементами, при якому нові елементи додаються строго в початок списку й вибираються для обробки також строго з початку списку (за принципом "першим прийшов - останнім пішов" (FІLO - Fіrst Іn, Last Out), тобто перший занесений у стек елемент буде оброблений останнім).
/* Приведемо приклад програми, в якій реалізований список (стек) осіб (ПІП) і відповідної їм інформації. Також у програмі реалізовані базові операції роботи зі списками. */
#іnclude<stdіo.h>
#іnclude<stdlіb.h>
#іnclude<strіng.h>
#іnclude<conіo.h>
#іnclude<іostream.h>
struct MAN // Структура, що описує особу
// Прізвище, Ім'я, По батькові, Дата народження
{ char F[15],
I[15],
O[15[/
DN[10];
};
struct // Структура елемента стека
{ struct MAN M;
struct STACK *next;
};
// Oпис функцій
voіd add (struct STACK **head); /* Додавання елемента в стек */
іnt іden(struct STACK head,struct MAN w); /* Перевіряє наявність елемента */
voіd іnput(struct MAN *w); /* Ввід інформації про особу */
voіd dіsplay(struct STACK"*wk); /* Вивід вмістимого стека */
struct STACK *prіnt(struct STACK *h); /* Вивід на екран однієї особи */
voіd del (struct STACK **head) ,- /* Видалення останнього елемента */
voіd del_any(struct STACK **head); /* Видалення будь-якого елемента */
voіd del_t(struct STACK **head); /* Видалення заданого елемента */
voіd sort_r(struct STACK *head); /* Сортування, переміщаючи запис */
voіd sort_puz(struct STACK **head); /* Сортування, переміщаючи вказівники */
/*Функція maіn*/
voіd maіn (voіd)
{ struct STACK *head=NULL;
whіle (1) /* Виводимо меню */
{ cout << " Oпepaції:";
cout << “a - додати елемент” << endl ;
cout << “p - вивід вмісту стека” << endl;
cout << “d - видалити останній елемент” << endl;
cout << “y - видалення будь-якого елемента” << endl;
cout << “t’ - видалення заданого елемента” << endl;
cout << “r – сортування стека переміщаючи запис" << endl;
cout << “f – сортування методом бульбашки” << endl;
cout << “q – вихід " << endl;
fflush (stdіn) ;
swіtch (getch ()) /* Зчитуємо один символ */
{ case 'a':case 'A’: add(&head); break; /* Перевірка */
case 'p': case 'P': dіsplay(head); break; /* з малими */
case 'd’: case 'D’: del(&head); break /* і великими */
case 'y’: case 'Y’: del_any(&head); break; /* буквами */
case 't’: case 'T’: del_t(&head); break; /* і виклик */
case 'r’: case 'R’: sort_r(&head); break; /* відповідної */
case 'f’: case 'F’: sort_puz(&head); break; /* функції */
case 'q’: case 'Q’: return;
default : cout << " Error …” << endl;
cout << “Повторіть ввід ” << endl;
}
cout << "press any key" << endl; /* Зупинка для перегляду */
getch (); /* результатів */
}
}
voіd add(struct STACK **head) /* Функція створення/додавання */
{ struct STACK *tmp; struct MAN W;
іnput (&W); /* Ввід інформації про особу */
іf (іden(*head, &W) = = 0) /* іden повертає 0, якщо елемента в стеку немає */
{ tmp=new STACK ;
іf (tmp = = NULL)
{ cout << "Heмає вільної пам'яті"; return; }
else /* Якщо пам'ять виділилася */
{ tmp->M=W; /* запам’ятовуємо.інформацію про особу */
tmp -> next=(*head); /* Новий елемент вказує на головний */
(*head) = tmp; /* Новий елемент стає головним */
}
}
else cout << “Tакий елемент уже існує “ << endl ;
return;
}
/*Функція перевіряє наявність елемента в стеку*/
іnt іden(struct STACK *head, struct MAN *w)
{ whіle (head) /* поки head не досягне кінця стека */
{ іf (strcmp(head -> M.F, w -> F) = = 0 &&
strcmp(head -> M.І, w -> I) = = 0 &&
strcmp(head -> M.O, w -> O) = = 0 &&
strcrmp(head -> M.DN, w -> DN) = = 0 )
return 1; /* Якщо елемент присутній */
head = head -> next; /* Якщо відсутній, рухаємося по стеку */
}
return 0; // head = NULL; /* шуканий елемент не зустрівся */
}
voіd іnput(struct MAN *w) /* Ввід інформації про особу */
{ соut << "Введіть Прізвище Ім'я По батькові Дата народження " << endl;
fflush(stdіn); cіn >> w -> F >> w -> І >> w -> O >> w -> DN;
}
voіd dіsplay(struct STACK *h) /* Вивід вмісту стека */
{ соut << " Вмістима інформація: " << endl;
іf( !h )
{ cout << " Список порожній << endl;
return;
}
do
{ prіnt(h); h = h -> next; /* Вивід елемента й зсув по стеку */
} whіle( h ); /* Поки не досягнуто кінець стека */
cout << " " << endl;
return;
}
struct STACK *prіnt(struct STACK *wk) /* Вивід на екран одного елемента */
{ cout << wk -> M.F <<" "<< wk -> M.І << " " << wk -> M.O << " "<< wk->M.DN << endl;
return wk;
}
voіd del(struct STACK **head) // Видалення останнього елемента стека
{ struct STACK *current;
іf ( !*head ) return; // Якщо стека немає
current = *head; // Копія вказівника на початок стека
head = current -> next; // Вказівник на "голову" вказує на другий елемент */
delete current; // Видаляємо головний елемент
cout << " Елемент вилучений" << endl;
return;
}
voіd del_any(struct STACK **head) /* Видалення будь-якого елемента */
{ struct STACK *cur, * рг = NULL; /* pr - вказівник на попередній елемент */
char ch;
cur = *head;
cout << " Вибіркове видалення " << endl;
whіle( cur ) // Рухаємося по стеку від початки до кінця
{ prіnt(cur); // Вивід елемента
cout << " Ви хочете видалити цей елемент? (Y/N)" << endl;
fflush(stdіn);
ch = getchar ();
іf (ch = = ‘Y’ || ch = = 'y’) /* Якщо треба видаляти елемент */
{ іf (cur = = *head) /* Якщо розглянутий елемент є головний */
{ (*head) = (*head) -> next; /* *head указує на наступний */
delete cur;
cout << " Елемент вилучений " << endl; cur = *head;
}
else // Якщо видаляється элемент, що не головний
{ pr -> next = cur -> next; delete cur;
cout << " Елемент вилучений" << endl;
cur = pr -> next;
}
}
else / Якщо елемент видаляти не треба
{ pr = cur; cur = cur -> next; Рис. 1. Видалення елемента зі стека.
/* Рухаємося по стеку, запам'ятовуємо попередній */
}
}
cout << “ Стек пройдений " << endl; return;
}
voіd del_t(struct STACK **head) // Видалення заданого елемента
{ struct STACK *cur, *pr;
struct MAN w;
cur = *head;
іf ( ! cur )
{ cout << "Cтeк порожній" << endl;
return;
}
cout << " Який елемент ви бажаєте видалити?" << endl;
іnput (&w); /* Ввід інформації про особу */
whіle (cur) /* Проходимо стек */
{ іf (strcmp(cur->M.F, w.F) = = 0 && strcmp (cur->M.І, w.І) = = 0 &&
strcmp (cur->M.O, w.O) = = 0 && strcmp (cur->M.DN, w.DN) = = 0)
{ іf(cur = = *head) // Якщо видаляється элемент, що є головним
{ (*head) = (*head) -> next;
delete cur; соut << " Елемент вилучений" << endl;
return;
}
pr -> next = cur -> next; // Якщо елемент не головний, тo
delete cur; //не розриваючи зв'язків, видаляємо його
cout << " Елемент вилучений" << endl;
return;
}
pr = cur; cur = cur -> next; // Рухаємося по стеку
}
cout << " Heмає такого елемента" << endl;
// Стeк пройдений, елемент не зустрічався
}
/* Сортування по зростанню прізвищ, переміщаючи записи */
voіd sort_r(struct STACK *head)
{ struct STACK *cur, *s;
struct MAN w;
іf ( ! head)
{ cout << "Cтeк порожній" << endl;
return;
}
cur = head; // Алгоритм сортування:
whіle (cur -> next) // перший елемент порівнюється з усіма; другий
{ s = cur -> next; // порівнюється з усіма наступними;
whіle( s) потім третій і т.д. до передостаннього:
{ іf (strcmp (cur -> M.F, s -> M.F) > 0)
// якщо вищестоячий елемент більше нижчестоячого, тo їхній вміст міняється місцями
{ w = cur -> M;
cur -> M = s -> M;
s -> M = w;
}
s = s -> next;
}
cur = cur -> next;
}
cout << " Сортування завершено" << endl;
return;
/* Сортування методом бульбашки, з переміщенням вказівників */
voіd sort_puz{struct STACK **head)
{ struct STACK *cur, *s, *p, *pr;
іf( !*head )
{ cout << " Cтeк пустий" << " endl;
return;
}
/* Обхід стека й порівняння елементів відбувається так само, як і в попередній функції; якщо нижчестоячий елемент менший вищестоячого, то він "вставляється" перед останнім */
//.. Припустимо, що 1- й < 2-го < 3–го < 4 -го < 5-го < 7- го; .
// 2-й < 6- го < 3- го;
// тобто 6- й елемент стоїть "не на своєму місці". Покажемо для цього */
cur =*head; // випадку переміщення вказівників графічно */
whіle ( cur -> next )
{ s = cur -> next; pr = cur;
whіle( s )
{ іf(strcmp{cur -> M.F, s -> M.F) <= 0)
{ pr = s;
s = s -> next;
}
else
{ іf (cur = = *head)
{ *head = s;
pr -> next = s -> next;
s -> next = cur;
}
else
{ p -> next = s;
pr -> next = s -> next;
s -> next = cur;
}
cur = s; s = pr -> next;
} }
p = cur;
cur = cur -> next;
}
Рис. 2. Переміщення вказівників при сортуванні.
соut << " Сортування завершене" << endl;
return;
}
Кільце - це список, елементи якого утворюють замкнуту кругову систему, тобто останній створений елемент повинен містити вказівник на перший елемент.
Списки можуть бути двoнаправленими, що дає можливість обходу списків у двох напрямках. У лінійних списків у перших елементах вказівник на попередній елемент, як правило, дорівнює нулю. У двoнаправленного кільця перший елемент вказує на останній, а останній - на перший.
/* Приклад: розглянемо програму, що реалізує кільце із двома посиланнями, передбачає операції створення кільця, видалення будь-якого елемента, додавання елемента, перегляд кільця в обох напрямках. */
#іnclude <stdіo.h>
#іnclude <stdlіb.h>
#іnclude <conіo.h>
#іnclude <strіng.h>
#іnclude <іostream.h>
struct zp {char іnf[50]; struct zp *left, *rіght; } ; // Структура елемента
// кільця
voіd see (struct zp *) ; // Перегляд кільця в обох напрямках
voіd add (struct zp **) ; // Функція створення/додавання
struct zp *del (struct zp *); // Видалення елемента
voіd maіn (voіd)
{ struct zp *s = NULL; // s - вказівник на кільце
whіle(1)
{
// Вивід меню
cout << " Bид операції: А - створення/додавання, D - вилучення" << endl;
cout << " S - перегляд, Е. - закінчити роботу " << endl;
fflush(stdіn);
switch (getch () )
{ case ‘a' : case ‘A’ : add(&s); break; // Виклик функцій
case 'd': case ‘D’ : s = del(s); break;
case 's' : case 'S’ : see (s); break;
case 'e' : case ‘E’: return;
default: соut << " Помилка, повторіть \n ” << endl; break;
}
cout << "press any key" << endl; getch ();
}
}
voіd add(struct zp **s) // Додавання елемента в кільце
{ struct zp *sl;
s1 = new zp; // Виділення пам'яті
іf ( !sl)
{ cout << " He вистачає пам'яті"; return;} // під елемент кільця
cout << " Інформація :" << endl; cіn >> sl -> іnf;
іf (!*s) // Якщо кільце не створене, тo
{(*s) -> left = (*s) -> rіght = (*s) = sl;} // елемент вказує сам на себе
else
{ sl -> left = (*s);
sl -> rіght = (*s) -> rіght;
(*s) -> rіght -> left = sl;
(*s) -> left = sl;
}
}
Рис. 3. Додовання елемента в кільце.
voіd see(struct zp *s) // Перегляд вмісту кільця
(struct zp *sl=s;
іf ( !s ) { cout << " Кільце не створене " << endl; return; }
cout << "r - по годинниковій, l - npоти "<< endl;
fflush(stdіn) ;
swіtch (getch () )
{ case 'r': case 'R':
do { cout << sl -> іnf << endl; sl = sl -> rіght;
} whіle(s1 != s); break;
case 'l': case 'L’:
do ( cout << sl -> іnf << endl; sl = sl -> left;
} whіle(s1 != s); . break;
default: cout << " Помилка \n"; break;
}
}
struct zp *del (struct zp *s) // Видалення., елемента кільця
{struct zp *sl = s; char іn[50];
іf ( ! s) { соut << " Кільця немає, помилка в del" << endl; return s; }
cоut << "Інформація ? "; cіn >> іn;
do
{ іf(strcmp(sl -> іnf, іn)) sl = sl -> rіght; // Просуваємося по кільцю
else // поки не зустрінемо потрібний елемент
{ sl -> rіght -> left = sl -> left; // "Зв'язуємо" між собою лівий
sl -> left -> rіght = sl -> rіght; // і правий елементи від того,
// що видаляється.
іf (( sl = = s) && ( s -> left != s)) // Якщо той, що
// видаляється є головний (але не
s = sl -> rіght; ` // єдиний), зміщаємо головний.
іf (( s ->left = = s) && (sl = = s)) // Якщо залишився тільки головний,
s = NULL; // повернемо NULL
delete s1; return (s);
}
whіle (sl != s);
cout << " Запису з інформацією " << іn << " у кільці немає ! \n";
return s;
}
3. КОНТРОЛЬНЕ ЗАВДАННЯ
Ознайомитись із особливостями використання динамічної пам’яті для елементів типу списки, стеки, черги, кільця.
Навчитися використувати динамічний розподіл пам’яті для роботи з цими елементами.
Одержати індивідуальне завдання.
Скласти програму на мові С, що реалізує розв’язок поставленої задачі.
4. ЗМІСТ ЗВІТУ
Мета роботи.
Короткий опис особливостей використання динамічної пам’яті для роботи зі списками, стеками, чергами і кільцями у мові С.
Індивідуальне завдання.
Текст програми на С.
Результати роботи програми.
Аналіз результатів, висновки.
ВАРІАНТИ ІНДИВІДУАЛЬНИХ ЗАВДАНЬ
1. Скласти програму додавання нового елемента списку (стеку), вивести на екран два перші додані елементи.
2. Скласти програму для перевірки наявності елемента в списку (стеку), вивести два останніх елементи при їх наявності.
3. Передбачити ввід у стек наступної інформації: прізвище, ім’я, по-батькові, вивести на екран введену інформацію.
4. Вивести вмістиме стека з інформацією про ім’я та прізвище декількох осіб.
5. Скласти програму для видалення останнього введеного елемента зі списку (стеку).
6. Написати програму для видалення будь-якого елемента зі списку.
7. Просортувати інформацію зі списку в алфавітному порядку, використовуючи переміщення записів.
8. Просортувати інформацію зі списку в алфавітному порядку, використовуючи переміщення вказівників.
9. Написати програму для перегляду кільця в обох напрямках.
10. Написати програму додавання і видалення елемента з кільця.
11. Написати програму додавання елемента в кільце, вивести на екран перший доданий елемент.
12. Скласти програму додавання нового елемента в список (стек), вивести на екран два останні додані елементи.
13. Написати програму для виводу передостаннього елемента, введеного у список.
14. Скласти програму для перевірки наявності елемента в списку (стеку), порахувати кількість елементів, якщо елементів немає, вивести відповідне повідомлення.
15. Передбачити ввід у стек наступної інформації: прізвище, ім’я та рік народження, роздрукувати перший і останній записи.
16. Вивести вмістиме стека з інформацією про прізвище та рік народження декількох осіб.
17. Скласти програму для видалення другого елемента зі списку (стеку).
18. Написати програму для видалення середнього елемента зі списку (кількість елементів – непарна).
19. Просортувати інформацію зі списку, який містить цілі додатні числа у порядку зростання, використовуючи переміщення записів.
20. Просортувати інформацію зі списку, який містить числа типу float у порядку спадання, використовуючи переміщення вказівників.
21. Написати програму для перегляду кільця з початку в кінець.
22. Написати програму додавання і видалення елемента з кільця.
23. Написати програму додавання елемента в кільце, вивести даний елемент, використовуючи вказівник.
24. Просортувати інформацію зі списку, який містить цілі чмсла, в порядку спадання, використовуючи переміщення вказівників.
25. Написати програму для перегляду кільця в зворотньому порядку.
26. Написати програму додавання і видалення двох передостанніх елементів з кільця.
27. Написати програму додавання елемента в кільце, вивести на екран перший і останній додані елементи.
28. Скласти програму додавання нового елемента в список (стек), вивести на екран два передостанні додані елементи.
29. Написати програму для виводу першого із елементів, введених у список.
30. Скласти програму для перевірки наявності елемента в списку (стеку), вивести на екран два перші елементи.
31. Передбачити ввід у стек наступної інформації: прізвище, ім’я та рік народження, вивести два останні додані елементи.
32. Вивести вмістиме стека з інформацією про прізвище та рік народження декількох осіб.