队列(Queues)
栈
栈可以保证元素存入和取出的顺序是后进先出(last-in first-out, LIFO)的。
public struct Stack<T> {
fileprivate var array = [T]()
public var isEmpty: Bool {
return array.isEmpty
}
public var count: Int {
return array.count
}
public mutating func push(_ element: T) {
array.append(element)
}
public mutating func pop() -> T? {
return array.popLast()
}
public var top: T? {
return array.last
}
}
关于栈的有趣事情:每次调用函数或方法,CPU都会将函数返回地址压入运行栈中。当这个函数执行结束的时候,CPU将返回地址从栈中取出,并据此返回到函数被调用的位置。所以。如果不断地调用太多的函数(例如死递归函数),就会得到一个所谓的“栈溢出(stack overflow)”错误,因为CPU运行栈没有空间了。
队列
队列可以保证元素存入和取出的顺序是先进先出(first-in first-out, FIFO)的。
这是一个简单粗暴的队列实现。
public struct Queue<T> {
fileprivate var array = [T][]
public var isEmpty: Bool {
return array.isEmpty
}
public var count: Int {
return array.count
}
public mutating func enqueue(_ element: T) {
array.append(element)
}
public mutating func dequeue() -> T? {
if isEmpty {
return nil
} else {
return array.removeFirst()
}
}
public var front: T? {
return array.first
}
}
入队操作是O(1)的,出队操作是O(n)的。
更加高效的队列
public struct Queue<T> {
fileprivate var array = [T?]()
fileprivate var head = 0
public var isEmpty: Bool {
return count == 0
}
public var count: Int {
return array.count - head
}
public mutating func enqueue(_ element: T) {
array.append(element)
}
public mutating func dequeue() -> T? {
guard head < array.count, let element = array[head] else { return nil }
array[head] = nil
head += 1
let percenttage = Double(head)/Double(array.count)
if array.count > 50 && percentage > 0.25 {
array.removeFirst(head)
head = 0
}
return element
}
public var front: T? {
if isEmpty {
return nil
} else {
return array[head]
}
}
}
在将队首元素出队时,我们首先将 array[head] 设置为 nil 来讲这个元素从数组中移除。然后将 head 的值加一,使得下一个元素变成新的队首值。
双端队列
一个基本实现
public struct Deque<T> {
private var array = [T]()
public var isEmpty: Bool {
return array.isEmpty
}
public var count: Int {
return array.count
}
public mutating func enqueue(_ element: T) {
array.append(element)
}
public mutating func enqueueFront(_ element: T) {
array.insert(element, atIndex: 0)
}
public mutating func dequeue() -> T? {
if isEmpty {
return nil
} else {
return array.removeFrist()
}
}
public mutating func dequeueBack() -> T? {
if isEmpty {
return nil
} else {
return array.removeLast()
}
}
public func peekFront() -> T? {
return array.first
}
public func peekBack() -> T? {
return array.last
}
}
dequeue() 和 enqueueFront() 的时间复杂度是O(n),因为它们在数组的前面工作。
更高效的版本
public struct Deque<T> {
private var array: [T?]
private var head: Int
private var capacity: Int
private let originalCapacity: Int
public init(_ capacity: Int = 10) {
self.capacity = max(capacity, 1)
originalCapacity = self.capacity
array = [T?](repeating: nil, count: capacity)
head = capacity
}
public var isEmpty: Bool {
return count == 0
}
public var count: Int {
return array.count - head
}
public mutating func enqueue(_ element: T) {
array.append(element)
}
public mutating func enqueueFront(_ element: T) {
if head == 0 {
capacity *= 2
let emptySpace = [T?](repeating: nil, count: capacity)
array.insert(contentsOf: emptySpace, at: 0)
head = capacity
}
head -= 1
array[head] = element
}
public mutating func dequeue() -> T? {
guard head < array.count, let element = array[head] else { return nil }
array[head] = nil
head += 1
if capacity >= originalCapacity && head >= capacity*2 {
let amountToRemove = capacity + capacity/2
array.removeFirst(amountToRemove)
head -= amountToRemove
capacity /= 2
}
return element
}
public mutating func dequeueBack() -> T? {
if isEmpty {
return nil
} else {
return array.removeLast()
}
}
public func peekFront() -> T? {
if isEmpty {
return nil
} else {
return array[head]
}
}
public func peekBack() -> T? {
if isEmpty {
return nil
} else {
return array.last!
}
}
}
优先级队列
优先级队列是一种队列,其中最重要的元素始终位于前面。
可以从优先级队列中受益的算法:
- 事件驱动的模拟。每个事件都有一个时间戳,您希望按照时间戳的顺序执行事件。优先级队列可以轻松找到需要模拟的下一个事件。
- Dijkstra 的图搜索算法使用优先级队列来计算最低成本。
- 霍夫曼编码用于数据压缩。该算法构建压缩树。它反复需要找到具有最小频率且尚未具有父节点的两个节点。
- 用于人工智能的A*寻路。
- 很多其他地方!
基于堆的Swift优先级队列:
public struct PriorityQueue<T> {
fileprivate var heap: Heap<T>
public init(sort: (T, T) -> Bool) {
heap = Heap(sort: sort)
}
public var isEmpty: Bool {
return heap.isEmpty
}
public var count: Int {
return heap.count
}
public var peek() -> T? {
return heap.peek()
}
public mutating func enqueue(element: T) {
heap.insert(element)
}
public mutating func dequeue() -> T? {
return heap.remove()
}
public mutating func changePriority(index i: Int, value: T) {
return heap.replace(index: i, value: value)
}
}
环形缓冲区
也被称为循环缓冲区。
这是一个概念性地回绕到开头的数组,因此您永远不必删除任何项目。所有操作都是O(1)。
Swift的一个简单实现
public struct RingBuffer<T> {
fileprivate var array: [T?]
fileprivate var readIndex = 0
fileprivate var writeIndex = 0
public init(count: Int) {
array = [T?](repeating: nil, count: count)
}
public mutating func write(_ element: T) -> Bool {
if !isFull {
array[writeIndex % array.count] = element
writeIndex += 1
return true
} else {
return false
}
}
public mutating func read() -> T? {
if !isEmpty {
let element = array[readIndex % array.count]
readIndex += 1
return element
} else {
return nil
}
}
fileprivate var availableSpaceForReading: Int {
return writeIndex - readIndex
}
public var isEmpty: Bool {
return availableSpaceForReading == 0
}
fileprivate var availableSpaceForWriting: Int {
return array.count - availableSpaceForReading
}
public var isFull: Bool {
return availableSpaceForWriting == 0
}
}
RingBuffer对象有一个数组用于实际存储数据,readIndex 和 writeIndex 变量用于指向数组的“指针”。wirte() 函数将新元素放入writeIndex 中的数组中,read() 函数返回 readIndex中的元素。
使用的时候是这样的:
var buffer = RingBuffer<Int>(count: 5)
buffer.write(123)
buffer.write(456)
buffer.write(789)
buffer.write(666)
buffer.read() // 123
buffer.read() // 456
buffer.read() // 789
buffer.write(333)
buffer.wirte(555)
buffer.read() // 666
buffer.read() // 333
buffer.read() // 555
buffer.read() // nil
环形缓冲区可以创建更优的队列,但它也有一个缺点:包装使得调整队列大小变得棘手。不过如果固定大小的队列适合你的目的,那就非常合适了。
当数据生产者以不同于数据使用者读取数据的速率写入数组时,环形缓冲区也非常有用。这通常发生在文件或网络I/O上。环形缓冲区也是高优先级线程 (例如音频渲染回调) 与系统其他较慢部分之间通信的首选方式。