Feladat
5. Készítsen egy sor típust! Alkalmazzon osztályt! A sorokat kétirányú fejelemes ciklikus láncolt
listával ábrázolja! Implementálja a szokásos műveleteket, és egy összefűző műveletet is, ami az első sorhoz sorban hozzáfűzi a második sor elemeit! (A második sor végül legyen üres!) Egészítse ki az osztályt a kényelmes és biztonságos használat érdekében megfelelő metódusokkal (sorbeolvasó operátor>>, sorkiíró operátor<<), alkalmazzon kivételkezelést, és bontsa modulokra a
programját! A teszt környezet hívja meg azt a műveletet is, amely kiszámítja két sor összefűzését! Az összefűzés és a többi operáció műveletigénye is Ơ(1), kivéve a sorbeolvasó és sorkiíró
operátorokat.
Sor típus
Típusérték-halmaz
- elemek sorozata
- végére tudunk csak betenni
- elejéről tudunk csak kivenni, illetve lekérdezni
Típus-műveletek
(Q,E)
Empty: → Q Üres sor létrehozása IsEmpty: Q → L Lekérdezés, üres-e a sor
In: Q x E → Q Sorba új elem berakása (utolsó helyre) Out: Q → Q x E Sorból elem kivétele (első)
First: Q → E Sor első elemének lekérdezése
Link: Q x Q → Q Kér sor összefűzése (a második sor törlésével)
Reprezentáció
Láncolt adatszerkezettel.
Helyett:
Kétirányú, fejelemes ciklikus lista
Implementáció
Kétirányú, fejelemes ciklikus listán könnyedén elérhető mind az első, mind pedig az utolsó elem, így az implementálás közben igen könnyű a műveletek kezelése, nincs szükség többnyire segédpointerek használatára.
Fejelem: Item* Head
Az utolsó elem: Head->prev Első elem: Head->next
Üres lista esetén csak a fejelem van meg: Head->prev=Head->next = NULL
Elem hozzávétele: A sor végére beláncolunk egy új elemet, melynek pointereit az eddigi utolsó elemmel illetve a fejelemmel kölcsönös összeköttetésbe hozzuk.
Elem kivétele: Az első elemre pointert állítunk, majd ezt az elemet megkerülve folytonossá
alakítjuk a listát, végül a segédpointerrel visszaadjuk az értékét majd töröljük a felesleges elemet a memóriából.
Összefűzés: Ki kell küszöbölni a második sor fejelemét, és össze kell kötni az utána következő valós elemmel a sort, mintha csak egy új elemet tennék hozzá, valamint a második sor végét kölcsönös összeköttetésbe kell hozni a sor elejével.
C++ megvalósítás
Osztály
A sorok típusát egy osztály segítségével valósítjuk meg.
class Queue { … };
Melyhez felhasználásra kerül az elemek struktúrája:
struct Item { ... };
#ifndef ITEM_H_INCLUDED
#define ITEM_H_INCLUDED struct Item
{
Item* prev;
char data;
Item* next;
Item(Item* p, char d, Item* n ) {
prev = p;
data = d;
next = n;
} };
#endif
#ifndef Queue_H_INCLUDED
#define Queue_H_INCLUDED
#include <iostream>
#include "Item.h"
class Queue {
public:
Queue();
~Queue();
Queue(const Queue& s);
bool IsEmpty();
char First();
void In(char x);
char Out();
Queue& operator= (const Queue& s);
void Link(Queue &s);
friend std::ostream& operator<<(std::ostream& out, const Queue& s);
friend std::istream& operator>>(std::istream& in, Queue& s);
enum Exceptions{EMPTY_QUEUE, INPUT_END};
private:
Item* Head;
void copy(const Queue& s);
void deallocate();
};
std::ostream& operator<<(std::ostream& out, const Queue& s);
std::istream& operator>>(std::istream& in, Queue& s);
#endif
A metódusok megvalósítása a Queue.cpp forrásállományba kerül. Ennek elején helyezzük el a #include
”Queue.h” direktívát.
1. Konstruktor
Tevékenység: A konstruktor létrehoz egy üres sort, amely csak egy '0'-t tartalmazó fejelemet tárol.
Bemenő adatok: paraméter nélkül Kimenő adatok: új sor
Definíció:
Queue::Queue() {
Head = new Item(0,'0',0);
}
2. Másoló konstruktor
Tevékenység: A konstruktor létrehoz egy új sort, melynek elemeit rendre beállítja egy másik sor megfelelő elemeinek értékére.
Bemenő adatok: sor Kimenő adatok: új sor Definíció:
void Queue::copy(const Queue& s) {
Head = new Item(0,'0',0);
Item* r = Head;
for (Item *p = s.Head->next; p != Head; p = p->next) {
r->next = new Item(r,p->data,Head);
r = r->next;
}
Head->prev = r;
}
Queue::Queue(const Queue& s) {
copy(s);
}
3. Értékadás operátor
Tevékenység: Az operátor értékül adja az értékadás jobboldalán adott sort a baloldalán álló (az operátor által alapértelmezett) sornak.
Bemenő adatok: sor
Kimenő adatok: alapértelmezett sor Definíció:
void Queue::deallocate() {
if ( !IsEmpty() ) {
Item* a;
Item* n = Head->next;
while(n != Head)
{ a=n;
n=a->next;
delete a;
}
Head->next=Head->prev=0;
} }
Queue& Queue::operator= (const Queue& s) {
if (this != &s) {
deallocate();
copy(s);
}
return *this;
}
4. Destruktor
Tevékenység: Megsemmisíti a létrehozott láncolt listát, felszabadítva ezzel a helyet.
Bemenő adatok: sor Kimenő adatok: - Definíció:
Queue::~Queue() {
deallocate();
delete Head;
}
5. IsEmpty
Tevékenység: Megállapítja egy sorról, hogy üres-e.
Bemenő adatok: sor
Kimenő adatok: logikai érték Definíció:
bool Queue::IsEmpty() {
return (Head->next == 0);
} 6. First
Tevékenység: A sor első elemének lekérdezése.
Bemenő adatok: sor
Kimenő adatok: első elem értéke Definíció:
char Queue::First() {
if (IsEmpty()) throw EMPTY_QUEUE;
return Head->next->data;
}
7. In
Tevékenység: Egy elem a sorhoz csatolása, a sor végére.
Bemenő adatok: sor
Kimenő adatok: sor az új elemmel Definíció:
void Queue::In(char x) {
if ( !IsEmpty() ) {
Item* e=new Item(Head->prev, x, Head);
Head->prev->next=e;
Head->prev=e;
} else {
Item* e=new Item(Head, x, Head);
Head->prev=e;
Head->next=e;
} } 8. Out
Tevékenység: Az első elem kivétele a sorból és visszatérési értékként való átadása.
Bemenő adatok: sor
Kimenő adatok: sor (első elem nélkül) + első elem értéke Definíció:
char Queue::Out() {
if (Head->next==0) throw EMPTY_QUEUE;
Item* s;
s=Head->next;
if (s->next==Head) Head->next=0;
else Head->next=s->next;
if (s->next==Head) Head->prev=0;
else (s->next)->prev=Head;
char c;
c=s->data;
delete s;
return c;
} 9. Link
Tevékenység: Két sor összecsatlakoztatása, elemeik összefűzése, a második sor megsemmisítésével.
Bemenő adatok: sor Kimenő adatok: sor Definíció:
void Queue::Link(Queue &s) {
Head->prev->next=s.Head->next;
s.Head->next->prev=Head->prev;
s.Head->prev->next=Head;
Head->prev=s.Head->prev;
s.Head->prev = s.Head->next = 0;
}
10. Kiíró operátor
Tevékenység: Egy sor kiírása.
Bemenő adatok: sor Kimenő adatok: ostream Definíció:
std::ostream& operator<<(std::ostream& out,const Queue& s) {
if ( s.Head->next==0 ) throw Queue::EMPTY_QUEUE;
for (Item *p = s.Head->next; p != s.Head; p = p->next) {
out << p->data;
}
return out;
}
11. Beolvasó operátor
Tevékenység: Sorba való beolvasás.
Bemenő adatok: istream Kimenő adatok: sor Definíció:
std::istream& operator>>(std::istream& in, Queue& s) {
char x='0';
while (in) {
in >> x;
if (x=='q') throw Queue::INPUT_END;
s.In(x);
}
return in;
}
Tesztkörnyezet
#include <iostream>
#include "Queue.h"
using namespace std;
int main() {
Queue Sor;
cout << Sor.IsEmpty() << endl;
Sor.In('a');
cout << Sor.IsEmpty() << endl;
cout << Sor.Out() << endl;
cout << Sor.IsEmpty() << endl;;
Queue Sor2;
try {
Sor.Out();
}
catch(Queue::Exceptions i) {
if (i==Queue::EMPTY_QUEUE) cout << "Ures sor!" << endl;
} try {
cin >> Sor;
}
catch(Queue::Exceptions i) {
if (i==Queue::INPUT_END);
}
cout << Sor << endl << endl;
try {
cin >> Sor2;
}
catch(Queue::Exceptions i) {
if (i==Queue::INPUT_END);
}
cout << Sor2 << endl << endl;
Sor.Link(Sor2);
cout << Sor << endl;
try {
cout << Sor2;
}
catch(Queue::Exceptions i) {
if (i==Queue::EMPTY_QUEUE) cout << "Ures sor!" << endl;
}
return 0;
}