用 Swift 实现常用的数据结构 - 栈

971 阅读3分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第2天,点击查看活动详情

什么是栈

栈是一种线性的数据结构,它常用来存储一系列同类型的元素。

它和数组或者队列非常相似,区别在于它们的添加删除元素的顺序。对于栈来说,它是后进先出的顺序,即 Last In First Out - LIFO。

对于栈的理解,大家可以想象这样一个场景:你的书桌上摞了一堆书,这堆书最上面的一本,肯定是最后放上去的。而你如果想阅读其中一本的话,你也会从最上面一本一本的往下拿。这种结构,对应的就是栈的结构。

理解了什么是栈,下面来看一下实现栈需要实现那些函数。

需要实现的函数

func isEmpty() -> Bool // 栈是否为空
func count() -> Int // 获取栈里有多少元素
func push(_ newElement: Element) // 入栈
func pop() -> Element? // 出栈
func top() -> Element? // 获取栈顶元素
func removeAll() // 移除栈里所有元素

因为在 Swift 中,Array 其实已经提供了栈的相同作用的函数。所以,这次我们底层使用 Array 来存储数据。

栈的完整实现:

class Stack<Element: Equatable> {
    private var _content = [Element]()
    
    func isEmpty() -> Bool {
        return _content.isEmpty
    }
    
    func count() -> Int {
        return _content.count
    }
    
    func push(_ newElement: Element) {
        _content.append(newElement)
    }
    
    func pop() -> Element? {
        guard count() > 0 else {
            return nil
        }
        return _content.popLast()
    }
    
    func top() -> Element? {
        guard count() > 0 else {
            return nil
        }
        return _content.last
    }
    
    func removeAll() {
        _content.removeAll()
    }
}

代码解释:

  • isEmpty/count/removeAll 直接调用 Array 的相应函数实现。
  • push:在栈顶添加元素,对应的就是 Array 的尾部,所以,调用 append 来添加。
  • pop:移除栈顶元素,调用 Array 的 popLast 即移除尾部元素。
  • top:获取栈顶元素,Array 的 last 属性即是返回数组的尾部元素。

虽然,Swift 中的 Array 很强大,但自己在用其封装一个栈并不是脱裤子放屁。因为,就笔者而言,如果在刷题的时候,如果直接使用 Array 来代替栈,就经常有一些操作会搞错,因为对于 push、pop 这种操作对应到 popLast 可能会出现偏差。比如:pop 有时一时糊涂就会调用 removeFirst 从而造成一些低级错误。

经典题目

有效的括号

LeetCode 的原题,此处为链接

这道题就是借助栈的后进先出来解决的:

  • 首先用哈希表来存储三种类型的括号:let dict = ["(":")", "[":"]", "{":"}"]
  • 遍历当前字符串:
    • 如果当前字符为左括号,则入栈。
    • 如果当前字符为右括号,则移除栈顶元素。
      • 若此时栈为空,则说明当前右括号为多余的,返回 false。
      • 判断移除的栈顶元素与当前有括号是否匹配,不匹配直接返回 false。
      • 若匹配,则继续遍历。
  • 若遍历完字符串:     * 栈不为空,则说明有多余的左括号,返回 false。

代码实现:

let dict = ["(":")", "[":"]", "{":"}"]

func isValid(_ s: String) -> Bool {
    let stack = Stack<String>()
    for char in s {
        let subStr = String(char)
        
        if dict.keys.contains(subStr) {
            stack.push(subStr)
        } else if stack.isEmpty() || dict[stack.pop()!] != subStr {
            return false
        }
    }   
    return stack.isEmpty()
}