530 likes | 634 Views
Generic Positional Containers and Double-Ended Queues. Generic Positional Containers. A generic container C<T> which is Organized and accessed by position vector[i] = ‘a’; list.insert(I, ‘a’); Can insert any T object at any position in C<T>
E N D
Generic Positional Containers • A generic container C<T> which is • Organized and accessed by position • vector[i] = ‘a’; • list.insert(I, ‘a’); • Can insert any T object at any position in C<T> • Can remove any T object at any position in C<T> • Can support • push_front(), pop_front(), front() • push_back(), pop_back(), back() • Examples: List, Deque
Double-Ended Queue • Deque (pronounced ‘Deck’) • Deque operations • Push/Pop at either end • Retrieve data from either end • Proper type
Specifying Deque<T> • Requirements for Deque • O(1)average runtime, O(size()) space • push_front(t), pop_front(), front() • push_back(t), pop_back(), back() • O(1) time and space for iterator operations • Random access iterator ([])
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]
begin end Deque<T> D Illustrated • content_size = 8 • D.empty() == true content
begin end Deque<char> D Illustrated (2) • content_size = 8 • D.push_back(‘M’) content
begin end Deque<char> D Illustrated (3) • content_size = 8 • D.push_back(‘e’) content
begin end Deque<char> D Illustrated (4) • content_size = 8 • D.push_back(‘r’) content
begin end Deque<char> D Illustrated (5) • content_size = 8 • D.push_back(‘r’) content
begin end Deque<char> D Illustrated (6) • content_size = 8 • D.push_back(‘y’) content
begin end Deque<char> D Illustrated (7) • content_size = 8 • D.pop_front() • O(1) content
begin end Deque<char> D Illustrated (8) • content_size = 8 • D.pop_front() content
begin end Deque<char> D Illustrated (9) • content_size = 8 • D.push_back(‘G’) content
begin end Deque<char> D Illustrated (10) • content_size = 8 • D.push_back(‘o’) • D.size() == (7 – 2 + 8) % 8 content
begin end Deque<char> D Illustrated (11) • content_size = 8 • D.push_back(‘A’) • D.size() = (0 – 2 + 8) % 8 content
begin end Deque<char> D Illustrated (12) • content_size = 8 • D.push_back(‘r’) • D.size() = (1 – 2 + 8) % 8 content
begin end Deque<char> D Illustrated (13) • D.size() == content_size – 1 • Now what? • Return full or • Double the capacity (as with Vector). content
Deque<T> Implementation Plan (2) • 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
Deque<T>::iterator Implementation Plan • Public interface • Start with the public interface of List<T>::iterator • Add bracket operator
Defining Deque<T> template <typename T> class Deque { public: typedef T value_type; // type definitions typedef DequeIterator<T> iterator; Deque(); // constructors and deconstructor Deque(size_t, const T&); Deque(const Deque<T>&); ~Deque(); // display functions void Display(ostream& os, char ofc = ‘\0’) const; void Dump(ostream& os) const;
Defining Deque<T> (2) int empty() const; // container read-only routines size_t size() const; T& front() const; T& back() const; T& operator[] (size_t) const; int push_front(const T&); // container write routines int pop_front(); int push_back(const T&); int pop_back(); Deque<T>& operator=(const Deque<T>&);
Defining Deque<T> (3) friend class DequeIterator<T>; // iterator support iterator begin() const; iterator end() const; protected: T* content; size_t content_size, begin, end; };
Defining Deque<T> (4) // 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>&);
Defining DequeIterator<T> template <typename T> class DequeIterator { friend class Deque<T>; public: typedef T value_type; // terminology support DequeIterator(); // constructors DequeIterator(const Deque<T>& I); DequeIterator(const DequeIterator<T>& I); DequeIterator(const size_t& i); T& retrieve() const; // return ptr to current Tval int valid() const; // cursor is valid element
Defining DequeIterator<T> (2) // various operators int operator==(const DequeIterator<T>& I2) const; int operator!=(const DequeIterator<T>& I2) const; T& operator*() const; // return reference to current Tval T& operator[] (size_t i) const; //return reference to Tval at index+i DequeIterator<T>& operator=(const DequeIterator<T>& I); DequeIterator<T>& operator++(); // prefix DequeIterator<T> operator++(int); // postfix DequeIterator<T>& operator--(); // prefix DequeIterator<T> operator--(int); // postfix
Defining DequeIterator<T> (3) // pointer arithmetic long operator-(const DequeIterator<T>& I2) const; DequeIterator<T>& operator+=(long n); DequeIterator<T>& operator-=(long n); DequeIterator<T> operator+(long n) const; DequeIterator<T>& operator+=(int n); DequeIterator<T>& operator-=(int n); DequeIterator<T> operator+(int n) const; DequeIterator<T>& operator+=(unsigned long n); DequeIterator<T>& operator-=(unsigned long n); DequeIterator<T> operator+(unsigned long n) const; DequeIterator<T>& operator+=(unsigned int n); DequeIterator<T>& operator-=(unsigned int n); DequeIterator<T> operator+(unsigned int n) const;
Defining DequeIterator<T> (3) protected: const Deque<T>* Qptr; size_t index; };
Implementing Deque<T> • Default constructor template <typename T> Deque<T>::Deque() : content(0), begin(0), end(0), content_size(0) { content = new T[default_content_size]; if (content == 0) { // error } content_size = default_content_size; }
Implementing Deque<T> (2) • Copy constructor 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]; if (content == 0) { // error } for (size_t j = 0; j < content_size; j++) { content[j] = Q.content[j]; } }
Implementing Deque<T> (3) • Read-only functions template <typename T> size_t Deque<T>::size() const { return (end – begin + content_size) % content_size; } template <typename T> T& Deque<T>::operator[] (size_t i) const { if (size() <= i) { // error } return content[(i + begin) % content_size]; }
Implementing Deque<T> (4) • 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); if (ofc != ‘\0’) { os << ofc; } } } template <typename T> void Deque<T>::Dump(ostream& os) const { for (size_t j = 0; j < content_size; ++j) { // print } }
Implementing Deque<T> (5) • Read-only 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>& Q1, const Deque<T>& Q2) { if (Q1.size() != Q2.size()) { return 0; } for (size_t j = 0; j < Q1.size(); ++j) { if (Q1[j] != Q2[j]) { return 0; } } return 1; }
Implementing Deque<T> (6) • Read-only operator overloads template <typename T> int operator!=(const Deque<T>& Q1, const Deque<T>& Q2) { return !(Q1 == Q2); }
Implementing Deque<T> (7) • Read-only functions template <typename T> int Deque<T>::empty() const { return begin == end; } template <typename T> void Deque<T>::Clear() { begin = end = 0; }
Implementing Deque<T> (8) • Read-only functions template <typename T> T& Deque<T>::front() const { // check for empty Tdeque… return content[begin]; } template <typename T> T& Deque<T>::back() const { // check for empty Tdeque… if (end == 0) return content[content_size - 1]; return content[end - 1]; }
Implementing Deque<T> (9) • Iterator support template <typename T> DequeIterator<T> Deque<T>::begin() const { Deque<T>::iterator I; I.Qptr = this; I.index = 0; return I; } template <typename T> DequeIterator<T> Deque<T>::end() const { Deque<T>::iterator I; I.Qptr = this; I.index = size(); return I; }
Implementing Deque<T> (10) • Assignment 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; }
Implementing Deque<T> (11) • push_back 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]; }
Implementing Deque<T> (12) • push_back if (end < begin) {end += content_size; } delete[] content; content = newcontent; content_size = newcontent_size; } content[end] = Tval; end = (end + 1) % content_size; return 1; }
Implementing Deque<T> (13) • push_front 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]; }
Implementing Deque<T> (14) • push_front if (begin < end) { begin += content_size; } delete[] content; content = newcontent; content_size = newcontent_size; } begin = (begin – 1 + content_size) % content_size; content[begin] = Tval; return 1; }
Implementing Deque<T> (15) • Pop routines 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; }
Implementing DequeIterator<T> • Constructors template <typename T> DequeIterator<T>::DequeIterator() : Qptr(0), index(0) { } template <typename T> DequeIterator<T>::DequeIterator(const Deque<T>& Q) : Qptr(&Q), index(0) { } template <typename T> DequeIterator<T>::DequeIterator(const DequeIterator<T>& I) : Qptr(I.Qptr), index(I.index) { }
Implementing DequeIterator<T> (2) • Initialization routines template <typename T> void DequeIterator<T>::Initialize(const Deque<T>& Q) { Qptr = &Q; index = 0; } template <typename T> void DequeIterator<T>::rInitialize(const Deque<T>& Q) { Qptr = &Q; index = Q.size() – 1; }
Implementing DequeIterator<T> (3) • Helper functions template <typename T> int DequeIterator<T>::valid() const { if (Qptr == 0) return 0; if (index >= Qptr->size()) return 0; return 1; } template <typename T> T& DequeIterator<T>::operator[] (size_t i) const { if (!Qptr) { // error } return Qptr->operator[](index + i); }
Implementing DequeIterator<T> (4) • Helper functions template <typename T> T& DequeIterator<T>::retrieve() const { // check for validity return Qptr->operator[](index); } template <typename T> T& DequeIterator<T>::operator* () const { if (Qptr == 0) { // error } if (Qptr->size() == 0) { // error } return Qptr->operator[](index); }
Implementing DequeIterator<T> (5) • Comparators template <typename T> int DequeIterator<T>::operator==(const DequeIterator<T>& I2) const { if (Qptr != I2.Qptr) return 0; if (index != I2.index) return 0; return 1; } template <typename T> int DequeIterator<T>::operator!=(const DequeIterator<T>& I2) const { return !(*this == I2); }
Implementing DequeIterator<T> (6) • Assignment template <typename T> DequeIterator<T>& DequeIterator<T>::operator=(const DequeIterator<T> & I) { if (this != &I) { Qptr = I.Qptr; index = I.index; } return *this; }
Implementing DequeIterator<T> (7) • Various operators template <typename T> DequeIterator<T>& DequeIterator<T>::operator++() { ++index; return *this; } template <typename T> DequeIterator<T> DequeIterator<T>::operator++(int) { DequeIterator<T> I(*this); operator ++(); return I; }