Feladat Készítsen egy egész számokat tartalmazó zsák típust! A zsákot dinamikusan lefoglalt tömb segítségével ábrázolja! Implementálja a szokásos műveleteket (elem betevése, kivétele, üres-e a halmaz, egy elem hányszor van a zsákban), valamint két zsák különbségét (a közös elemek csak akkor maradnak meg, ha a kivonandó zsákbeli előfordulási számuk a nagyobb, és ekkor az előfordulási számuk különbségével), továbbá egy zsák kiírását, és végül a másoló konstruktort és az értékadás operátort! Törekedjen a különbségképzés műveletigényének minimalizálására, a dokumentációban mutasson rá a saját megoldásának műveletigényére! Zsák típus A feladat lényege egy felhasználói típusnak a zsák típusnak a megvalósítása. Típusérték-halmaz Olyan számokat (ebben az esetben egész számokat: Z) tartalmazó zsákkal akarunk dolgozni, amelyekben az különböző értékű elemek számát tartjuk nyilván. Az elemek sorrendje lényegtelen. E zsákoknak mind a mérete (nεn) mind az aktuális kapacitása (nεn) lényeges paraméter a megvalósítás szempontjából, de lényegtelen a felhasználó számára. Formálisan: Elem = (Érték: Z, Darab: N) Bag = b=érték meret méret ε N kapacitás ε N méret kapacitás i,jε [1..méret]: b[i].érték b[j].érték A kapacitás változóban a dinamikus tömbünk aktuális méretét tároljuk, a mértet változóban pedig azt, hogy meddig van feltöltve.
Típus-műveletek 1.Érték betétele Adott érték betétele a zsákba. Ha van már ilyen értékű elem a zsákban, akkor az elem darabszámának növelése egyel egyébként, ha a tömb nincs tele, elem beszúrása az utolsó elem után. Méret növelése eggyel. Ha a tömb tele van, akkor előtte megduplázzuk a kapacitását. Ezt úgy tesszük, hogy létre hozunk egy dupla akkora kapacitású tömböt, egyenként átmásoljuk az elemeket, majd töröljük az eredeti tömböt. 2.Érték kivétele Adott érték kivétele a zsákból. Ha az érték több mint egyszer szerepel a zsákban, akkor csökkentjük eggyel az elem darabszámát. Ha egyszer szerel, akkor felülírjuk az utolsó elemmel és csökkentjük egyel a méretet. Ha nem szerepel, akkor változatlanul hagyjuk a tömböt. 3.Üres-e a tömb Ha méret = 0 akkor igazzal térünk vissza, egyébként hamissal. 4.Érték megszámlálása A zsákban adott érték előfordulásainak megszámlálása. Ha van az adott értékből a zsákban, visszaadjuk a darabszámát, egyébként nullát adunk vissza. 5.Külömbség Egy zsákból egy másik zsák kivonása. A közös elemek csak akkor maradnak meg, ha a kivonandó zsákbeli előfordulási számuk a nagyobb, és ekkor az előfordulási számuk különbségével.
Implementáció 1.Érték betétele l := hamis i := 0 to méret-1 \ tömb*i+.érték = beszúrandóérték / tömb*i+.darabszám ++, l := igaz SKIP \ -l / \ méret = kapacitás / p := tömb, kapacitás := kapacitás*2 tömb := new *kapacitás+ i := 0 to méret-1 SKIP tömb*i+ = p*i+ SKIP delete [] p tömb*méret+.érték = beszúrandóérték, tömb*méret+.darabszám = 1 méret++ 2.Érték kivétele i := 0 to méret-1 \ tömb*i+.érték = törlendőérték / tömb*i+.darabszám -- \ tömb*i+.darabszám = 0 / SKIP tömb*i+ = tömb*méret-1+ SKIP méret -- 3.Üres-e a tömb return méret = 0 4.Érték megszámlálása i := 0 to méret-1 \ tömb*i+.érték = megszámlálandóérték / return tömb*i+.darabszám SKIP return 0
5.Külömbség i := a.méret-1 to 0 j := 0 to b.méret-1 \ a.tömb*i+.érték = b.tömb*j+.érték / dif := a.tömb*i+.darabszám - b.tömb*i+.darabszám \ dif <= 0 / a.tömb*i+ := a.tömb*méret-1+ a.méret -- a.tömb*i+.darabszám := dif SKIP break return a A különbség műveletigénye Legjobb esetben a zsák és b zsák diszjunkt az értékekre nézve, így a ciklusmagból csak az első összehasonlítás hajtódik végre. Ekkor a műveletigény a.méret*b.méret értékadás. Legrosszabb esetben b szák értékei részhalmaza a szák értékeinek. Ekkor a belső két értékadás és még egy összehasonlítás is végrehajtódik. Ekkor a műveletigény a.méret*b.méret*2értékadás és ugyanennyi összehasonlítás. A műveletigény csökkenthető,ha az értékeket rendezetten tárolnánk. Ekkor viszont a folyamatos rendezés miat növekedne a műveletek műveletigénye. Ez is csökkenhető,ha az értékeket nem tömbben, hanem keresőfában tárolnánk.
Osztály A zsákok típus egy osztály segítségével van megvalósítva. Elelm - value : int - number : int - elemek : Elem - meret : int - kapacitas : int + insert(int) : void + erase(int) : void + isempty() : bool + count(int) : int +dist(bag, Bag) : Bag + read() : void +write() : void Bag A teljes osztály-definíciót a Bag.h fejállományban van elhajezve.
Tesztelési terv 1) Értékek betétele a) 10,0,-1 2) Érték kivétele a) olyan érték, ami többször van bent b) olyan érték, ami egyszer van bent c) olyan érték, ami nincs bent d) kivétel üres zsákból 3) Üres-e a) lekérdezés üres zsákra b) lekérdezés nem üres zsákra c) lekérdezés újonnan létrehozott zsákra 4) Érték megszámlálása a) olyan érték, ami többször van bent b) olyan érték, ami egyszer van bent c) olyan érték, ami volt bent, de már nincs d) üres zsákban érték megszámlálása 5) A c := a b különbség kipróbálása. a) Eltérő méretű zsákokkal (az a és b mérete különbözik, a c és a mérete különbözik) b) ugyanaz az érték többször van a-ban min b-ben c) ugyanaz az érték ugyanannyiszor van a-ban min b-ben d) ugyanaz az érték kevesebbszer van a-ban min b-ben e) üres zsákok különbsége 6) Zsák létrehozása meglévő alapján a) Zsák létrehozása, majd az új és a régi megváltoztatása, mindkettő kiírása. 7) Értékadás kipróbálása (különböző méretű és kapacitású zsákokra is). a) a = a b) a = b = c (azonos méretű és kapacitású zsákokra) c) a = b (azonos és eltérő méretű és kapacitású zsákokra)
Melléklet: C++ kód #ifndef BAG_H_INCLUDED #define BAG_H_INCLUDED #include <iostream> class Bag public: Bag(); virtual ~Bag(); Bag(const Bag& a); Bag& operator=(const Bag& a); void insert(int value); void erase(int value); bool isempty(); int count(int value); friend Bag operator- (Bag a, const Bag& b); friend std::istream& operator>>(std::istream& s, Bag& a); friend std::ostream& operator<<(std::ostream& s, const Bag& a); private: class Elem public: int value, number; ; Elem *elemek; int kapacitas, meret; ; #endif // BAG_H_INCLUDED A metódusok megvalósítása a Bag.cpp forrásállományba kerül. Ennek elején helyezzük el a #include Bag.h, #include <iostream>, #include <stdlib.h> direktívákat, valamint a using namespace std; utasítást. 1. Konstruktor Tevékenység: A konstruktor létrehoz egy 0 méretű zsákot, azaz lefoglalja annak elemeit tartalmazó 1 hosszú tömböt. Bemenő adatok: - Kimenő adatok: új zsák (Bag) Definíció: Bag::Bag() meret=0; kapacitas=1; elemek= new Elem[kapacitas];
2. Destruktor Tevékenység: A destruktor felszabadítja az elemeket tartalmazó tömböt a zsák megszűnésekor. Bemenő adatok: alapértelmezett zsák(bag) Kimenő adatok: - Definíció: Bag::~Bag() delete [] elemek; 3. Másoló konstruktor Tevékenység: A konstruktor létrehoz egy zsákot, azaz automatikusan lefoglalja annak elemeit tartalmazó tömböt, és átmásolja a megadott értékketl. Bemenő adatok: egy zsák (Bag) Kimenő adatok: új zsák (Bag) Definíció: Bag::Bag(const Bag& a) meret = a.meret; kapacitas = a.kapacitas; elemek = new Elem[kapacitas]; for(int i=0; i<meret; ++i) elemek[i] = a.elemek[i]; 4. Értékadás operátor Tevékenység: Az operátor értékül adja az értékadás jobboldalán adott zsákot a baloldalán álló (az operátor által alapértelmezett) zsáknak Ha a két zsák nem ugyanaz, akkor lecseréli az alapértelmezett zsák elemeit tartalmazó tömböt egy megfelelő méretűre és át másolja az adott zsák értékeit tartalmazó tömböt. Bemenő adatok: zsák (Bag) Kimenő adatok: alapértelmezett zsák (Bag) Definíció: Bag& Bag::operator=(const Bag& a) if(this==&a) return *this; delete [] elemek; meret = a.meret; kapacitas = a.kapacitas; elemek = new Elem[kapacitas]; for(int i=0; i<meret; ++i) elemek[i] = a.elemek[i]; return *this;
5. Érték betétele Tevékenység: Elem betétele az alapértelmezett zsákba. Ha volt már a zsákba ilyen érték, megnöveli egyel a darabszámát, egyébként beszúr egy új elemet. Ha az értékeket tartalmazó tömb megtelt, akkor megduplázza a kapacitását. Ezt úgy teszi, hogy létrehoz egy kétszer akkora kapacitású tömböt és egyenként átmásolja az elemeket. Bemenő adatok: az alapértelmezett zsák (Bag) Éreték( int) Kimenő adatok: - Definíció: void Bag::insert(int value) bool l=false; for(int i=0;i<meret;i++) if (elemek[i].value==value) elemek[i].number++;l=true;; if(!l) if (meret==kapacitas) Elem *p=elemek; kapacitas=kapacitas*2; elemek= new Elem[kapacitas]; for(int i=0;i<meret;i++) elemek[i]=p[i]; delete [] p; elemek[meret].value=value; elemek[meret].number=1; meret++;
6. Érték kitörlése Tevékenység: Elem kitörlése az alapértelmezett zsákból. Ha több mint egy ilyen érték van a zsákban, csökkenti egyel a darabszámát. Ha egy ilyen érték van a tömbben, felülírja az utolsó elemmel és csökkenti a zsák méretét eggyel. Bemenő adatok: az alapértelmezett zsák (Bag) éreték( int) Kimenő adatok: - Definíció: void Bag::erase(int value) for(int i=0;i<meret;i++) if (elemek[i].value==value) elemek[i].number--; if (elemek[i].number==0) elemek[i]=elemek[meret-1];meret--; 7. Üresség lekérdezése Tevékenység: Zsák ürességének lekérdezése. Igazzal tér vissza, ha üres a zsák, különben hamissal. Bemenő adatok: az alapértelmezett zsák (Bag) Kimenő adatok: logikai érték( bool) Definíció: bool Bag::isempty() return meret==0; 8. Elem megszámlálása Tevékenység: Egy adott érték megszámlálása. Végigmegy az értékeket tartalmazó tömbön, és ha talál megegyezőt az adott értékkel, akkor visszaadja a darabszámát. Különben nullát ad vissza. Bemenő adatok: az alapértelmezett zsák (Bag) éreték(int) Kimenő adatok: logikai érték( bool) Definíció: int Bag::count(int value) for(int i=0;i<meret;i++) if(elemek[i].value==value)return elemek[i].number; return 0;
9. Különbség Tevékenység: Két egymásba ágyazott ciklussal összehasonlítja a két zsák értékeit. Ha megegyeznek, akkor, ha darabszámuk különbsége nagyobb nullánál megtörténik a kivonás, egyébként töröljük az elemet a kisebbítendő szákból úgy, hogy értékül adjuk neki az utolsó elemet és csökkentjük eggyel a tömb méretét. Bemenő adatok: kisebbítendő zsák és kivonandó zsák (Bag, Bag) Kimenő adatok: eredmény zsák(bag) Definíció: Bag operator-(bag a,const Bag& b) for(int i=a.meret-1; i>=0; --i) for(int j=0; j<b.meret; j++) if(a.elemek[i].value==b.elemek[j].value) int dif=a.elemek[i].number-b.elemek[j].number; if (dif<=0) a.elemek[i]=a.elemek[a.meret-1]; a.meret--; else a.elemek[i].number=dif; return a; 10. Beolvasás Tevékenység: Az operátor a standard bemenetről bekéri az adott zsák értékeit. Be/kimenő adat: standard bemeneti folyam (istream ) Bemenő adat: zsák (Bag) Definíció: istream& operator>>(istream& s, Bag& a) string st; do s >> st; if(st!="q") a.insert(atoi(st.c_str())); while (st!="q");
10. Kiírás Tevékenység: Az operátor a standard kimenetre írja az adott zsák összes értékét. Be/kimenő adat: standard bemeneti folyam (ostream) Bemenő adat: zsák (Bag) Definíció: ostream& operator<<(ostream& s, const Bag& a) s << "< "; for(int i=0; i<a.meret; ++i) for (int j=0; j<a.elemek[i].number;j++) s << a.elemek[i].value; if (j<a.elemek[i].number-1) s << ", "; if (i<a.meret-1) s<< ", "; ; s << " >"; return s; Tesztkörnyezet A fekete doboz teszteseteket egy részét lefedő C++ kód: main.cpp #include <iostream> #include <stdlib.h> #include "bag.h" using namespace std; class Menu public: void Run(); private: Bag a; void MenuWrite(); void Insert(); void Erase(); void IsEmpty(); void Count(); void Distinct(); void Print(); ;
int main() Menu m; m.run(); return 0; void Menu::Run() int c = 0; do MenuWrite(); cin >> c; switch(c) case 1: Insert(); case 2: Erase(); case 3: IsEmpty(); case 4: Count(); case 5: Distinct(); case 6: Print(); while(c!=0); void Menu::MenuWrite() cout << endl << endl; cout << " 1. -Elemek betevése" << endl; cout << " 2. -Elem kivétele" << endl; cout << " 3. -Üres-e a zsák" << endl; cout << " 4. -Hányszor van egy érték" << endl; cout << " 5. -Zsákok külömbsége" << endl; cout << " 6. -Zsák kiíratása" << endl; cout << " 0. -Kilepes" << endl;
void Menu::Insert() cout << "Adja meg a betenni kívánt értékeket(kilépés: Q):"; cin >> a; cout << "Adja zsák aktuálistartalma: " << a; void Menu::Erase() int value; cout << "Adja meg a kivenni kívánt értéket:"; cin >> value; a.erase(value); cout << "Adja zsák aktuális tartalma: " << a; void Menu::IsEmpty() if (a.isempty()) cout << "A zsák üres."; else cout << "A zsák nem üres."; void Menu::Count() int value; cout << "Adja meg a lekérdezni kívánt értéket:"; cin >> value; cout << "A(z) " << value << " érték elõfordulásainak száma: " << a.count(value); void Menu::Distinct() cout << "Kérem a kivonni való zsák elelmeit! (Kilépés: Q )"; Bag b; cin >> b; cout << a << " - " << b << " = " << a-b; void Menu::Print() cout << "Adja zsák aktuális tartalma: " << a;