Queues

Mar 7, 2018 · 3 minute read

TL;DR

API

Queues are a fundamental FIFO data structure. In their most basic implementation, a queue should support three operations:

protocol QueueType {
  associatedtype Element
  /// Number of elements in the queue
  var count: Int { get }
  /// Adds a new element to the back of the queue
  func enqueue(_ value: Element)
  /// Removes an element from the front of the queue
  func dequeue() -> Element?
}

Implementation

A possible implementation of the previous API, that aims to preserve the O(1) performance requirements of a Queue could be based on a Linked List as shown below:

class Queue<T>: QueueType {

    private var head: Node<T>?
    private var tail: Node<T>?

    // MARK: - QueueType

    var count: Int = 0

    func enqueue(_ value: T) {
        let node = Node(value: value)
        if head == nil {
            head = node
            tail = node
        } else {
            tail?.next = node
            tail = node
        }
        count += 1
    }

    @discardableResult
    func dequeue() -> T? {
        guard count > 0 else { return nil }
        let value = head?.value
        head = head?.next
        if head == nil { tail = nil }
        count -= 1
        return value
    }
}

private class Node<T> {
    var value: T
    var next: Node<T>?

    init(value: T, next: Node<T>? = nil) {
        self.value = value
        self.next = next
    }
}

Extensions

We could make the previous implementation more versatile and easier to use by conforming to a couple of protocols present in the standard library of swift:

extension Queue: Sequence {
    func makeIterator() -> AnyIterator<T> {
        var current = head
        return AnyIterator<T> {
            guard current != nil else { return nil }
            let next = current?.value
            current = current?.next
            return next
        }
    }
}

extension Queue: CustomDebugStringConvertible {
    var debugDescription: String {
        return "[\(map { "\($0)" }.joined(separator: ", "))]"
    }
}

The usage of this data structure can be made even more comfortable by adding conformance to ExpressibleByArrayLiteral. Unfortunately this cannot be done via extensions so a modification to the original implementation will be needed.

class Queue<T>: QueueType, ExpressibleByArrayLiteral {
    // ...
    required init(arrayLiteral elements: T...) {
        elements.forEach { enqueue($0) }
    }
    // ...
}

Usage

Finally, here is an example of how to use the Queue structure:

// This initialization style is possible thanks to the 
// conformance to "ExpressibleByArrayLiteral"
let queue: Queue<Int> = [1, 2, 3, 4, 5]

// Functions like map, flatMap and filter are all available 
// to us thanks to the conformance to "Sequence"
let squares = queue.map { $0 * $0 }

// We obtain debugger friendly output thanks to the
// conformance to "CustomDebugStringConvertible"
Swift.print(queue)