Data Structures and Algorithms

Stacks and Queues

Introduction

Stacks and queues are two data structures which support inserting and removing data from ends of the structure only.

Stack

A stack is something which supports insertion and removal in LIFO order: last in, first out. You can put things on top of the stack, and remove things from the top of the stack.

Examples

  • Web browser's "back" feature
  • Text editor's "undo" feature
  • Function calls in C++

Supported Methods

push(object)  // adds to the top of stack
pop()         // removes top element
object &top() // returns a reference to the top element
size()        // number of elements in the stack
empty()       // checks if the stack has no elements

Underlying Implementation

You can either use an array or a list to implement a stack. In the STL, it is implemented as a deque<>, which is made using a vector of vectors.

Queue

A queue supports insertion and removal in FIFO order: first in, first out. You can push from the back, and pop from the front.

Examples

  • Browsing history
    • New pages are added to history
    • Old pages removed in 30 days

Supported Methods

push(object)    // adds to the top of stack
pop()           // removes top element
object &front() // returns a reference to the top element
size()          // number of elements in the stack
empty()         // checks if the stack has no elements

Note the high level of similarity to the stack!

Underlying Implementation

Again, you could either use an array or a queue.

Arrays

If you used an array, you would have to allocate more memory and enqueue an element if the size was greater than the max capacity.

Linked List

\(O(1)\) insertions and deletions, but the one downside of using a singly-linked list is that the size() function takes \(O(n)\) time in the STL.

Deque

A double-ended queue. Can push and pop from both sides! Allows for efficient insertion and removal from the front and the back.

push_front()
push_back()
pop_front()
pop_back()
front()
back()

size()
empty()

It can also be traversed using an iterator.

Underlying Implementation

The deque is typically implemented either using a circular array, or a doubly-linked list. The doubly-linked list supports efficient removal, and other operations map directly to doubly-linked list operations.

Priority Queue

A priority queue is a type of queue where each datum is paired with a value. Priority values are usually numbers, but they can be anything. In any case, you should be able to compare priority values using the operator <. The priority queue supports insertion of data and inspection, and deletion of datum with the largest value.

Example

A group of bikers, where the fastest ones exit the race first.

Underlying Implementation

A priority queue is implemented in STL with a heap on top of any random access container that you choose. This could be a vector<>, but not a list<>.

Complexity:

Insert Remove max
Unsorted sequence container \(O(1)\) \(O(n)\)
Sorted sequence container \(O(n)\) \(O(1)\)
Heap \(\O(log n)\) \(\O(log n)\)
Array of linked lists (for priorities of small integers) O(1) O(1)

Choosing a Data Structure

Factors for consideration:

  • The right operations
  • The right behavior
  • The right trade-offs for runtime complexities
  • Memory overhead

Potential concern:

  • Limit the interface to avoid problems