Міністерство освіти і науки, молоді та спорту України
Національний університет “Львівська політехніка”
Кафедра ЕОМ
Курсова робота ( частина 2 )
На тему:
"Динамічні структури даних"
з дисципліни:
" Програмування"
Вибір індивідуального варіанту:
Вибір АТД: №= [(30) + (1994) + (105) ] % 4 + 1 = 2
Вибір номера завдання: №=[(07) + (1994) + (83) ] % 10 + 1 = 5.
АТД «Черга», Варіант 5.
ЗАВДАННЯ НА КУРСОВУ РОБОТУ
ЗАВДАННЯ 3.
Змоделювати абстрактний тип даних (АТД). Оформити АТД у вигляді класа. Переписати основні операції роботи з АТД і продемонструвати їх застосування при додаванні та вилученні елементів з АТД.
ЗАВДАННЯ 4.
У місті розташовано n автобусних зупинок, позначених числами з N={1,2,....,n}. В текстовому файлі записано k автобусних маршрутів, заданих послідовностями сусідніх зупинок при русі автобуса в одному напрямку:
М1={P11,P12,...,P1m1},
М2={P21,P22,...,P2m2},
.....
Mk={Pk1,Pk2,...,Pkmk}, де Pij натуральне число з N.
Написати програму, що по заданих номерах зупинок I і J визначає найбільш швидкий шлях переміщення пасажира з зупинки I у зупинку J з використанням наявних маршрутів автобусів при умові, що час руху між сусідніми зупинками у всіх маршрутах однаковий і у 3 рази менший за час зміни маршруту. Автобуси можуть рухатись в обох напрямках.
ЗМІСТ
1. ТЕОРЕТИЧНА ЧАСТИНА
2. ЗАВДАННЯ 3. ПОБУДОВА АТД
2.1. Постановка задачі
2.2. Динаміка вмісту
2.3. Результати виконання програми
3. ЗАВДАННЯ 4. ЗАСТОСУВАННЯ АТД
3.1. Постановка задачі
3.2. Алгоритм розв’язання задачі
3.2.1. Словесний опис алгоритму
3.2.2. Граф-схема алгоритму
3.3. Результати виконання програми
ВИСНОВКИ
СПИСОК ЛІТЕРАТУРИ
ДОДАТОК А. ТЕКСТ ПРОГРАМИ ДО ЗАВДАННЯ 3
ДОДАТОК Б. ТЕКСТ ПРОГРАМИ ДО ЗАВДАННЯ 4
1. ТЕОРЕТИЧНА ЧАСТИНА
СТЕК
Стек - динамічна структура даних, що представляє собою впорядкований набір елементів, у якій додавання нових елементів і видалення існуючих відбувається з одного кінця, який називається вершиною стека. Згідно визначення, елементи вилучаються зі стека в порядку, зворотному їхньому додаванню в цю структуру, тобто діє принцип LIFO (Last In, Fist Out – останнім прийшов, першим вийшов).
Найбільш наочним прикладом організації стека служить дитяча пірамідка, в якій додавання й зняття кілець здійснюється саме відповідно до визначення стека. Можна уявити також стопку тарілок. Нижня тарілка із цієї стопки буде використана останньою, а верхня тарілка, яка була покладена в стопку останньою, буде використана першою. Подібне відбувається й в стеку.
Стеки широко використовуються як для розв’язання прикладних задач, так і в системному програмному забезпеченні, включаючи компілятори і інтерпретатори.
Історично склалося так, що дві основні операції для стека - помістити в стек і вилучити зі стека - одержали назву відповідно "заштовхнути" і "виштовхнути". Тому для реалізації стека необхідно створити дві функції: "push" (заштовхнути), що поміщає елемент у вершину стека, і "pop" (виштовхнути), що вилучає з вершини стека один елемент. Необхідно також передбачити певну область у пам'яті, де фактично буде зберігатися стек. Для цього можна використати масив або можна виділити область пам'яті, використовуючи засоби динамічного розподілу пам'яті.
ЧЕРГА
ЧергоюFІFO (Fіrst - Іn - Fіrst- Out - "першим прийшов - першим вийшов") називається така послідовність зі змінною довжиною, у якій додавання елементів виконується тільки з однієї сторони (цю сторону часто називають кінцем або хвостом черги), а вилучення - з іншої сторони (називають початком або головою черги). Ті самі черги до прилавків і до кас, які ми так не любимо, є типовим побутовим прикладом черги FІFO.
Основні операції над чергою - ті ж, що й над стеком - додавання, вилучення, визначення розміру, читання, що не руйнує чергу.
Дек - особливий вид черги. Дек (від англ. deq - doubleendedqueue, тобто черга із двома кінцями) - це така послідовність, у якій як додавання, так і вилучення елементів може здійснюватися з кожного із двох кінців. Окремі випадки дека - дек з обмеженим входом і дек з обмеженим виходом. Логічна й фізична структури дека аналогічні логічній і фізичній структурі кільцевоїFіFO-черги. Однак, стосовно до дека доцільно говорити не про початок і кінець, а про лівий і правий кінці. Динамічна реалізація дека є об'єднанням стека і черги.
Основні операції над деком:
додавання элемента справа;
додавання элемента слева;
вилучення элемента справа;
вилучення элемента слева;
Задачі, що вимагають використання структури дека, зустрічаються в обчислювальній техніці і програмуванні набагато рідше, ніж завдання, реалізовані на структурі стека або черги. Як правило, вся організація дека виконується програмістом без яких-небудь спеціальних засобів системної підтримки.
Прикладом дека може бути, наприклад, якийсь термінал, у який вводяться команди, кожна з яких виконується якийсь час. Якщо ввести наступну команду, не дочекавшись, поки закінчиться виконання попередньої, то вона встане в чергу й почне виконуватися, як тільки звільниться термінал. Це FІFO-черга. Якщо ж додатково ввести операцію скасування останньої введеної команди, то отримаємо дек.
СПИСОК
Лінійний список – це скінчена послідовність однотипних елементів (вузлів), можливо, з повтореннями. Список розташовується в пам'яті довільним чином. Кожний вузол однонаправленого лінійного зв'язаного списку містить вказівник на наступний вузол списку, що дозволяє переміщуватись вперед по списку. Кожний вузол двонаправленого лінійного зв'язаного списку містить вказівники на попередній і наступний вузли списку, що дозволяє переміщуватись по списку вперед та назад.
Вставка і вилучення вузлів у списку реалізовані ефективно: змінюються тільки вказівники. З іншого боку, довільний доступ до вузлів списку підтримується погано: щоб прийти до певного вузла списку, треба відвідати всі попередні вузли. Крім того, на відміну від стеків або черг, додатково витрачається пам'ять під один або два вказівники на кожний елемент списку.
Список росте дуже просто: додавання кожного нового елемента приводить до того, що вказівники на попередній і наступний вузли списку, між якими вставляється новий вузол, міняють свої значення. У новому елементі таким вказівникам присвоюється значення адрес сусідніх елементів.
Список використовує тільки такий об'єм пам'яті, який потрібний для наявної кількості елементів.
2. ЗАВДАННЯ 3. ПОБУДОВА АТД
2.1. Постановка задачі
Змоделювати чергу або дек на базі статичного масиву згідно з завданням. Написати основні операції для роботи з чергою (push, pop, front, empty, full)або деком (push_left, push_right, pop_left, pop_right, front_left, front_right,empty, full) і продемонструвати правильність їх виконання. Для цього (якщо в завданні не вказано інший спосіб) в програмі на вході задати послідовність з К (К>10) різних цілих чисел (числа вводити з клавіатури). Всі додатні числа послідовно заносити в чергу (дек), кожне від’ємне число має вилучати з черги (деку) один елемент (при роботі з деком, парні числа працюють з правим кінцем деку, а непарні – з лівим). Виводити на екран динаміку вмісту черги (деку) під час обробки заданої послідовності. Вхідну послідовність чисел задати такою, щоб вона демонструвала роботу основних операцій та генерувала виникнення ситуацій "втрати значимості черги"(queueunderflow) і "переповнення черги" (queueoverflow) або "втрати значимості деку"(dequnderflow) і "переповнення деку" (deqoverflow).
Змоделювати дек, в якому до опису деку додано дві функції commute_left та commute_right, які заміняють елемент, що знаходиться в кінці деку (правому або лівому відповідно), на заданий елемент. Кожний раз, коли після операції додавання в лівому або в правому кінці деку опиниться число більше за 50, то треба, використовуючи функції commute_left або commute_right, замінити його значення на число 10. Після обробки всієї заданої вхідної послідовності перевірити, чи буде дек “дзеркальним” (тобто перший елемент буде дорівнювати останньому, другий – передостанньому і т.д.).
2.2. Динаміка вмісту
Послідовність K цілих (додатніх, від'ємних, нульових, парних і непарних) чисел
Моя послідовність: 1, -2, -3, 4, 5, 6, 7, 8, 9, 10, 11
Схематичне зображення черги після обробки кожного числа з вхідної послідовності
Введемо такі позначення: P – покажчик початку черги, К – покажчик кінця черги
Порожня черга
Елементи вхідної послідовності
1
-2
К
P
P К
P К
1
queue();
Push_left(1);
Pop_right(-2);
Елементи вхідної послідовності
-3
4
5
P К
P К
P К
4
5
4
Pop_left(-3);
Push_right(4);
Push_left(5);
Елементи вхідної послідовності
6
7
8
P К
P К
P К
5
4
6
7
5
4
6
7
5
4
6
8
Push_right(6);
Push_left(7);
Push_right(8);
Елементи вхідної послідовності
9
10
11
P K
P К
P K
9
7
5
4
6
8
9
7
5
4
6
8
10
Push_left(9);
Push_right(10);
Push_left(11);
Перевірити, чи буде дек “дзеркальним”.
Ні, елементи в деку розміщені не дзеркально.
2.3. Результати виконання програми
/
3. ЗАВДАННЯ 4. ЗАСТОСУВАННЯ АТД
3.1. Постановка задачі
У місті розташовано n автобусних зупинок, позначених числами з N={1,2,....,n}. В текстовому файлі записано k автобусних маршрутів, заданих послідовностями сусідніх зупинок при русі автобуса в одному напрямку:
М1={P11,P12,...,P1m1},
М2={P21,P22,...,P2m2},
.....
Mk={Pk1,Pk2,...,Pkmk}, де Pij натуральне число з N.
Написати програму, що по заданих номерах зупинок I і J визначає найбільш швидкий шлях переміщення пасажира з зупинки I у зупинку J з використанням наявних маршрутів автобусів при умові, що час руху між сусідніми зупинками у всіх маршрутах однаковий і у 3 рази менший за час зміни маршруту. Автобуси можуть рухатись в обох напрямках.
Вказівки до розв’язання задачі: Ідея рішення грунтується на використанні черг. Для кожної зупинки введіть трійку чисел (О,М,Z), де О - номер зупинки, М - номер маршруту, Z - величина затримки. При виборі чергового елемента з черги можливі дві ситуації:
1). Продовжується рух по тому ж маршруту. У цьому випадку в чергу занесіть трійки типу (О',М,Z), де О' - номер зупинки, сусідньої з О, у маршруті М, Z=0.
2). Змінюється маршрут. У цьому випадку в чергу занесіть трійки типу (О,М',Z), де М' - номер зміненого маршруту, Z=3 (затримка на пересадку).
Всі нові трійки породжуються тільки трійками з затримкою, рівною 0. У випадку трійки з ненульовою затримкою занесіть її знову в чергу зі зменшеним на 1 значенням затримки.
3.2. Алгоритм розв’язання задачі
3.2.1. Словесний опис алгоритму
Алгоритм полягає у складенні програми, у консольному режимі програми Visual Studio.
Принцип роботи програми:
1) Для кожної зупинки вводимо трійку чисел (О,М,Z), де О - номер зупинки, М - номер маршруту, Z - величина затримки.
2) Виводиться розклад маршрутів.
3) Виводиться початок маршруту.
4)Після цього відбувається зміна маршруту.
5) Виводиться кінцевий результат.
3.2.2. Граф-схема алгоритму
/
3.3. Результати виконання програми
/
ВИСНОВОК:
Виконуючи дану курсову роботу, я не тільки навчилася будувати ефективні алгоритми вирішення задач, а й і закріпила свої знання про динамічні структури даних, а особливо список, оскільки саме ця структура даних була заданою у завданні до моєї курсової роботи.
СПИСОК ВИКОРИСТАНОЇ ЛІТЕРАТУРИ
ГОСТ 19.701-90 (ИСО 5807-85) ЕСПД. Схемы алгоритмов и программ, данных и систем. Условные обозначения и правила выполнения.
Берзтисс А.Т. Структуры данных . – М.:Статистика, 1974 - 408 с.
А.Ахо, Дж.Хопкрофт, Дж.Ульман, Д.Джеффри. Структуры данных и алгоритмы.; Пер. с англ.– М.:Изд.дом ”Вильямс”, 2001. – 384 с.
Уильям Топп, Уильям Форд. Структуры данных в С++. – М.:Бином, 2000 - 700 с.
ДОДАТОК А. ТЕКСТ ПРОГРАМИ ДО ЗАВДАННЯ 3
main.cpp
_--------------------
#include"deque.h"
int main()
{
deque A(7);
intsize,temp;
cout<<"Vveditrozmirposlidovnosti: "; cin>>size;
for(inti=0;i<size;i++){
cout<<"Vveditchlen: "; cin>>temp;
if(temp>0)
if(temp%2==0)
if(temp>50)
A.commute_right();
else{
cout<<"push_right("<<temp<<")\n";
A.push_right(temp);
}
else
if(temp>50)
A.commute_left();
else{
cout<<"push_left("<<temp<<")\n";
A.push_left(temp);
}
else
if(((temp%2)+2)%2==0){
cout<<"pop_right()\n";
A.pop_right();
}
else{
cout<<"pop_left()\n";
A.pop_left();
}
A.show();
}
if(A.ifdzerkalo())
cout<<"\nElementy u deku rozmisheni dzerkalno.\n";
else
cout<<"\nElementy u dekurozmisheni ne dzerkalno.\n";
return 0;
}
deque.h
#include<iostream>
usingnamespace std;
class deque
{
public:
deque(int _size){
size=_size;
data=newint [size];
head=0; //вказує на початок черги
tail=0; //вказує на перший вільний член після кінця черги
s_temp=0; //кількість елементів черги рівна 0
}
virtual ~deque(){
delete []data;
}
void push_right(int item){
if(s_temp==size){
cout<<"\n\n!!!deque overflow!!!\n\n";
return;
}
data[tail]=item;
tail=(tail+1)%size;
s_temp++;
}
void push_left(int item){
if(s_temp==size){
cout<<"\n\n!!!deque overflow!!!\n\n";
return;
}
head=((head-1)+size)%size;
data[head]=item;
s_temp++;
}
void pop_left(){
if(s_temp==0){
cout<<"\n\n!!!queue underflow!!!\n\n";
return;
}
head=(head+1)%size;
s_temp--;
}
void pop_right(){
if(s_temp==0){
cout<<"\n\n!!!queue underflow!!!\n\n";
return;
}
tail=(((tail-1)%size)+size)%size;
s_temp--;
}
int front_left(){
return data[head];
}
int front_right(){
return data[((tail-1)%size+size)%size];
}
bool empty(){
if(s_temp==0)
returntrue;
else
returnfalse;
}
bool full(){
if(s_temp==size)
returntrue;
else
returnfalse;
}
void show(){
cout<<"Deque: ";
for(int i=head,k=0;k<s_temp;k++,i=(i+1)%size)
cout<<" "<<data[i];
cout<<"\n";
}
bool ifdzerkalo(){
for(int i=head,k=0;k<s_temp;k++,i=(i+1)%size)
if(data[i]!=data[((tail-1)%size+size)%size-i])
returnfalse;
returntrue;
}
void commute_left(){
data[head]=10;
}
void commute_right(){
data[((tail-1)+size)%size]=10;
}
private:
intsize; //розмір динамічного масиву
int *data; //вказівник на динамічний масив
inthead,tail; //голова та хвістчерги
ints_temp; //кількість елементів у черзі
};
ДОДАТОК Б. ТЕКСТ ПРОГРАМИ ДО ЗАВДАННЯ 4
queue.h
#ifndef _QUEUE_H_
#define _QUEUE_H_
#include <iostream>
using namespace std;
template <class T>
class Queue
{
private:
static const int CAPACITY = 50; // розмір черги
T data[CAPACITY]; // місце під чергу
int first; // зберігає індекс початку черги
int last; // зберігає інденс кінця черги
public:
Queue(); // конструктор з замовчуванням
~Queue() { }; // деструктор
int count; // кількість ел. в черзі
bool push(const T & item); // додавання елемента в кінець черги
bool pop(); // видалення елемента з початку черги
bool full() const; // визначає чи черга повна
bool empty() const; // визначає чи пуста черга
T front() const; // повертає значення першого елемента черги
T Last() const {return data[last];}
};
template <class T>
Queue<T>::Queue() // створює пусту чергу
{
count = 0;
last = -1;
first = 0;
}
template <class T>
bool Queue<T>::empty() const // true, якщо черга пуста, false, в іншому випадку
{
return last < first;
}
template <class T>
bool Queue<T>::full() const
{
return last >= CAPACITY - 1;
}
template <class T>
bool Queue<T>::pop()
{
if (empty()) { // якщо черга пуста
cout << "Error! Queue underflow!" << endl;
return false;
}
first++; // в іншому випадку, видаляєм елемент
count--;
return true;
}
template <class T>
bool Queue<T>::push(const T & item)
{
if (full()) { // якщо черга повна
cout << "Error! Queue overflow!" << endl;
return false;
}
count++; // в іншому випадку
data[++last] = item; // додаєм елемент в кінець черги
return true;
}
template <class T>
T Queue<T>::front() const
{
return data[first]; // повертаєм значення 1-го елемента черги
}
#endif
test.cpp
#include <iostream>
#include <fstream>
#include <vector>
#include <string>
#include "queue.h"
using namespace std;
const int n = 10;
struct data
{
int number;
vector<int> station;
};// опис структури зберiгання маршрутiв
struct Queue_Help
{
int O;
int M;
int Z;
}; // опис структури зберiгання iтерацiй поїздки в черзi
typedef data Type; // псевдонiм
bool ReadFromFile(vector<Type> &v, const string &FileName = "input.txt"); // зчитування з файлу маршрутiв
void ShowBus(const vector<Type> &v); // вивiд маршрутiв
int MostPriority(const vector<Type> v, int I, int J); // визначення найбiльш вигiдного шляху для поїздки
bool IsEnd(const Type t, int I, int J); // визначення чи кiнець поїздки
void ShowQueue(Queue<Queue_Help> q)
{
while (!q.empty())
{
cout << "Зупинка №" << q.Last().O << " Маршрут №" << q.Last().M << " Затримка: " << q.Last().Z << endl; // виводим вмiстиме черги
q.pop();
}
cout << endl;
}
void ShowLast(Queue<Queue_Help> q) // вивiд вмiстимого черги
{
//while (!q.empty()) // доки черга не порожня
{
cout << "Зупинка №" << q.Last().O << " Маршрут №" << q.Last().M << " Затримка: " << q.Last().Z << endl; // виводим вмiстиме черги
//q.pop();
}
cout << endl;
}
int main()
{
vector<Type> bus;
setlocale(0, "Ukr");
ReadFromFile(bus); // зчитуєм з файлу iнформацiю про маршрути
Queue<Queue_Help> q; // ств. об"єкт черги
Queue_Help tmp;
cout << "Кiлькiсть зупинок: " << n << endl;
int I, J, i, z = 0; // необхiднi змiннi
cout << "Введiть I, J (вiд 1 до " << n << "): ";
cin >> I >> J; // зчитування зупинок
int Num;
Type temp;
system("cls");
// вивiд iнформацiї
cout << "Кiлькiсть зупинок: " << n << endl;
cout << "Перша зупинка: " << I << endl;
cout << "Остання зупинка: " << J << endl;
cout << "\n Розклад маршрутiв: " << endl;
ShowBus(bus); // вивiд маршрутiв
Num = MostPriority(bus, I, J); // зберiгаєм номер найбiльш пiдходящого маршруту
for (unsigned i = 0; i < bus.size(); ++i)
if (bus[i].number == Num)
temp = bus[i]; // знаходим цей маршрут
cout << "\nПочаток поїздки: \n\n";
while (!IsEnd(temp, I, J)) // доки не кiнець поїздки
{
for (i = I; i < temp.station[temp.station.size()-1] ; ++i) // їдем до кiнцевої зупинки даним маршрутом
{
tmp.O = i;
tmp.M = temp.number;
tmp.Z = z;
q.push(tmp);
ShowLast(q); // вивiд останнього ел. якщо потрiбно виводити всю чергу, видалити даний рядок, i розкоментувати наступний
// ShowQueue(q);
}
I = temp.station[temp.station.size()-1]; // змiщаєм зупинку
if (!IsEnd(temp, I, J)) // якшо не кiнець поїздки
{
// потрiбно змiнити маршрут
cout << "Пiсля цього вiдбудеться змiна маршруту!\n";
Num = MostPriority(bus, I, J); // знаходим новий номер маршруту
for (unsigned i = 0; i < bus.size(); ++i)
if (bus[i].number == Num)
temp = bus[i]; // знаходим цей маршрут
z = 3; // встановлюєм затримку
}
while (z != 0) // якщо є затримка
{
// виконуєм наступну умову
// Всi новi трiйки породжуються тiльки трiйками з затримкою, рiвною 0.
// У випадку трiйки з ненульовою затримкою занесiть її знову в чергу зi зменшеним на 1 значенням затримки.
tmp.O = i;
tmp.M = temp.number;
tmp.Z = z;
z--; // зменшуєм затримку
q.push(tmp); // додаєм в чергу
ShowLast(q); // вивiд останнього ел. якщо потрiбно виводити всю чергу, видалити даний рядок, i розкоментувати наступний
// ShowQueue(q); // виводим в чергу
}
}
// якщо ми тут, то маршрут, на якому ми їдемо, їде до нашої зупинки
while (I <= J) // доки не наша зупигка
{
tmp.O = I; // встановлюєм номер зупинки
tmp.M = temp.number; // встановлюєм номер маршруту
tmp.Z = 0; // встановлюєм затримку
I++; // переходим на наступну зупинку
q.push(tmp); // додаєм в чергу
ShowLast(q); // вивiд останнього ел. якщо потрiбно виводити всю чергу, видалити даний рядок, i розкоментувати наступний
// ShowQueue(q); // виводим чергу
}
system("pause");
return 0;
}
int MostPriority(const vector<Type> v, int I, int J) // визначення найбiльш вигiдного шляху для поїздки
{
vector<Type> t;
e: bool is = false;
for (unsigned i = 0; i < v.size(); ++i)
{
is = false;
for (unsigned j = 0; j < v[i].station.size(); ++j)
{
if (!is && v[i].station[j] == I)
{
t.push_back(v[i]);
is = true;
}
if (is && v[i].station[j] == J)
return v[i].number;
}
}
if (I != J)
{
J--;
goto e;
}
}
bool IsEnd(const Type t, int I, int J) // визначення чи кiнець поїздки
{
bool is = false;
for (unsigned i = 0; i < t.station.size(); ++i)
{
if (t.station[i] == I)
is = true;
if (is && t.station[i] == J)
return true;
}
return false;
}
bool ReadFromFile(vector<Type> &v, const string &FileName /* = "input.txt" */) // зчитування з файлу маршрутiв
{
ifstream fin;
int t = 0;
int i = 1;
bool is = false;
Type k;
fin.open(FileName.c_str(), ios_base::in);
if (!fin)
{
cout << "Can't open file \"" << FileName << "\" for reading." << endl;
system("pause");
abort();
}
while (fin)
{
fin >> t;
if (t == 0)
{
fin >> t;
k.number = t;
v.push_back(k);
while (k.station.size() != 0)
k.station.pop_back();
}
else
k.station.push_back(t);
}
return true;
}
void ShowBus(const vector<Type> &v) // вивiд маршрутiв
{
for (unsigned i = 0; i < v.size(); ++i)
{
cout << "Маршрут №" << v[i].number << ": Зупинки: ";
for (unsigned j = 0; j < v[i].station.size(); ++j)
{
cout << v[i].station[j];
if (j != v[i].station.size()-1)
cout << ", ";
}
cout << endl;
}
return;
}