刷题笔记 - 栈与队列(Swift)

162 阅读5分钟

底部代码需要依赖的 Stack:

struct Stack<Element> {
    private var _content = [Element]()
    
    var isEmpty: Bool { return _content.isEmpty }
    var last: Element? { return _content.last }
    
    mutating func pop() -> Element? {
        return _content.popLast()
    }
    
    mutating func push(_ newElement: Element) {
        _content.append(newElement)
    }
}

底部代码需要依赖的 DeQueue:

struct DeQueue<Element> {
    private var _content = [Element]()
    
    var isEmpty: Bool { return _content.isEmpty }
    var first: Element? { return _content.first }
    var last: Element? { return _content.last }
    
    mutating func headOut() -> Element {
        guard !_content.isEmpty else {
            fatalError("empty queue")
        }
        return _content.removeFirst()
    }
    
    mutating func headIn(_ newElement: Element) {
        _content.insert(newElement, at: 0)
    }
    
    mutating func trailOut() -> Element {
        guard !_content.isEmpty else {
            fatalError("empty queue")
        }
        return _content.removeLast()
    }
    
    mutating func trailIn(_ newElement: Element) {
        _content.append(newElement)
    }
}

底部代码需要依赖的 Queue

最小栈

题解:

  • 创建两个栈,dataStack:用来存储数据;minStack:用来存储每一次进栈的最小值。
  • push:
    • 将 val 入栈到 dataStack。
    • 如果 minStack 为空,将 val 入栈到 minStack,如果不为空,则比较 val 与 minStack 栈顶元素的大小。
      • 大于:continue
      • 小于等于:将 val 入栈到 minStack
  • pop
    • 将 dataStack 栈顶元素出栈 - popLast
    • 比较 popLast 与 minStack 栈顶元素的大小:
      • 大于:continue
      • 等于:移除 minStack 的栈顶元素
  • getMin:获取 minStack 的栈顶元素。
  • top:获取 dataStack 的栈顶元素。

代码:

class MinStack {
   private var _dataStack = Stack<Int>()
   private var _minStack = Stack<Int>()
   
   init() { }
   
   func push(_ val: Int) {
       _dataStack.push(val)
       if _minStack.isEmpty {
           _minStack.push(val)
       } else {
           if let last = _minStack.last, val <= last {
               _minStack.push(val)
           }
       }
   }
   
   func pop() {
       let popLast = _dataStack.pop()
       if let popLast = popLast, let last = _minStack.last, popLast == last {
           _minStack.pop()
       }
   }
   
   func top() -> Int {
       return _dataStack.last!
   }
   
   func getMin() -> Int {
       return _minStack.last!
   }
}

两个栈模拟队列

题解:

  • 创建两个栈, inStack:存放入队的数据;outStack:存放出队的数据
  • 入队:将元素入栈到 inStack
  • 出队:先判断 inStack 和 outStack 是否都为空,为空则抛出异常。调用 inToOut 函数,移除 outStack 的栈顶元素
  • inToOut:先判断 outStack 是否为空,为空则代表需要从 inStack 导数据到 outStack。导数据的逻辑:只要 inStack 不为空,则 inStack 移除栈顶元素并入栈到 outStack 代码:
class CQueue {
    private var _inStack = Stack<Int>()
    private var _outStack = Stack<Int>()
    
    init() { }
    
    func appendTail(_ value: Int) {
        _inStack.push(value)
    }
    
    func deleteHead() -> Int {
        if _inStack.isEmpty && _outStack.isEmpty {
            return -1
        }
        inToOut()
        return _outStack.pop()!
        
    }
    
    private func inToOut() {
        if _outStack.isEmpty {
            while !_inStack.isEmpty {
                _outStack.push(_inStack.pop()!)
            }
        }
    }
}

使用递归函数和栈操作进行栈逆序

题解:

  • 定义两个递归函数,getAndRemoveLast:用于获取并移除栈底元素;reverse:逆序栈元素
  • getAndRemoveLast:获取并移除栈顶元素-top,若移除之后栈为空,则直接返回top。否则递归调用得到栈底元素-last,将 top 入栈,返回 last。
  • reverse:若栈为空直接返回。不为空,调用 getAndRemoveLast 获取栈底元素-last,递归调用,last 入栈。 代码:
struct ReverseStack {
    func reverse(stack: inout Stack<Int>) {
        if stack.isEmpty {
            return
        }
        let last = getAndRemoveLast(stack: &stack)
        reverse(stack: &stack)
        stack.push(last)
    }
    
    private func getAndRemoveLast(stack: inout Stack<Int>) -> Int {
        let top = stack.pop()!
        if stack.isEmpty {
            return top
        } else {
            let last = getAndRemoveLast(stack: &stack)
            stack.push(top)
            return last
        }
    }
}

猫狗队列

题解:

  • 创建一个新的结构:PetQueue 用来包装 Cat/Dog 对象,该结构主要添加 count 来识别入队实例的顺序。另一个目的则是不改变原有类的结构。
  • 创建两个队列,dogQueue:存放 PetQueue 包裹的 Dog 实例;catQueue:存放 PetQueue 包裹的 Cat 实例;
  • count: 当前入队实例的总数。
  • add:dog 实例,创建 PetQueue 对象放入 dogQueue;cat 实例,创建 PetQueue 对象放入 catQueue。
  • pollAll:比较 dogQueue 与 catQueue 对头的 count,谁的 count 小,谁出队。
  • pollDog:dogQueue 队头出队。
  • pollCat:catQueue 队头出队。
  • isEmpty:dogQueue catQueue 是否同时为空
  • isDogEmpty:dogQueue 是否为空
  • isCatEmpty:catQueue 是否为空

代码:

class Pet {
    let type: String
    init(type: String) {
        self.type = type
    }
}

class Cat: Pet {
    static let type = "Cat"
    init() {
        super.init(type: Cat.type)
    }
}

class Dog: Pet {
    static let type = "Dog"
    init() {
        super.init(type: Dog.type)
    }
}

struct PetQueue {
    let pet: Pet
    let count: Int
    
    init(pet: Pet, count: Int) {
        self.pet = pet
        self.count = count
    }
}


struct DogCatQueue {
    private var _count = 0
    private var _dogQueue = Queue<PetQueue>()
    private var _catQueue = Queue<PetQueue>()
    
    mutating func add(pet: Pet) {
        if pet.type == Dog.type {
            _dogQueue.inQueue(PetQueue(pet: pet, count: _count))
            _count += 1
        } else if pet.type == Cat.type {
            _catQueue.inQueue(PetQueue(pet: pet, count: _count))
            _count += 1
        } else {
            fatalError("error type")
        }
    }
    
    mutating func pollAll() -> Pet {
        if !_dogQueue.isEmpty && !_catQueue.isEmpty {
            let dogFirst = _dogQueue.first!
            let catFirst = _catQueue.first!
            
            if dogFirst.count < catFirst.count {
                return _dogQueue.outQueue().pet
            } else {
                return _catQueue.outQueue().pet
            }
        } else if !_dogQueue.isEmpty {
            return _dogQueue.outQueue().pet
        } else if !_catQueue.isEmpty {
            return _catQueue.outQueue().pet
        }
        fatalError("queue is empty")
    }
    
    mutating func pollDog() {
        _dogQueue.outQueue()
    }
    
    mutating func pollCat() {
        _catQueue.outQueue()
    }
    
    func isEmpty() -> Bool {
        return _dogQueue.isEmpty && _catQueue.isEmpty
    }
    
    func isDogEmpty() -> Bool {
        return _dogQueue.isEmpty
    }
    
    func isCatEmpty() -> Bool {
        return _catQueue.isEmpty
    }
}

用一个栈实现另一个栈的排序

题解:

  • 存储数据的为 data,辅助的为 help,当前元素:cur。
  • 弹出 data 栈顶元素 - cur,如果 cur 小于等于 help 栈顶元素,则将 cur 入栈到 help,否则将 help 元素逐一入栈 data,直到 cur 小于等于 help 栈顶元素,再将 cur 入栈 help。
  • 重复执行上述操作直到 data 为空。
  • 最后将 help 元素逐一入栈 data。

代码:

var help = Stack<Int>()
var data = Stack<Int>()

func sort() {
    guard !data.isEmpty else {
        return
    }
    
    while !data.isEmpty {
        let cur = data.pop()!
        while !help.isEmpty && cur > help.last! {
            data.push(help.pop()!)
        }
        help.push(cur)
    }
    
    while !help.isEmpty {
        data.push(help.pop()!)
    }
}

用栈来求解汉诺塔问题

生成窗口最大值数组

题解:

  • 异常判断:nums 为空或 window 小于 1 或 nums 的长度小于 window 直接返回
  • 利用双端队列来存储最大值的索引
  • 放入规则:1)队列为空直接将 i 放入队尾;2)若队尾元素 j,numsj 小于等于 nums i,则移除队尾,直至 numsj 大于 nums i。
  • 弹出规则:如果队头的下表 y == i - window,说明队头的索引已在窗口之外,即索引已过期,弹出即可。当循环到 i 时,窗口范围为:[i - window + 1...i]
  • 收集当前窗口最大元素:i 大于等于 windon - 1,说明已完整包含窗口元素,可以开始收集。 代码:
func deMaxWindow(nums: [Int], window: Int) {
    if nums.isEmpty || window < 1 || nums.count < window {
        return
    }
    var deQueue = DeQueue<Int>()
    var res = [Int]()
    
    for i in 0..<nums.count {
        while !deQueue.isEmpty && nums[deQueue.last!] <= nums[i] {
            deQueue.trailOut()
        }
        deQueue.trailIn(i)
        
        if deQueue.first! == i - window {
            deQueue.headOut()
        }
        
        if i >= window - 1 {
            res.append(nums[deQueue.first!])
        }
    }
}

单调栈结构

题解

  • 使用 stack 来存储当前索引值,且 stack 按照数组索引值元素(nums[xxx])的递减顺序来存储。
  • i: 当前索引值;leftIndex:左边离 nums[i] 最近且比 nums[i] 小的元素值索引;rightIndex:右边离 nums[i] 最近且比 nums[i] 小的元素值索引;last:栈顶元素。
  • 循环遍历数组 nums:
    • 若 stack 非空且 nums[i] < nums[last],出栈 last,因 nums[last] > nums[i] 且 i 为右边最近,所以 rightIndex = i; 若此时 stack 为空,则说明比 nums[last] 左边小的元素不存在,leftIndex = -1;若不为空,记当前栈顶元素为 newLast,因 nums[last] > nums[newLast],且 newLast 为左边最近,所以 leftIndex = newLast。
    • 若 stack 为空,或 nums[i] > nums[j],则将 i 入栈。
  • 清算栈内数据:
    • 比栈内索引值位置元素小的右边元素不存在,所以 rightIndex = -1。
    • 弹出栈顶元素 last,若此时栈为空,则 leftIndex = -1,若不为空则 leftIndex = newLast。

代码:

func findLeftAndRightMinValueIndex(nums: [Int]) -> [[Int]] {
    if nums.isEmpty {
        return []
    }
    var res = Array(repeating: [-1, -1], count: nums.count)
    
    var stack = Stack<Int>()
    for i in 0..<nums.count {
        while !stack.isEmpty && nums[i] < nums[stack.last!] {
            let curIndex = stack.pop()!
            let right = i
            let left = stack.isEmpty ? -1 : stack.last!
            res[curIndex] = [left, right]
        }
        stack.push(i)
    }
    
    while !stack.isEmpty {
        let curIndex = stack.pop()!
        let right = -1
        let left = stack.isEmpty ? -1 : stack.last!
        res[curIndex] = [left, right]
    }
    
    return res
}

进阶:数组元素允许重复。

题解:stack 中存放数组,记弹出的元素为 i,当 i 下面的值为长度大于 1 的数组时,leftIndex = 数组的最后一个元素。其他思路类似。

代码:

func duplicateFindLeftAndRightMinValueIndex(nums: [Int]) -> [[Int]] {
    if nums.isEmpty {
        return []
    }
    var res = Array(repeating: [-1, -1], count: nums.count)
    
    var stack = Stack<[Int]>()
    for i in 0..<nums.count {
        while !stack.isEmpty && nums[i] < nums[stack.last![0]] {
            let last = stack.pop()!
            let leftIndex = stack.isEmpty ? -1 : stack.last!.last!
            let rightIndex = i
            for ele in last {
                res[ele] = [leftIndex, rightIndex]
            }
        }
        if !stack.isEmpty && nums[i] == nums[stack.last![0]] {
            var last = stack.pop()!
            last.append(i)
            stack.push(last)
        } else {
            stack.push([i])
        }
    }
    
    while !stack.isEmpty {
        let last = stack.pop()!
        let right = -1
        let left = stack.isEmpty ? -1 : stack.last!.last!
        for ele in last {
            res[ele] = [left, right]
        }
    }
    
    return res
}

求最大子矩阵的大小

题解:

  • 异常条件:数组为空直接返回。
  • 分解矩阵
    • 根据行去分解,统计每个元素从自身位置(包括自身位置)往上有几个连续的1。
  • 在分解的过程中去统计最大矩形:本质还是单调栈的结构,栈顶元素为数组的索引,从栈顶到栈底,索引位置的值(即数组相应索引的元素)需依次递减。
    • 当前位置的元素大于栈顶位置的元素,直接入栈。
    • 若小于或等于,则弹出栈顶元素,并计算最大区域。
    • 当前位置记为 i,弹出位置记为 j,新的栈顶元素记为 k。则 j 位置的最大矩形计算公式为:(i - k - 1) * height[j]。
    • 清空栈阶段:
      • 将 i 置为 height 数组的长度,然后再按上述计算公式计算。

代码:

func maxRecSize(map: [[Int]]) -> Int {
    guard !map.isEmpty else {
        return 0
    }
    var maxSize = 0
    var height = Array(repeating: 0, count: map[0].count)
    
    for row in map {
        for i in 0..<row.count {
            height[i] = row[i] == 0 ? 0 : height[i] + 1
        }
        let res = maxRecFromBottom(height: height)
        maxSize = max(res, maxSize)
    }
    
    return maxSize
}

func maxRecFromBottom(height: [Int]) -> Int {
    guard !height.isEmpty else {
        return 0
    }
    
    var stack = Stack<Int>()
    var maxSize = 0
    
    for i in 0..<height.count {
        while !stack.isEmpty && height[i] <= height[stack.last!] {
            let j = stack.pop()!
            let k = stack.last ?? -1
            let curSize = height[j] * (i - k - 1)
            
            maxSize = max(maxSize, curSize)
        }
        stack.push(i)
    }
    
    let i = height.count
    while !stack.isEmpty {
        let j = stack.pop()!
        let k = stack.last ?? -1
        let curSize = height[j] * (i - k - 1)
        
        maxSize = max(maxSize, curSize)
    }
    return maxSize
}

最大值减去最小值小于等于 num 的子数组数量

可见的山峰对数量