Generic Positional Containers and DoubleEnded Queues 1 Generic
Generic Positional Containers and Double-Ended Queues 1
Generic Positional Container • A generic container C<T> that is – Organized and accessed by position • The order of elements in container is determined by the order in which they are inserted into container – Sufficient to support either: • push_front(), pop_front(), front() or • push_back(), pop_back(), back() – Supporting associated iterators C<T>: : Iterator • is a “p. Container”, for short – Also known as “sequence containers” • Examples: Vector, List, Stack, Queue, and Deque 2
Double-Ended Queue • Deque (pronounced ‘Deck’) • Deque operations – Push/Pop at either end – Retrieve data from either end – Data of proper type • Assumptions on element type T (proper type) – Constructor T() and destructor ~T() – Copy constructor – Assignment operator= 3
Specifying Deque<T> • Requirements for Deque – O(1) average runtime, • push_front(t), pop_front(), front() • push_back(t), pop_back(), back() – O(size()) space – O(1) time and space for iterator operations – Random access iterator (for typical array-based implementation) • Bracket operator ([ ]), also known as • Pointer arithmetic 4
Deque<T> Implementation Plan • Circular array – Protected array content of size content_size – Content wraps around the end of the array to the beginning. – Illusion: content[content_size] == content[0] • Relative Indexing – Protected integers • begin, end – Bracket [] Operator • • Similar to Vector Element position relative to begin Front element is content[begin] Back element is content[end – 1] – Size is (end – begin + content_size) % content_size 5
Deque<T>: : iterator Implementation Plan • Public interface – Start with the public interface like that of List<T>: : iterator • i. e. like the iterator for vector or linked list – Add bracket operator 6
Deque<T> D Illustrated • content_size = 8 • D. empty() == true 0 1 2 3 4 5 6 7 content begin end 7
Deque<char> D Illustrated (2) • content_size = 8 • D. push_back(‘M’) 0 content 1 2 3 4 5 6 7 M begin end 8
Deque<char> D Illustrated (3) • content_size = 8 • D. push_back(‘e’) content 0 1 M e 2 3 4 5 6 7 begin end 9
Deque<char> D Illustrated (4) • content_size = 8 • D. push_back(‘r’) content 0 1 2 M e r 3 4 5 6 7 begin end 10
Deque<char> D Illustrated (5) • content_size = 8 • D. push_back(‘r’) content 0 1 2 3 M e r r 4 5 6 7 begin end 11
Deque<char> D Illustrated (6) • content_size = 8 • D. push_back(‘y’) content 0 1 2 3 4 M e r r y 5 6 7 begin end 12
Deque<char> D Illustrated (7) • content_size = 8 • D. pop_front() • O(1) 0 content 1 2 3 4 e r r y 5 6 7 begin end 13
Deque<char> D Illustrated (8) • content_size = 8 • D. pop_front() 0 content 1 2 3 4 r r y 5 6 7 begin end 14
Deque<char> D Illustrated (9) • content_size = 8 • D. push_back(‘G’) 0 content 1 2 3 4 5 r r y G 6 7 begin end 15
Deque<char> D Illustrated (10) • content_size = 8 • D. push_back(‘o’) • D. size() == (7 – 2 + 8) % 8 0 content 1 2 3 4 5 6 r r y G o 7 begin end 16
Deque<char> D Illustrated (11) • content_size = 8 • D. push_back(‘A’) • D. size() = (0 – 2 + 8) % 8 0 content 1 2 3 4 5 6 7 r r y G o A begin end 17
Deque<char> D Illustrated (12) • content_size = 8 • D. push_back(‘r’) • D. size() = (1 – 2 + 8) % 8 0 content 1 r 2 3 4 5 6 7 r r y G o A begin end 18
Deque<char> D Illustrated (13) • D. size() == content_size – 1 • Now what? – Return full or – Double the capacity (as with Vector). 0 content 1 r 2 3 4 5 6 7 r r y G o A begin end 19
Defining Deque<T> template <typename T> class Deque { public: // type definitions typedef T value_type; typedef Deque. Iterator<T> iterator; // constructors, destructor Deque(); Deque(size_t, const T&); Deque(const Deque<T>&); Deque(Deque<T>&&); ~Deque(); // copy // move // member operators Deque<T>& operator = (const Deque<T>&); Deque<T>& operator=(Deque<T>&&); T& operator [] (size_t) const; // copy assignment // move assignment // generic display methods void Display (ostream& os, char ofc = ‘ ') const; void Dump (ostream& os) const; 20
Defining Deque<T> (2) // Container class protocol int Empty () const; size_t Size () const; int push_front (const T&); int pop_front (); int push_back (const T&); int pop_back (); void Clear (); T& Front () const; T& Back () const; // and move version of push_front and push_back // iterator support friend class Deque. Iterator<T>; iterator begin() const; iterator end() const; protected: // classic circular array implementation T* content; size_t content_size, begin, end; }; 21
Defining Deque<T> (3) // operator overloads (friend status not required) template<class T> ostream& operator<<(ostream& os, const Deque<T>& a); template<class T> int operator==(const Deque<T>&, const Deque<T>&); template<class T> int operator!=(const Deque<T>&, const Deque<T>&); 22
Defining Deque. Iterator<T> template <typename T> class Deque. Iterator { friend class Deque<T>; public: // terminology support typedef T value_type; // constructors Deque. Iterator(); Deque. Iterator(const Deque<T>& Q); Deque. Iterator(const Deque. Iterator<T>& I); // information/access T& retrieve() const; // return ptr to current Tval int valid() const; // cursor is valid element 23
Defining Deque. Iterator<T> (2) // various operators int operator==(const Deque. Iterator<T>& I 2) const; int operator!=(const Deque. Iterator<T>& I 2) const; T& operator*() const; // return reference to current Tval T& operator[] (size_t i) const; //return reference to Tval at index i Deque. Iterator<T>& operator=(const Deque. Iterator<T>& I); Deque. Iterator<T>& operator++(); // prefix Deque. Iterator<T> operator++(int); // postfix Deque. Iterator<T>& operator--(); // prefix Deque. Iterator<T> operator--(int); // postfix 24
Defining Deque. Iterator<T> (3) // pointer arithmetic long operator-(const Deque. Iterator<T>& I 2) const; Deque. Iterator<T>& operator+=(long n); Deque. Iterator<T>& operator-=(long n); Deque. Iterator<T> operator+(long n) const; Deque. Iterator<T>& operator+=(int n); Deque. Iterator<T>& operator-=(int n); Deque. Iterator<T> operator+(int n) const; Deque. Iterator<T>& operator+=(unsigned long n); Deque. Iterator<T>& operator-=(unsigned long n); Deque. Iterator<T> operator+(unsigned long n) const; Deque. Iterator<T>& operator+=(unsigned int n); Deque. Iterator<T>& operator-=(unsigned int n); Deque. Iterator<T> operator+(unsigned int n) const; 25
Defining Deque. Iterator<T> (3) // Initializers void Initialize (const Deque<T>& Q); void r. Initialize (const Deque<T>& Q); protected: const Deque<T>* Qptr; size_t index; }; 26
Implementing Deque<T> • Default constructor template <typename T> Deque<T>: : Deque() : content{nullptr}, begin{0}, end{0}, content_size{0} { content = new (nothrow) T[default_content_size]; if (content == nullptr) { // error } content_size = default_content_size; } static const size_t default_content_size = 10; // another way, using exception handling // try { // content = new T[default_content_size]; // } // catch (bad_alloc&) { // report error // } 27
Implementing Deque<T> (2) • Copy and move constructors template <typename T> Deque<T>: : Deque(const Deque<T>& Q) : content_size(Q. content_size), begin(Q. begin), end(Q. end) { content = new T[content_size]; // error handling if memory is not properly allocated. for (size_t j = 0; j < content_size; j++) { content[j] = Q. content[j]; } } Template <typename T> Deque<T>: : Deque(Deque<T> &&Q) : content_size(Q. content_size), begin(Q. begin), end(Q. end), content(Q. content) { Q. content = nullptr; Q. content_size = 0; Q. begin = Q. end = 0; } 28
Implementing Deque<T> (3) • Copy and move assignment opeators template <typename T> Deque<T>& Deque<T>: : operator=(const Deque<T>& Q) { if (this != &Q) { T* newcontent = new T[Q. content_size]; // check for allocation delete[] content; content = newcontent; content_size = Q. content_size; begin = Q. begin; end = Q. end; // copy queue elements } return *this; } template <typename T> Deque<T> & Deque<T>: : operator=(Deque<T>&& Q) { std: : swap(content, Q. content); std: : swap(content_size, Q. content_size); std: : swap(begin, Q. begin); std: : swap(end, Q. end); return *this; } 29
Implementing Deque<T> (4) • Index operator template <typename T> T& Deque<T>: : operator[] (size_t i) const { if (size() <= i) { // error } return content[(i + begin) % content_size]; } • Display functions template <typename T> void Deque<T>: : Display(ostream& os, char ofc) const { for (size_t j = 0; j < size(); ++j) { os << operator[](j); os << ofc; } } template <typename T> void Deque<T>: : Dump(ostream& os) const { for (size_t j = 0; j < content_size; ++j) { // print } } 30
Implementing Deque<T> (5) • operator overloads template <typename T> ostream operator<<(ostream& os, const Deque<T>& Q) { Q. Display(os); return(os); } template <typename T> int operator==(const Deque<T>& Q 1, const Deque<T>& Q 2) { if (Q 1. size() != Q 2. size()) { return 0; } for (size_t j = 0; j < Q 1. size(); ++j) { if (Q 1[j] != Q 2[j]) { return 0; } } return 1; } template <typename T> int operator!=(const Deque<T>& Q 1, const Deque<T>& Q 2) { return !(Q 1 == Q 2); } 31
Implementing Deque<T> (6) • Container class protocol template <typename T> size_t Deque<T>: : Size() const { return (end – begin + content_size) % content_size; } template <typename T> int Deque<T>: : empty() const { return begin == end; } template <typename T> void Deque<T>: : Clear() { begin = end = 0; } 32
Implementing Deque<T> (7) template <typename T> T& Deque<T>: : front() const { // check for empty deque… return content[begin]; } template <typename T> T& Deque<T>: : back() const { // check for empty deque… if (end == 0) return content[content_size - 1]; return content[end - 1]; // or return content[(end – 1 + content_size) % content_size]; } 33
Implementing Deque<T> (8) template <typename T> int Deque<T>: : push_back(const T& Tval) { if (size() + 1 >= content_size) { // deque is full unsigned j, k; size_t newcontent_size = 2 * content_size; if (content_size == 0) newcontent_size = 2; T* newcontent = new T[newcontent_size]; // check for allocation error for (j = k = begin; j != end; j = (j + 1) % content_size, ++k) { newcontent[k] = content[j]; } if (end < begin) {end += content_size; } delete[] content; content = newcontent; content_size = newcontent_size; } content[end] = Tval; end = (end + 1) % content_size; return 1; } How to implement the move version of the push_back function? 34
Implementing Deque<T> (9) template <typename T> int Deque<T>: : push_front(const T& Tval) { if (size() + 1 >= content_size) { // deque is full unsigned j, k; size_t newcontent_size = 2 * content_size; if (content_size == 0) newcontent_size = 2; T* newcontent = new T[newcontent_size]; // check for allocation error for (j = k = begin; j != end; j = (j + 1) % content_size, ++k) { newcontent[k] = content[j]; } if (end < begin) { end += content_size; } delete[] content; content = newcontent; content_size = newcontent_size; } begin = (begin – 1 + content_size) % content_size; content[begin] = Tval; return 1; } How to implement the move version of push_front()? 35
Implementing Deque<T> (10) template <typename T> int Deque<T>: : pop_front() { if (begin == end) return 0; begin = (begin + 1) % content_size; return 1; } template <typename T> int Deque<T>: : pop_back() { if (begin == end) return 0; end = (end – 1 + content_size) % content_size; return 1; } 36
Implementing Deque<T> (11) • Iterator support template <typename T> Deque. Iterator<T> Deque<T>: : begin() const { Deque<T>: : iterator I; I. Qptr = this; I. index = 0; return I; } template <typename T> Deque. Iterator<T> Deque<T>: : end() const { Deque<T>: : iterator I; I. Qptr = this; I. index = size(); return I; } 37
Implementing Deque. Iterator<T> • Constructors template <typename T> Deque. Iterator<T>: : Deque. Iterator() : Qptr{nullptr}, index{0} { } template <typename T> Deque. Iterator<T>: : Deque. Iterator(const Deque<T>& Q) : Qptr{&Q}, index{0} { } template <typename T> Deque. Iterator<T>: : Deque. Iterator(const Deque. Iterator<T>& I) : Qptr{I. Qptr}, index{I. index} { } 38
Implementing Deque. Iterator<T> (2) • Initialization routines template <typename T> void Deque. Iterator<T>: : Initialize(const Deque<T>& Q) { Qptr = &Q; index = 0; } template <typename T> void Deque. Iterator<T>: : r. Initialize(const Deque<T>& Q) { Qptr = &Q; index = Q. size() – 1; } 39
Implementing Deque. Iterator<T> (3) • Helper functions template <typename T> int Deque. Iterator<T>: : valid() const { if (Qptr == nullptr) return 0; if (index >= Qptr->size()) return 0; return 1; } template <typename T> T& Deque. Iterator<T>: : operator[] (size_t i) const { if (!Qptr) { // error } return Qptr->operator[](index + i); } 40
Implementing Deque. Iterator<T> (4) • Helper functions template <typename T> T& Deque. Iterator<T>: : retrieve() const { if (Qptr == nullptr) { // error } if (Qptr->size() == 0) { // error } return Qptr->operator[](index); } template <typename T> T& Deque. Iterator<T>: : operator* () const { // check for validity return Qptr->operator[](index); } 41
Implementing Deque. Iterator<T> (5) • Comparators template <typename T> int Deque. Iterator<T>: : operator==(const Deque. Iterator<T>& I 2) const { if (Qptr != I 2. Qptr) return 0; if (index != I 2. index) return 0; return 1; } template <typename T> int Deque. Iterator<T>: : operator!=(const Deque. Iterator<T>& I 2) const { return !(*this == I 2); } 42
Implementing Deque. Iterator<T> (6) • Assignment template <typename T> Deque. Iterator<T>& Deque. Iterator<T>: : operator=(const Deque. Iterator<T> & I) { if (this != &I) { Qptr = I. Qptr; index = I. index; } return *this; } 43
Implementing Deque. Iterator<T> (7) • Various operators template <typename T> Deque. Iterator<T>& Deque. Iterator<T>: : operator++() { // prefix ++index; return *this; } template <typename T> Deque. Iterator<T>: : operator++(int) { // postfix Deque. Iterator<T> I(*this); operator ++(); return I; } 44
Implementing Deque. Iterator<T> (8) • Various operators template <typename T> Deque. Iterator<T>& Deque. Iterator<T>: : operator--() { // prefix --index; return *this; } template <typename T> Deque. Iterator<T>: : operator--(int) { // postfix Deque. Iterator<T> I(*this); operator --(); return I; } 45
Implementing Deque. Iterator<T> (9) • Various operators template <typename T> long Deque. Iterator<T>: : operator-(const Deque. Iterator<T>& I 2) const { // return the distance between the two iterators return index – I 2. index; } template <typename T> Deque. Iterator<T>: : operator+(long n) const { // advance the iterator by n Deque. Iterator<T> I(*this); return I += n; } 46
Implementing Deque. Iterator<T> (10) • Various operators template <typename T> Deque. Iterator<T>& Deque. Iterator<T>: : operator+=(long n) { index += n; return *this; } template <typename T> Deque. Iterator<T>& Deque. Iterator<T>: : operator-=(long n) { index -= n; return *this; } 47
Reading assignment • Chapter 4 Trees 48
- Slides: 48