{{page>courses:b6b36pjc:styles#common&noheader&nofooter}} {{page>courses:b6b36pjc:styles#ukoly&noheader&nofooter}} ====== Flatset ====== Poslední povinný domácí úkol bude implementace datové struktury, které se říká __flatset__, v STL stylu. Výhoda flatsetu oproti ''std::set'' je rychlejší výhledávání i iterace seřazených prvků, nevýhoda pak je, že vkládání a mazání prvků je časově náročnější. Interně ''flat_set'' funguje tak, že si udržuje seřazené pole obsahující všechny prvky. To znamená, že pokud hledáme nějaký prvek, můžeme použít binární vyhledávání. Znamená to ale taky to, že asymptotická složitost ''flat_set::insert'' i ''flat_set::erase'' je lineární a ne logaritmická jako u ''std::set::insert'', respektive ''std::set::erase''((Tudíž ''flat_set'' je vhodný pro případy, kdy se bude často hledat a iterovat, ale nevhodný pro případy, kdy se často mění jeho obsah)). ===== Implementace ===== Nebudeme po vás chtít implementaci, která kompletně kopíruje rozhraní ''std::set'', stačí podmnožina popsaná tímto útržkem: template class flat_set { public: using iterator = ...; using const_iterator = ...; using size_type = ...; using value_type = ...; // Speciální member funkce se chovají běžným stylem flat_set(); flat_set(Comparator const& cmp); flat_set(flat_set const& rhs); flat_set(flat_set && rhs); flat_set& operator=(flat_set const& rhs); flat_set& operator=(flat_set && rhs); ~flat_set(); // Konstruktory co vytvoří flat_set z prvků definovaných jako // [first, last). Alokují pouze jednou pokud je to možné. template flat_set(InputIterator first, InputIterator last); template flat_set(InputIterator first, InputIterator last, Comparator const& cmp); // Vloží prvek do setu, buďto pomocí kopie, nebo pomocí přesunu. std::pair insert(T const& v); std::pair insert(T&& v); // Vloží prvky z [first, last), alokuje pouze jednou pokud je to možné template void insert(InputIterator first, InputIterator last); // Smaže prvek na který ukazuje i, vrátí iterátor k dalšímu prvku iterator erase(const_iterator i); // Smaže všechny prvky z [from, to), vrátí iterátor k dalšímu prvku iterator erase(const_iterator from, const_iterator to); // Iterátory předané dovnitř erase odkazují dovnitř setu. // Smaže prvek rovný klíči pokud existuje. // Vrátí kolik prvků bylo smazáno size_type erase(value_type const& key); // Běžné funkce k vytvoření iterátorů iterator begin() noexcept; iterator end() noexcept; const_iterator begin() const noexcept; const_iterator end() const noexcept; const_iterator cbegin() const noexcept; const_iterator cend() const noexcept; bool empty() const; size_type size() const; size_type capacity() const; // Zajistí, aby se do instance flat_setu dalo vložit c prvků // aniž by byla potřeba realokace void reserve(size_type c); // Vymaže všechny prvky ze setu void clear(); // Vrátí iterátor ukazující na prvek ekvivalentní s v, nebo end(), // pokud takový prvek není uvnitř setu iterator find(T const& v); const_iterator find(T const& v) const; // Vrátí iterátor k prvnímu prvku, který není menší nežli t, // nebo end() pokud takový prvek neexistuje. iterator lower_bound(T const& t); const_iterator lower_bound(T const& t) const; // Vrátí iterátor k prvnímu prvku, který je větší nežli t, // nebo end() pokud takový prvek neexistuje. iterator upper_bound(T const& t); const_iterator upper_bound(T const& t) const; // Prohodí obsah dvou setů (včetně komparátoru) void swap(flat_set& o); }; // porovnání probíhá lexikograficky bool operator==(flat_set const& a, flat_set const& b); bool operator!=(flat_set const& a, flat_set const& b); bool operator< (flat_set const& a, flat_set const& b); bool operator<=(flat_set const& a, flat_set const& b); bool operator>=(flat_set const& a, flat_set const& b); bool operator> (flat_set const& a, flat_set const& b); // Prohodí obsah dvou setů (včetně komparátoru) template void swap(flat_set const& a, flat_set const& b); Pokud není specifikováno, jaký komparátor má ''flat_set'' použít, používá se ''operator<''. Potřebné hlavičkové soubory a testy ke stažení {{:courses:b6b36pjc:ukoly:flatset.zip|zde}}. Odevzdejte hlavičkový soubor ''flatset.hpp'', ve kterém implementujete ''flat_set''. ===== Rady ===== Jak můžete vidět na bodovém ohodnocení, tento úkol je míněn jako signifikantně jednodušší než ostatní dva velké úkoly. To ovšem platí pouze v případě, že na implementaci půjdete chytře -- doporučujeme tedy začít rozmyšlením si, jak si implementaci co nejvíce zjednodušit((Pro zajímavost, soubor s referenčním řešením má 150 řádků)). Pokud použijete přiložený CMakeLists.txt, budou se vám kompilovat dvě sady testů, "basics" a "full". Testy z "basics" sady slouží jako rychlá kontrola funkcionality během vývoje, zatímco testy ze sady "full" pak jsou ty testy, na kterých bude váš flatset kontrolován v Brute. ==== Porovnání prvků ==== ''flat_set'' má k dispozici buďto ''operator<'', nebo speciální komparátor, který se stejným způsobem chová. To znamená, že se nemůžete spolehnout na existenci ''operator=='' u vašeho prvku, a musíte si vystačit s ''<''. Jedna z možností je předpokládat, že \begin{align*} a = b &\iff \big(\neg (a < b) \wedge \neg (b < a)\big) \\ \end{align*} ==== Defaultní argumenty pro argumenty šablon ==== Podobně jako u argumentů funkce, argumenty šablon mohou mít výchozí hodnoty, které se použijí, pokud uživatel explicitně nespecifikuje jinou: #include template struct tester { static bool equal() { return a == b; } }; int main() { std::cout << tester<2>::equal() << '\n'; // 1 std::cout << tester<1, 1>::equal() << '\n'; // 1 } ==== Range constructor ==== Range constructor, tj. ''flat_set(Iterator first, Iterator last)'', by měl mít algoritmickou složitost O(N * log N). Pokud ho implementujete pomocí range insertu, získáte ve výsledku insertion sort, který má složitost kvadratickou. Vhodným postupem může být prvky zkopírovat, setřídit a odstranit duplicity (máme set, ne multiset). ==== Užitečné hlavičky ==== Silně doporučujeme prohlédnout si * [[http://en.cppreference.com/w/cpp/header/vector|]] * [[http://en.cppreference.com/w/cpp/header/algorithm|]] * [[http://en.cppreference.com/w/cpp/header/functional|]] ===== Hodnocení ===== ^ **Sekce** ^ Body ^ | Základní funkcionalita | 2.5 | | Správné zacházení s referencemi | 0.5 | | Správná algoritmická složitost operací((Vyžaduje optimalizovaný range constructor)) | 0.5 | | Optimalizovaný insert dle kategorie iterátorů | 0.5 | | Optimalizovaný konstruktor dle kategorie iterátorů | 0.5 | | Optimalizované implementace range konstruktoru | 0.5 | Připomínáme, že z úlohy musíte získat alespoň polovinu možných bodů, tj. alespoň 2.5.