Standard Template Library Adatstruktúrák és algoritmusok a C++ nyelvhez
Bevezetés A Hewlett-Packard Company által fejlesztett sablonkönyvtár -> bekerült a C++98-ba Általánosított osztály- és függvénysablonokat tartalmaz a leggyakrabban alkalmazott adatstruktúrák és algoritmusok használatára Standard Template Library Programmer's Guide: http://www.sgi.com/tech/stl/ Tóth Bertalan: C++ programozás STL konténerekkel http://www.zmgzeg.sulinet.hu/bemutatkozunk/tan konyv/progismhalado/ccpp/c11stl.pdf
Az STL részei Konténerek (tárolók) - adatstruktúrák Iterátorok (bejárók) Algoritmusok - az adattárolók elemeinek elérése - az alapvető algoritmusok megvalósítása (sorba rakás, keresés, szélsőértékek )
Az STL részei Konténerek Allokátorok Helyfoglaló Iterátorok Adapterek Illesztő Algoritmusok Funktorok Függvényobjektum (functors) Adapter tárolók Adatfolyamok (stream) String adatstruktúrára alapozott egyszerű kezelésű adatstruktúrák I/O C++
Példa Tároló Bejáró Algoritmus #include <vector> #include <algorithm> using namespace std; vector<int> vect; // a vect adatainak megadása sort(vect.begin(),vect.end());
Tárolók Soros array<> vector<> deque<> forward_list<> list<> Az elemek sorrendjét a tárolás sorrendje határozza meg. Rendezett set<> multiset<> map<> multimap<> Asszociatív Nem rendezett unordered_set<> unordered_multiset<> unordered_map<> unordered_multimap<> Az adatokat egy kulccsal azonosítva tárolják.
Az STL elemek fejállományai Leggyakoribb elemek Algoritmusok: rendezés, keresés, másolás stb. Asszociatív tárolók: rendezett halmazok (elemismétlődéssel multiset, illetve elemismétlődés nélkül - set) Asszociatív tárolók: kulcs/érték adatpárok kulcs szerint rendezett tárolása 1:1 (map), illetve 1:n (multimap) kapcsolatban Asszociatív tárolók: nem rendezett halmazok (elemismétlődéssel unordered_multiset, illetve elemismétlődés nélkül unordered_set) Asszociatív tárolók: kulcs/érték adatpárok nem rendezett tárolása (elemismétlődéssel unordered_multimap, illetve elemismétlődés nélkül unordered_map) Függvényobjektumok ( function(), bind() ) Iterátorelemek, előre definiált iterátorok, adatfolyam-iterátorok Műveleti elemek, move(), swap(), a pair (adatpár) struktúra Numerikus műveletek a konténerekben tárolt adatokon Soros tároló adapter: verem (statck) Soros tároló adapterek: sor (queue), prioritásos sor (priority_queue) Soros tároló: egydimenziós statikus tömb (array) Soros tároló: egyirányú lineáris lista (forward_list) Soros tároló: kétirányú lineáris lista (list) Soros tároló: kettősvégű sor (deque) Soros tároló: egydimenziós dinamikus tömb (vector) Fejállomány <algorithm> <set> <map> <unordered_set> <unordered_map> <functional> <iterator> <utility> <numeric> <stack> <queue> <array> <forward_list> <list> <deque> <vector>
Soros és asszociatív tárolok tulajdonságai mindegyiknek van default és copy (értékekre) konstruktora, = operátora, a.swap(b) tagfüggvénye és swap(a,b) algoritmusa, == és!= operátora (azonos elemek azonos sorrendben), sorrendiség (az első nem azonos elem határozza meg), begin() bejáró a kezdetre (rbegin() a kétirányúnál ), end() bejáró a végére (rend() a kétirányúnál), size() méret, empty() üres? maxsize() maximálisan tárolható elemszám tagok (értékek, referenciák, const referenciák, bejárók (vissza is)), konstans bejárók (vissza is), helyzet-különbségek, méretek)
A vector: #include <vector> Elemek sorban (dinamikus tömbökkel megvalósítva) Folytonos adatterületen pointerrel és bejáróval is bejárható Automatikusan növekszik és csökken a tárolási mérete Az elemek könnyen elérhetők pozíció alapján (állandó idővel) Az elemek sorban bejárhatók (lineáris idővel) Elemek illeszthetők/törölhetők a végéről (konstans idővel) Elemek beilleszthetők és törölhetők is, azonban erre mások (deque, list) jobb időt produkálnak Memóriamodell:
A vector: #include <vector> Konstruktorok (vector<tip>) // üres tip típusú vektor vector<tip> vektor_0; vector<tip> vektor_0 {}; // db elemű vektor a típus alapértelmezett elemeivel vector<tip> vektor_1 (db); // db elemű vektor ertek elemekkel vector<tip> vektor_2 (db,ertek); // inicializáló lista alapján vector<tip> vektor_3 {init. lista}; // iterátor vektor-ból [begin,end) tartomány átmásolásával vector<tip> vektor_4 (vektor.begin(), vektor.end()); vector<tip> vektor_4 {vektor.begin(), vektor.end()}; // másolás vektor-ból (másoló konstruktor) vector<tip> vektor_5 (vektor); vector<tip> vektor_5 {vektor}; // átmozgatás vektor-ból vector<tip> vektor_5 (move(vektor)); vector<tip> vektor_5 {move(vektor)};
A vector: #include <vector> Bejárók (vector<tip>::iterator) vector<tip>::iterator v; // iterátora begin() // kezdet end() // vég 1 2 3 3 2 1 vector<tip>::reverse_iterator rv; // fordított irányú iterátor rbegin() // vissza kezdet rend() // vissza vég &v[0] (*v) v++ rv++ // pointer a memóriára // maga a vektor elem, mintha pointer lenne // következő elemre lépés // fordított irány esetén is ez adja a következő // elemet nem a rv--
A vector: #include <vector> Tulajdonságok operator[i], at(i) // az i. elem, at() ellenőriz is front() // az első elem referenciája back() // az utolsó elem referenciája tip* data() // visszaadja az első elem pointerét C++11 Méretek size() resize(n[,val]) capacity() reserve(n) Módosítók push_back(val) pop_back() // az elemek aktuális száma // n-re méretez val [vagy default] elemmel feltöltve // a férőhelyek száma (automatikusan is nő) // a férőhelyek számának növelése n-re // a val az utolsó elem mögé kerül // törli az utolsó elemet és visszatér az értékével // adott pozícióba helyez, nem hatékony iterator insert ( iterator position, const T& x )
A vector: #include <vector> Törlés bool empty ( ) const; erase(iterator tol, iterator ig) void clear() // üres-e // tol ig töröl // mindent töröl Keresés (#include <algorithm>) vector<tip>::iterator find(vector< tip>::iterator tol, vector< tip >::iterator ig, tip adat); vector<tip>::iterator search(vector< tip>::iterator tol, vector< tip >::iterator ig, vector< tip >::iterator elemtol, vector< tip >::iterator elemig); bool binary_search(vector< tip>::iterator tol, vector< tip >::iterator ig, tip ert); Műveletek elvégzése elemeken fvtip for_each(vector< tip>::iterator tol, vector< tip >::iterator ig, fvtip fvg);
A vector: #include <vector> A konténer elemeinek bejárása iterátorok segítségével: vector<int> vect {20,30,40,50}; vector<int>::iterator v; for (v = begin(vect); v!= vect.end(); v++) cout << *v << endl; v = begin(vect); while(v!= vect.end()) { cout << *(v++) << endl; } for (auto v2 = begin(vect); v2!= vect.end(); v2++) cout << *v2 << endl; //C++11
A vector: #include <vector> A konténer elemeinek bejárása iterátorok nélkül: for (auto elem: vect) // csak olvasható C++11 cout << elem << endl; for (auto& elem: vect) // írható/olvasható C++11 elem+=10; A konténer elemeinek bejárása for_each segítségével: void myfunc(int elem) { cout << elem << endl; } // elemeket kiíró függvény vector<int> vect {20,30,40,50}; for_each (vect.begin(), vect.end(), myfunc); //fgv. meghívása az elemekre
A vector: #include <vector> Megjegyzések: A beírás elrontja az iterátorokat: vector<int>::iterator beg = vect.begin(); vector<int>::iterator end = vect.end(); vect.push_back(12); vector<int>::iterator p=find(beg, end,12); Szükséges hely bővítése: // elrontja // futási hiba Ha ismert az elemek száma, akkor mindig használjuk a mérettel rendelkező konstruktort, mert ha új elem behelyezésekor elfogy a lefoglalt hely, akkor automatikusa átméreteződik (új helyre másolódik) a tartalom, ez nagy elemszám esetén belassítja a működést. vector<int> vect; for (int i = 0; i < 1000; i++) { vect.push_back(i); // üres vektor kerül bővítésre // bővítés cout << "merete:"<<vect.size()<<" helye:"<<vect.capacity()<< endl; } // másolások száma: 18
#include <stdlib.h> #include <vector> #include <algorithm> #include <iostream> using namespace std; void myfunc (int i) { cout << " " << i; } int main(){ vector<int> vect(2,0); vect[0]=1; // a vect adatainak megadása 1 vect[1]=2000; for (int i=0; i<8; i++) // a vect adatainak megadása 2 vect.push_back((i+1)*5); // a vect adatainak kiírása 1 vector<int>::iterator vit; // iterator vectorra for (vit=vect.begin(); vit!=vect.end(); vit++) cout<<*(vit)<<endl; cout<<endl; cout<<"merete:"<<vect.size()<<" helye:"<<vect.capacity()<<endl; //10 13 // a vect adatainak kiírása 2 for (int i=0; i<vect.size(); i++) cout<<vect[i]<<endl; cout<<endl; sort(vect.begin(),vect.end()); // rendező algoritmus // a vect adatainak kiírása 3 C++11 for (auto& elem : vect) cout << elem << endl; cout<<endl; // keresés vector<int>::iterator it = find (vect.begin(), vect.end(), 2000); if (it!= vect.end()) cout << "van: " << *(it) << endl; // a vect adatainak kiírása 4 for_each (vect.rbegin(), vect.rend(), myfunc); // 2000 40 35 30 25 20 15 10 5 1 int *p = vect.data(); // az első elemre mutató pointer lekérdezése C++11 return 0;}
Az array: #include <array> C típusú tömb osztályba foglalása C++11-ben Létrehozás: array<tip,méret> Elemek sorban meghatározott elemszámmal foglalnak helyet Folytonos adatterületen pointerrel és bejáróval is bejárható Az elemek könnyen elérhetők pozíció alapján (állandó idővel) Az elemek sorban bejárhatók (lineáris idővel) Memóriamodell: Előnye a hagyományos tömbbel szemben, hogy rendelkezik bejáróval és tagfüggvényekkel, valamint alkalmazhatók rá az algoritmusok.
Az array: #include <array> Hátránya, hogy függvényeknél a típusnak és a méretnek is egyeznie kell. //Összeg fgv. csak 10 elemű int típusú array-ra double szum(array<int, 10> a) { double szum = 0; for (int i = 0; i < 10; i++) szum += a[i]; return szum; } array<int, 10> tomb; tomb.fill(20); szum(tomb); //eltérő méret esetén fordítási hiba
A deque: #include <deque> Kétősvégű sor, amely mindkét végén növelhető Konstans idő alatt adhatunk hozzá, illetve távolíthatunk el elemet a sor végeiről: push_front(val), pop_front() Nem folytonos adatterületen helyezkedik el (nincs capacity() és reserve(n)), egydimenziós tömböt tartalmazó listában tárolódik Az elemek könnyen elérhetők pozíció alapján (állandó idővel) Az elemek sorban bejárhatók (lineáris idővel) Lassabb, mint a sor (queue) Memóriamodell:
Az egyszeres láncolatú lista: #include <forward_list> Csak az elején lehet bővíteni: push_front(val) Az elemek nem érhetőek el az indexelés operátorral (nincs [] és at()) Tetszőleges pozícióba beszúrhatunk (insert_after()) és törölhetünk (erase_after()) konstans idő alatt A beszúrás és törlés művelet nem rontja el az iterátorokat Memóriamodell:
A list: #include <list> Kétirányban láncolt lista, mindkét végéhez hozzáadhatunk és törölhetünk elemeket Az elemek nem érhetőek el az indexelés operátorral (nincs [] és at()) Tetszőleges pozícióba beszúrhatunk (insert()) és törölhetünk (erase()) konstans idő alatt A beszúrás és törlés művelet nem rontja el az iterátorokat Memóriamodell:
A list: #include <list> Tagfüggvényei: lst1.splice(i1,lst2) // lst2 összes elem törlődik és lst1-be // insertálódik i1-től lst1.merge(lst2) // sorba rendezett listák összefésülése lst1.sort() // növekvő sorba rendezés lst1.sort(hasonlító) // sorba rendezés Hasonlító fgv.objektum szerint lst1.revers() // sorrend megfordítása lst1.remove(adat) // minden adat értékű elem törlése lst1.unique() // egymást követő ismétlődő elemek törlése iterator insert (iterator position,const value_type& val); //beszúrás Egyszerű példa: persze: int-el mindig, minden működik Joe list<int> lista1{ 10, 5, 8, 23, 8 }; lista1.sort();//5 8 8 10 23 lista1.unique(); // 5 8 10 23 list<int> lista2{ 0, 15, 8, 23, 8 }; lista2.sort(); //0 8 8 15 23 lista2.merge(lista1); //0 5 8 8 8 10 15 23 23 lista1 üres lett lista2.insert(lista2.begin(), 34); // 34 0 5 8 8 8 10 15 23 23 lista1.splice(lista1.begin(), lista2); // lista2 üres lett
Összetettebb példa: class Auto{ private: string tipus; string rendszam; public: Auto(string tip, string rsz) :tipus(tip), rendszam(rsz){}; void Kiir() const { cout << tipus << "\t" << rendszam << endl; } string Rszam() const {return rendszam;} string Tipus() const {return tipus;} bool operator < (const Auto& masik) const { return tipus < masik.tipus;} bool operator == (const Auto& masik) const { return tipus == masik.tipus;} }; bool AutotHasonlit(const Auto& bal, const Auto& jobb) { //rendező függvény return bal.rszam() < jobb.rszam(); } list<auto> Kocsik{ { "Volvo", "DFR-356" }, { "Renault", "MER-637" } }; Kocsik.push_back(Auto("Opel", "HIK-123")); Kocsik.push_front(Auto("Suzuki", "GDS-145")); Kocsik.push_front(Auto("Suzuki", "GDS-145")); Kocsik.push_front(Auto("Opel", "MKL-785")); list<auto>::iterator it; for (it = Kocsik.begin(); it!= Kocsik.end(); it++) it->kiir(); Kocsik.sort(); // Sorba rendezés az alapértelmezett < operátorral for (it = Kocsik.begin(); it!= Kocsik.end(); it++) it->kiir(); Kocsik.sort(AutotHasonlit); // Sorba rendezés függvénnyel, rendszám alapján for (it = Kocsik.begin(); it!= Kocsik.end(); it++) it->kiir(); Kocsik.unique(); // Azonos elemek törlése == operátorral for (it = Kocsik.begin(); it!= Kocsik.end(); it++) it->kiir();
Asszociatív tárolók A hozzáférés nem az elem pozíciója, hanem egy kulcs értéke alapján történik. Rendezett konténer esetén a műveletek általában logaritmikus végrehajtási idejűek Minden lehetséges kulcsérték legalább egyszer előfordul. Léteznek kulcsismétlést megengedő változatok (multiset, multimap), a keresés ebben az esetben lineáris végrehajtási idejű
A set #include <set> A tárolt adatokat kulcsként használja set esetén a kulcsoknak egyedinek kell lennie multiset esetén ismétlődhetnek a kulcsok Konstruktorok (set<tip,compare>) set<tip> set_0; // üres (tip1) típusú set tip tomb[]= {t0, t1, t2}; set<tip> set_1(tomb,tomb+3); // iteratív pointerekkel set<tip> set_2(set_1.begin(),set_1.end()); // iterációs létrehozás set<tip> set_3(set_1); // másoló konstruktor // Lehet definiálni az összehasonlítást default: std::less<tip> Bejárók (set<tip>::iterator) begin() // kezdet end() // vég rbegin() // vissza kezdet rend() // vissza vég set<tip>::iterator m; (*m) // maga az elem, mintha pointer lenne 1 3 5 6 7 1 3 3 7 7
A set #include <set> Méretek size() max_size() Módosítók insert(iterator,const tip & x); Törlés bool empty ( ) const; erase(iterator tol, iterator ig) clear() Keresések (#include <algorithm>) NINCS operator[] // az elemek aktuális száma // az elemek maximális száma // beszúr // üres-e // tol ig töröl // mindent töröl set<tip>::iterator find(const tip& x); // az adott kulcsú elem size_type count ( const key_type& x ) const; //adott elem száma pair<itb,ite> equal_range ( const key_type& x ); //adott kulcsú elem tartománya iterator lower_bound ( const key_type& x ); // nem kisebb, mint x elem iterátora iterator upper_bound ( const key_type& x ); // nagyobb, x elem iterátora
A set #include <set> Tárolása bináris fájlként történik Elem beszúrása: 7 ->
A set #include <set> Példák: set<int> set_0; int tomb[] = { 8, 3, 10, 1 }; set<int> set_1(tomb, tomb + 4); set<int> set_2(set_1.begin(), set_1.end()); set<int> set_3(set_1); // üres (int) típusú set // iteratív pointerekkel // iterációs létrehozás // másoló konstruktor set<int>::iterator it_s; for (it_s = set_1.begin(); it_s!= set_1.end(); it_s++) // set elemek kiírása cout << " " << *it_s; // 1 3 8 10 cout << endl; cout << "size:" << set_1.size() << endl; // meret 4 cout << "maxsize:" << set_1.max_size() << endl; // max meret 214748364 set_1.insert(set_1.begin(), 6); // beilleszt az 1. helyre for (it_s = set_1.begin(); it_s!= set_1.end(); it_s++) cout << (*it_s) << endl; // 1 3 6 8 10 if (set_1.find(10)!= end(set_1)) // tartalmaz 10-et cout << set_1.count(10) << " db. 10-es" << endl; // 1 db. 10-es pair<set<int>::iterator,set<int>::iterator> par = set_1.equal_range(20); cout<<"a 6-nal nem kisebb kulcsu elem:"<<(*par.first) << endl; //6 cout<<"a 6-nal nem kisebb kulcsu elem:"<<(*set_1.lower_bound(20)) << endl; //6 cout<<"a 6-nal nagyobb kulcsu elem:"<<(*par.second) << endl; //8 cout<<"a 6-nal nagyobb kulcsu elem:"<<(*set_1.upper_bound(20)) << endl; //8
A set #include <set> #include <iostream> #include <vector> #include <algorithm> #include <set> #include <functional> using namespace std; int tomb[] = { 5,6,7,7,6,5,5,6 }; vector<int> v(tomb, tomb + 8); // 5 6 7 7 6 5 5 6 sort(v.begin(), v.end()); // 5 5 5 6 6 6 7 7 vector<int>::iterator also, felso; also = lower_bound(v.begin(), v.end(), 6); felso = upper_bound(v.begin(), v.end(), 6); cout << "6 also hatar poz.: " << (also - v.begin()) << endl;// 3 cout << "6 felso hatar poz.: " << (felso - v.begin()) << endl;// 6 multiset<int, greater<int>> ms(tomb, tomb + 8); // 7 7 6 6 6 5 5 5 multiset<int, greater<int>>::iterator malso, mfelso; malso = ms.lower_bound(6); mfelso = ms.upper_bound(6); cout << "6 also hatar poz.: " << distance(ms.begin(), malso) << endl;// 2 cout << "6 felso hatar poz.: " << distance(ms.begin(), mfelso) << endl;// 5
A set #include <set> Saját adattípus, vagy osztály esetén meg kell adni a rendezést : 1. Függvény pointerrel: multiset<auto, bool(*) (const Auto &, const Auto &)> Autok(AutotHasonlit); 2. Osztálysablon alapján: (#include <functional>) multiset<auto, less<auto>> Autok; //operátor < alapján 3. Függvényobjektum alkalmazásával: struct Rendez { bool operator () (const Auto & bal, const Auto & jobb) { return bal.rszam() < jobb.rszam();} }; multiset<auto, Rendez> Autok; Autok.insert(Auto("Opel", "HIK-123")); Autok.insert(Auto("Suzuki", "GDS-145")); Autok.insert(Auto("Renault", "MER-637")); Autok.insert(Auto("Opel", "HIK-123")); for (auto it2 = Autok.begin(); it2!= Autok.end(); it2++) it2->kiir(); //Auto(*it).Kiir();
A map #include <map> Adatpárok tárolódnak (kulcs, érték), a pair sablon alapján first -> kulcs second -> adat template <class T1, class T2> struct pair { T1 first; T2 second; pair() : first(t1()), second(t2()) {} }; A map elemei a kulcs alapján rendezettek 1 2 5 7 8 A multimap esetén megengedett a kulcsismétlés 1 2 5 5 5
A map #include <map> Konstruktorok (map<tip1,tip2,compare>) map<tip1,tip2> map_0; // üres (tip1,tip2) típusú map map<tip1,tip2> map_1(map_0.begin(),map_0.end()); // iterációs létrehozás map<tip1,tip2> map_2(map_1); // másoló konstruktor // Lehet definiálni az összehasonlítást kulcsra és értékre is Bejárók (map<tip1,tip2>::iterator) begin() // kezdet end() // vég rbegin() // vissza kezdet rend() // vissza vég map<tip1,tip2> ::iterator m; // maga az elem, mintha pointer lenne (*m).first a kulcs m->second - az érték Tulajdonságok operator[] // az adott kulcsú elem, ha nem létezik létrehozza a kulcsot érték nélkül
Keresések (#include <algorithm>) map<tip1,tip2>::iterator find(const tip1& x); size_type count ( const key_type& x ) const; pair<itb,ite> equal_range ( const key_type& x ); //adott kulcsú elem tartománya iterator lower_bound ( const key_type& x ); // nem kisebb, x kulcsú elem it. iterator upper_bound ( const key_type& x ); // nagyobb, x kulcsú elem it. A map #include <map> Méretek size() max_size() // az elemek aktuális száma // az elemek maximális száma Módosítók insert(iterator,pair<tip1,tip2>(kulcs,ertek)); Törlés bool empty ( ) const; // üres-e erase(iterator tol, iterator ig) // tol ig töröl clear() // mindent töröl
Példák: A map #include <map> map<char,int> map_0; // az alap konstruktor map<char,int>::iterator m0_it; // iterátor map_0['c']=99; map_0['b']=98; map_0['a']=97; map_0['d']=100;// feltöltés m0_it=map_0.begin(); cout<<m0_it->first<<endl; // az első elem kulcsa - a cout<<(*m0_it).second<<endl; // az első elem értéke - 97 map<char,int> map_1 (map_0.begin(),map_0.end()); // iteratív feltöltés map<char,int> map_2 (map_1); // másoló konstruktor for (m0_it=map_0.begin(); m0_it!=map_0.end(); m0_it++) // a map elemek kiírása cout<<(*m0_it).first<<" : "<<(*m0_it).second<<endl; // a : 97, b : 98 cout<<"size:"<<map_0.size()<<endl; // a méret 4 cout<<"maxsize:"<<map_0.max_size()<<endl; // a maximális méret - 178956970 m0_it=map_0.begin(); map_0.insert(m0_it,pair<char,int>('a',65)); // beszúrás for (m0_it=map_0.begin(); m0_it!=map_0.end(); m0_it++) // cout<<(*m0_it).first<<" : "<<(*m0_it).second<<endl; // A : 65, a: 97, cout<<map_0.count('a')<< " db. a kulccsal"<<endl; // 1 cout<<"a legkisebb 'a' kulcsu elem:" <<(*map_0.lower_bound('a')).second<<endl; // 97 cout<<"az 'a' kulcsu elem felso hat:" <<(*map_0.upper_bound('a')).second<<endl; // 98
A map #include <map> map<char, string> Morse; Morse['A'] = ".-"; Morse['N'] = "-."; Morse['1'] = ".----"; Morse['B'] = "-..."; Morse['O'] = "---"; Morse['2'] = "..---"; Morse['C'] = "-.-."; Morse['P'] = ".--."; Morse['3'] = "...--"; Morse['D'] = "-.."; Morse['Q'] = "--.-"; Morse['4'] = "...-"; Morse['E'] = "."; Morse['R'] = ".-."; Morse['5'] = "..."; Morse['F'] = "..-."; Morse['S'] = "..."; Morse['6'] = "-..."; Morse['G'] = "--."; Morse['T'] = "-"; Morse['7'] = "--..."; Morse['H'] = "..."; Morse['U'] = "..-"; Morse['8'] = "---.."; Morse['I'] = ".."; Morse['V'] = "...-"; Morse['9'] = "----."; Morse['J'] = ".---"; Morse['W'] = ".--"; Morse['0'] = "-----"; Morse['K'] = "-.-"; Morse['X'] = "-..-"; Morse['L'] = ".-.."; Morse['Y'] = "-.--"; Morse['M'] = "--"; Morse['Z'] = "--.."; string input = "SOS Korszeru Informatika"; for (int i = 0; i< input.length(); i++) cout << Morse[toupper(input[i])];
Rendezetlen asszociatív tárolók A set, multiset, map, multimap tárolók rendezetlen megfelelői. (unordered_set, unordered_map ) Az elemeket egy hasító függvény alapján osztják szét halmokra. Az elemeket a hasító tábla alapján érjük el. Például: szám % 10 10 halom jön létre A beépített alaptípusok mindegyikének van egy hasító függvénye. Saját adattípus esetén a hasító és az összehasonlító függvényeket nekünk kell elkészíteni. hash<auto>(const Auto & obj); // hasító fgv. equals_to<auto>(const Auto & obj1, const Auto & obj2); // == operátor vagy fgv. objektum
Példák: Az unordered_set #include <unordered_set> struct AutoHasher //Hasító fgv. objektum { size_t operator()(const Auto & obj) const { return hash<string>()(obj.tipus()); // Típusból képzett hasító fgv. } }; struct AutoComparator //Összehasonlító fgv. objektum { bool operator()(const Auto & obj1, const Auto & obj2) const { return obj1.rszam() == obj2.rszam(); // Rendszámok egyezése } }; unordered_set<auto,autohasher,autocomparator> usetautok; usetautok.insert(auto("opel", "HIK-123")); usetautok.insert(auto("suzuki", "GDS-145")); usetautok.insert(auto("renault", "MER-637")); usetautok.insert(auto("opel", "FGH-578")); usetautok.insert(auto("opel", "HIK-123")); //azonos rendszám miatt nem kerül be for (auto it2 = usetautok.begin(); it2!= usetautok.end(); it2++) it2->kiir();
Tároló adapterek vector<> deque<> list<> Soros Tároló Tároló adapter stack<> queue<> priority_queue<> Soros tárolókra épülő osztálysablonok, melyek egyszerű kezelésű adatstruktúrákat valósítanak meg. Az adapterek elemein nem lehet végiglépkedni, ezért nem használhatóak rajtuk az algoritmusok sem.
A stack #include <stack> Last-in, first-out, LIFO működést valósít meg Adaptálható deque,vector, list konténerekre építve (amiknek van back(), push_back() és pop_back() művelete). top() verem pop() push()
A stack #include <stack> Konstruktorok (stack<tip, tarolo<tip>>) stack<tip,vector<tip> > stack_0; // üres stack vector-t használ vector<tip> vek (2,200); // vektor 2 elemmel stack<tip,vector<tip> > stack_1 (vek); // stack vektorral inicializálva Kezelés bool empty ( ) const void push(const tip & x) void pop() tip & top() size_type size() const // üres-e // a stack-be tölt // leveszi a felső elemet // visszaadja a felső elemet // a méret; Példák stack<int,vector<int> > stack_0; // üres stack vector-t használ vector<int> vek (2,200); // vektor 2 elemmel stack<int,vector<int> > stack_1 (vek); // stack vektorral inicializálva for (int i=1; i<5; i++) stack_1.push(i);// tölti az elemeket for (int i=1; i<3; i++){ cout<<stack_1.top()<<endl; // kiírja és stack_1.pop(); // leveszi a felsőt - 4 3 } cout<<stack_1.size()<<endl; // a meret 4 (2+4-2)
A stack #include <stack> Mi a kimenet? int szam = 11; stack<int> verem; while (szam > 0) { verem.push(szam % 2); szam /= 2; } while(!verem.empty()) { cout << verem.top(); verem.pop(); }
A queue #include <queue> First-in, first-out, FIFO működésű sort valósít meg Adaptálható deque vagy list konténerekre építve (amiknek van front(), back(), push_back() és pop_front() művelete). Az alapműveletek mindegyikéhez konstans idő szüksége back() front() push() sor pop()
A queue #include <queue> Konstruktorok (queue<tip, tarolo<tip>>) queue<tip,list<tip>> que_0; // üres queue list-t használ list<tip> lst (2,200); // list 2 elemmel queue<tip,list<tip>> que_1 (lst); // queue list-tel inicializálva Kezelés bool empty ( ) const; // üres-e void push(const tip & x) // a queue végére tölt void pop() // leveszi a legrégebbi elemet tip & front() // visszaadja a legújabb elemet tip & back() // visszaadja a legrégebbi elemet size_type size() const // a méret; Példák list<int> lst (2,200); // lista 2 elemmel queue<int,list<int>> que_1 (lst); // queue list-tel inicializálva queue<int,deque<int>> que_0; // üres queue deque-t használ for (int i=1; i<10; i++) que_0.push(i); // tölti az elemeket for (int i=1; i<4; i++){ cout<<que_0.front()<<endl; // 1 2 3 que_0.pop(); // leveszi a felsőt } cout<<que_0.size()<<endl; // 6 (9-3)
A priorty_queue #include <queue> Best-in, first-out, BIFO működésű sort valósít meg, ahol a legjobb a legnagyobb prioritású elem Adaptálható vector vagy deque konténerekre építve Az elsőbbséget egy rendezési előírás definiálja A sor elején mindig a legnagyobb prioritású elem helyezkedik el top() prioritásos sor pop()
A priorty_queue #include <queue> Konstruktorok (priority_queu<tip, tarolo<tip>, class Compare>) priority_queu<tip,vector<tip>> pque_0; // üres p_queue vector-ral vector<int> v{ 1, 34, 43, 20 }; // vector elemekkel priority_queue<int, vector<int>> pque_1(v.begin(), v.end()); Kezelés bool empty ( ) const; // üres-e void push(const tip & x) // a queue végére tölt void pop() // leveszi a legrégebbi elemet tip & front() // visszaadja a legújabb elemet size_type size() const // a méret; Példák priority_queue<int, vector<int>> psor; // üres prioritási sor vector-ra psor.push(100); // feltöltés psor.push(20); psor.push(200); while (!psor.empty()) { cout << psor.top() << endl; // kiíratás 200 100 20 psor.pop(); // leveszi a felsőt }
Segítség a konténer választáshoz: Forrás: Tóth Bertalan: C++ programozás STL konténerekkel
Iterátorok #include <iterator> Az adathalmaz bejárására alkalmas objektumok. Egy pozíciót határoz meg a tárolóban. Forrás: Tóth Bertalan: C++ programozás STL konténerekkel
Iterátorok #include <iterator> Iterátorokra alkalmazható függvénysablonok: begin(), end() C tömbre készít bejárót advance(n) előre mozgat n lépéssel distance(itb, ite) két iterátor közötti elemek száma next(it) egyel lépteti előre az iterátort prev(it) egyel visszalépteti az itereátort next(it,n), prev(it,n) n szer léptet előre/vissza
Input és Output iterátorok #include <iterator> istream_iterator: a konténer elemeinek beolvasására használható: vector<double> adatok(3, 0); cout << "Kerek 3 szamot: "; istream_iterator<double> polvaso(cin); for (int i = 0; i < 3; i++) { adatok[i] = *polvaso; if (i < 2) polvaso++; } for (double elem : adatok) cout << elem << "\t";
Input és Output iterátorok #include <iterator> ostream_iterator: a kimeneti adatfolyam használatával kiíratások végezhetők el az algoritmusokból vector<double> adatok(3, 0); cout << "Kerek 3 szamot: "; istream_iterator<double> polvaso(cin); for (int i = 0; i < 3; i++) { adatok[i] = *polvaso; if (i < 2) polvaso++; } copy(begin(adatok), end(adatok), ostream_iterator<double>(cout, "\t"));
Algoritmusok #include <algorithm> Globális függvénysablonok, amelyek iterátorok segítségével férnek hozzá a konténerben tárolt adatokhoz Az algoritmusok függetlenek a konténerektől, az iterátorok feladata ismerni a konténert Ha a tároló valamilyen algoritmus saját tagfüggvénnyel is meg tud valósítani, akkor célszerű inkább azt használni (hatékonyabb és biztonságosabb) A végrehajtott algoritmus működési eredményét többféleképpen is megkaphatjuk Konténerben Iterátorként Adatként
Algoritmusok #include <algorithm> Nem módosító algoritmusok: nem változtatják meg az elemeket, sem azok tárolási sorrendejét equal(), find(), for_each(), max(), min(), mismatch(), search() Módosító algoritmusok: copy(), fill(), for_each(), merge(), move(), replace(), swap(), transform() Eltávolító algoritmusok: elemek törlésére remove(), unique() Átalakító algoritmusok: elemsorrend megváltoztatáshoz partition(), reverse(), rotate() Rendező algoritmusok: partition(), sort() Rendezett tartomány algoritmusai: binary_search(), equal_range(), lower_bound(), upper_bound(), merge()
Függvényobjektumok Az algoritmusok működése testre szabható, meghatározhatjuk, hogy milyen művelet hajtódjon végre az elemeken Hagyományos függvénymutató is lehet, de legtöbbször objektum A függvényobjektum olyan típus, amely megvalósítja a függvényhívás () operátorát Létezik egyoperandusú (unary) és kétoperandusú (binary) függvényobjektum
Függvényobjektumok Hagyományos függvény: bool Paros(int x){ return (x % 2) == 0; } Functional: #include <functional> class Paros{ public: bool operator () (int x) const{ return (x % 2) == 0; } } p1; bool x = p1(1); //mintha függvény lenne
Függvényobjektumok void Novel(double &elem){ elem++;} struct Osszeg{ Osszeg(double osszeg = 0):osszeg(osszeg) { } void operator()(double szam){ osszeg += szam; } double osszeg; }; vector<double> szamok{ 2, 3, 6, 9, 35 }; for (double e : szamok) cout << e <<" "; cout << endl; //függvény for_each(begin(szamok), end(szamok), Novel); for (double e : szamok) cout << e <<" "; cout << endl; //függvény obj. Osszeg szum=for_each(begin(szamok), end(szamok), Osszeg()); cout << szum.osszeg<< endl;
Függvényobjektumok (C++98) Unáris függvény: template <class Arg, class Result> struct unary_function { typedef Arg argument_type; typedef Result result_type; }; Példa: class fact: public unary_function<int, long> { public: long operator () (int a) { long f=1; for (int i=2; i<=a; i++) f*=i; return f; } };
Függvényobjektumok (C++98) Bináris függvény: template <class Arg1, class Arg2, class Result> struct binary_function { typedef Arg1 first_argument_type; typedef Arg2 second_argument_type; typedef Result result_type; }; Példa: template <class T> struct plus : binary_function <T,T,T> { T operator() (const T& x, const T& y) const { }; } return x+y;
Függvényobjektumok C++11-ben leegyszerűsítették: class Negyzet{ public: int operator() (int a) { return a*a; }; } int init[3] = {3,5,7}; deque<int> f(init, end(init)); // vége: init +3 vector<int> c(5); // üres vektor // a deque elemek négyzete transform(f.begin(), f.end(), c.begin(), Negyzet()); for (deque<int>::iterator i=f.begin(); i<f.end(); i++) cout<<(*i)<<" "; // 3 5 7 cout<<endl; for (vector<int>::iterator i=c.begin(); i<c.end(); i++) cout<<(*i)<<" "; // 9 25 49 0 0 cout<<endl;
Függvényobjektumok Általánosítva: template <class T> class Negyzet{ public: T operator() (T a) { return a*a; }; } int init[3] = {3,5,7}; deque<int> f(init, end(init)); // vége: init +3 vector<int> c(5); // üres vektor // a deque elemek négyzete transform(f.begin(), f.end(), c.begin(), Negyzet<int>()); for (deque<int>::iterator i=f.begin(); i<f.end(); i++) cout<<(*i)<<" "; // 3 5 7 cout<<endl; for (vector<int>::iterator i=c.begin(); i<c.end(); i++) cout<<(*i)<<" "; // 9 25 49 0 0 cout<<endl;
Függvényobjektumok Kétoperandusú esetben (binary function): template <class T> class Add{ public: T operator() (T a, T b) { return a+b; } }; double A[5]={ 2, 3, 6, 9, 35 }; vector<double> B(5); fill(b.begin(), B.end(), 10); deque<double> C(5); transform(begin(a), end(a), B.begin(), C.begin(), Add<double>()); for (double e : C) cout << e << " "; cout << endl; //12 13 16 19 45
Függvényobjektumok Előre definiált függvényobjektumok: #include <functional> int adatok[5] = { 35, 1, 3, 12, 23 }; sort(begin(adatok), end(adatok), greater<int>()); //35 23 12 3 1 vector<int> B(5); vector<int> C(5); iota(begin(b), end(b), 1);// 1 2 3 4 5 transform(begin(adatok), end(adatok), B.begin(), C.begin(), plus<int>()); for (double e : C) cout << e << " "; cout << endl; //36 25 15 7 6
Függvényobjektumok Alkalmazhatunk lambda-kifejezéseket is, amelyek névtelen függvényobjektumok vector<double> szamok{ 2, 3, 6, 9, 35 }; list<double> lista(5); for (double e : szamok) cout << e <<" "; cout << endl; for_each(begin(szamok), end(szamok), [](double &x)-> double {return x*x; }); double szum = 0; for_each(begin(szamok), end(szamok), [&szum](double x){szum += x; }); cout << szum << endl; transform(szamok.begin(), szamok.end(), lista.begin(), [](double x) -> double{ return x*x; }); for_each(lista.begin(), lista.end(), [](double x){cout << x << " "; });
Összetettebb példa: class Hallgato { public: Hallgato(string nev,string neptun,int kor=19):nev(nev),neptun(neptun),kor(kor) {} string Nev() const { return nev; } string Neptun() const { return neptun; } int Kor() const { return kor; } bool operator < (const Hallgato & h) { return nev < h.nev; } private: string nev, neptun; int kor; }; deque<hallgato> Tankor; Tankor.push_back(Hallgato("Kiss Petra", "B0SIFJ")); Tankor.push_back(Hallgato("Nagy Peter", "FG45TH")); Tankor.push_back(Hallgato("Kovacs Vilmos", "ASA47G")); Tankor.push_back(Hallgato("Molnar Kata", "DFRH7K")); string nev = "Kiss Petra"; bool KeresNev(const Hallgato & a) { return a.nev() == nev; } deque<hallgato>::iterator di; di = find_if(tankor.begin(), Tankor.end(), KeresNeptun("FG45TH")); if (di!= Tankor.end()) cout << di->nev() << endl; class KeresNeptun { public: string neptun; KeresNeptun(string neptun) { this->neptun = neptun; } bool operator ()(const Hallgato & h) { return neptun == h.neptun(); } }; string nept = "DFRH7K"; di = find_if(tankor.begin(), Tankor.end(), [nept](hallgato &h) -> bool {return h.neptun() == nept; }); if (di!= Tankor.end()) cout << di->nev() << endl;
Ismétlés Mi a kimenet? int tomb[] = { 5,6,7,7,6,5,5,6 }; deque<int> v(tomb + 2, tomb + 8); sort(v.begin(), v.end()); v.empty(); v.push_front(8); for (auto elem : v) elem += 10; deque<int>::reverse_iterator it = v.rbegin(); while (it!= v.rend() - 1) { cout << *it << " "; it++; }