底部代码需要依赖的 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
}