Queue • A container in which insertion is done at one end (the tail) and deletion is done at the other Containers: end (the head). Queue and List • Also called FIFO (First-In, First-Out)

Jordi Cortadella and Jordi Petit push pop Department of Computer Science (enqueue) Queue (dequeue)

Containers © Dept. CS, UPC 2 Queue usage The class Queue

template class Queue { Queue Q; // Constructor public:

Q.push(5); // Inserting few elements Queue(); // Constructor Q.push(8); ~Queue(); // Destructor Q.push(6); Queue(const T& Q); // Copy constructor int n = Q.size(); // n = 3 Queue& operator= (const Queue& Q); // Assignment operator

while (not Q.empty()) { void push(const& T x); // Enqueues an element int elem = Q.front(); // Get the first element void pop(); // Dequeues the first element cout << elem << endl; Q.pop(); // Delete the element T front() const; // Returns the first element } int size() const; // Number of elements in the queue bool empty() const; // Is the queue empty? };

Containers © Dept. CS, UPC 3 Containers © Dept. CS, UPC 4 Implementation with linked lists Implementation with linked lists first last first, last first, last first last

next next next next next Q.push(5) next Q.push(8) next next elem elem elem elem elem 5 5 8

first last template class Queue { next next next Q.push(6) private: struct Node { 5 8 6 T elem; Node* next; }; first last next next Node *first; // Pointer to the first element Q.pop() Node *last; // Pointer to the last element int n; // Number of elements 8 6

Containers © Dept. CS, UPC 5 Containers © Dept. CS, UPC 6 Queue: some methods Queue: constructors and destructor

/** Default constructor: an empty queue. */ /** Returns the number of elements. */ /** Removes the first element. Queue() : first(nullptr), last(nullptr), n(0) { } int size() const { Pre: the queue is not empty. */ return n; void pop() { /** Copy constructor. */ } assert(not empty()); Queue(const Queue& Q) { Node* old = first; copy(Q); /** Checks whether the queue is first = first->next; } empty. */ delete old; bool empty() const { if (--n == 0) last = nullptr; /** Assignment operator. */ return size() == 0; } Queue& operator= (const Queue& Q) { } if (&Q != this) { /** Returns the first element. free(); private: /** Inserts a new element at the end Pre: the queue is not empty. */ copy(Q); /** Frees the of of the queue. */ T front() const { } nodes in the queue. */ void push(const T& x) { assert(not empty()); return *this; void free() { Node* p = new Node {x, nullptr}; return first->elem; } Node* p = first; if (n++ == 0) first = last = p; } while (p) { else last = last->next = p; /** Destructor. */ Node* old = p; } ~Queue() { p = p->next; free(); delete old; } } }

Containers © Dept. CS, UPC 7 Containers © Dept. CS, UPC 8 Queue: copy (private) Implementation with circular buffer Q.first p1 • A queue can also be implemented with an array Q: (vector) of elements. first p2

*this: • It is a more efficient representation if the

/** Copies a queue. */ maximum number of elements in the queue is void copy(const Queue& Q) { known in advance. n = Q.n; if (n == 0) { first = last = nullptr; } else { Node* p1 = Q.first; 7 0 Node* p2 = first = new Node {p1->elem}; 0 1 2 3 4 5 6 7 6 1 while (p1->next) { p1 = p1->next; 5 2 p2 = p2->next = new Node {p1->elem}; 4 3 } p2->next = nullptr; last = p2; } } Containers © Dept. CS, UPC 9 Containers © Dept. CS, UPC 10 Implementation with circular buffer Implementation with circular buffer

read read a a

7 0 7 0 6 1 b 6 1 b 5 2 c 5 2 c 4 3 write 4 3 d e d

write

after Q.push(e)

Containers © Dept. CS, UPC 11 Containers © Dept. CS, UPC 12 Implementation with circular buffer Implementation with circular buffer

write c

7 0 read 7 0 6 1 b b 6 1

5 5 2 c a 2 write 4 3 read 4 3 e d

after Q.pop()

Containers © Dept. CS, UPC 13 Containers © Dept. CS, UPC 14 Implementation with circular buffer Queue: Complexity • All operations in queues can run in constant time, except for: c d – Copy: linear in the size of the list. 7 0 write – Delete: linear in the size of the list. b 6 1

5 a 2 • Queues do not allow to access/insert/delete read 4 3 elements in the middle of the queue.

after Q.push(d)

Containers © Dept. CS, UPC 15 Containers © Dept. CS, UPC 16 List List: graphical representation • List: a container with sequential access. first last 5 8 3 0 4 3 9 1 4 8 • It allows to insert/erase elements in the middle of the list in constant time. L.insert(7)

• A list can be considered as a sequence of elements 5 8 3 0 4 7 3 9 1 4 8 with one or several cursors (iterators) pointing at internal elements. L.move_right()

• For simplicity, we will only consider lists with one 5 8 3 0 4 7 3 9 1 4 8 iterator. L.erase() • Check the STL list: it can be visited by any number of iterators. 5 8 3 0 4 7 3 1 4 8

Containers © Dept. CS, UPC 17 Containers © Dept. CS, UPC 18 List implementation: doubly linked nodes The class List: private representation

5 8 3 0 4 9 1 4 8 template class List {

cursor /** Doubly linked node of the list. */ Sentinel struct Node { Node* prev; /** Pointer to the previous node. */  5 8 3 0 4 9 1 4 8 T elem; /** The element of the list. */ Node* next; /** Pointer to the next element. */ }; cursor: pointer at the first node after the cursor Node* sentinel; /** Sentinel of the list. */ Node* cursor; /** Node after the cursor. */ cursor int n; /** Number of elements (without sentinel). */

Empty list: 

Containers © Dept. CS, UPC 19 Containers © Dept. CS, UPC 20 The class List: public methods The class List: public methods

public: public: /** Constructor of an empty list. */ /** Checks whether the cursor is at the beginning of the list. */ List() : sentinel(new Node), cursor(sentinel), n(0) { bool is_at_front() const { sentinel->next = sentinel->prev = sentinel; return cursor == sentinel->next; } } /** Destructor. */ ~List() { /** Checks whether the cursor is at the end of the list. */ free(); bool is_at_end() const { } return cursor == sentinel; } /** Copy constructor. */ /** Returns the number of List(const List& L) { /** Moves the cursor one position backward. elements in the list. */ Pre: the cursor is not at the beginning of the list. */ copy(L); int size() const { } void move_backward() { return n; assert(not is_at_front()); /** Assignment operator. */ } cursor = cursor->prev; List& operator= (const List& L) { } if (&L != this) { /** Checks whether the list /** Moves the cursor one position forward. free(); is empty. */ copy(L); Pre: the cursor is not at the end of the list. */ } bool empty() const { void move_forward() { return *this; return size() == 0; assert(not is_at_end()); } } cursor = cursor->next; }

Containers © Dept. CS, UPC 21 Containers © Dept. CS, UPC 22 The class List: public methods The class List: public methods cursor public: public: cursor /** Moves the cursor to the a b c d /** Erases the element after the cursor. beginning of the list. */ Pre: cursor is not at the end. */ void move_to_front() { x void erase() { a b c d cursor = sentinel->next; assert(not is_at_end()); } Node* p = cursor; cursor cursor p->next->prev = p->prev; /** Moves the cursor to the cursor = p->prev->next = p->next; end of the list. */ delete p; a b c d void move_to_end() { a b c d --n; cursor = sentinel; } p } x /** Returns the element after the cursor. /** Inserts an element x before the cursor. */ Pre: the cursor is not at the end. */ void insert(const T& x) { p T front() const { Node* p = new Node {cursor->prev, x, cursor}; assert(not is_at_end()); cursor->prev = cursor->prev->next = p; return cursor->elem; ++n; } }

Exercises: implement the private methods copy() and free().

Containers © Dept. CS, UPC 23 Containers © Dept. CS, UPC 24 Higher-order functions Higher-order functions: example

• A higher-order function is a function that can receive template other functions as parameters or return a function as a class List { result. … /** Transforms every element of the list using f. It returns a reference to the list. */ • Most languages support higher-order functions List& transform(void f(T&)); (C++, python, R, Haskell, Java, JavaScript, …). /** Returns a list with the elements for which f is true */ List filter(bool f(const T&)) const; • The have different applications: – sort in STL is a higher-order function (the compare /** Applies f sequentially to the list and returns a function is a parameter). single value. For the list [풙ퟏ, 풙ퟐ, 풙ퟑ, … , 풙풏] it returns – functions to visit the elements of containers (lists, trees, 풇(… 풇 풇 init, 풙ퟏ , 풙ퟐ … , 풙풏). etc.) can be passed as parameters. If the list is empty, it returns init.*/ – Mathematics: functions for composition and integration T reduce(T f(const T&, const T&), T init) const; receive a function as parameter. } – etc…

Containers © Dept. CS, UPC 25 Containers © Dept. CS, UPC 26 Higher-order functions: example Higher-order functions: example

/** Checks whether a number is prime */ List& transform(void f(T&)) { bool isPrime(int n) {…} Node* p = sentinel->next; while (p != sentinel) { // Visit all elements and apply f to each one f(p->elem); /** Adds two numbers */ p = p->next; int add(int x, int y) { } return x + y; return *this; } } /** Substitutes a number by its square */ List filter(bool f(const T&)) const { void square(int& x) { List L; x = x*x; Node* p = sentinel->next; } while (p != sentinel) { // Pick elements only if f is asserted if (f(p->elem)) L.insert(p->elem); p = p->next; } /** The following code computes: return L; } ෍ 풙ퟐ T reduce(T f(const T&, const T&), T init) const { 풙∈푳, 풙 is prime T x = init; // Initial value Node* p = sentinel->next; // First element (if any) */ while (p != sentinel) { x = f(x, p->elem); // Composition with next element p = p->next; int n = L.filter(isPrime).transform(square).reduce(add, 0); } return x; }

Containers © Dept. CS, UPC 27 Containers © Dept. CS, UPC 28 Queues implemented as circular buffers • Design the class queue implemented with a circular buffer (using a vector): – The push/pop/front operations should run in constant time. – The copy and delete operations should run in linear time. – The class should have a constructor with a parameter 푛 that should indicate the maximum number of elements in the queue.

EXERCISES • Consider the design of a variable-size queue using a circular buffer. Discuss how the implementation should be modified.

Containers © Dept. CS, UPC 29 Containers © Dept. CS, UPC 30 Reverse and Josephus Merge sort

• Design the method reverse() that reverses the contents of the list: • Design the method merge(const List& L) that merges – No auxiliary lists should be used. the list with another list L, assuming that both lists are sorted. Assume that a pair of elements can be compared with the – No copies of the elements should be performed. operator <.

• Solve the Josephus problem, for 푛 people and • Design the method sort() that sorts the list according to executing every 푘-th person, using a circular list: the < operator. Consider merge sort and quick sort as possible algorithms. https://en.wikipedia.org/wiki/Josephus_problem

Containers © Dept. CS, UPC 31 Containers © Dept. CS, UPC 32