数据结构学习-栈(Stack)

539 阅读2分钟

是一种后进先出(Last In First Out, LIFO)的线性数据结构,它只能在一端进行数据操作。在平时使用中,栈的基本操作有:

  • 添加元素,称为入栈 push
  • 移除元素,称为出栈 pop

代码实现

初始代码

下面是一个元素类型为泛型的栈

struct Stack<Element> {
    
    // 使用数组存储数据, 里面的元素是连续存储的
    private var storage: [Element] = []
}

extension Stack: CustomStringConvertible {
    
    var description: String {
        """
        -----top-----
        \(storage.map({ "\($0)" }).reversed().joined(separator: "\n"))
        -------------
        """
    }
}

push 和 pop 操作

    mutating func push(_ element: Element) {
        // 元素添加到数组末尾,时间复杂度 O(1)
        storage.append(element)
    }
    
    @discardableResult
    mutating func pop() -> Element? {
        // 时间复杂度 O(1)
        storage.removeLast()
    }

额外的 peek 和 isEmpty 操作

    // 查看栈顶元素
    func peek() -> Element? {
        storage.last
    }
    
    var isEmpty: Bool {
        storage.isEmpty
    }

Swift 的辅助初始化


struct Stack<Element> {
    // 直接传入一个数组    
    init(_ elements: [Element]) {
        storage = elements
    }
}

// 使用Swift的 ExpressibleByArrayLiteral 协议实现初始化
extension Stack: ExpressibleByArrayLiteral {
    
    init(arrayLiteral elements: Element...) {
        storage = elements
    }
    
}

一个简单的使用

    func stackTest() {
        /*
        var stack = Stack<Int>()    // 默认初始化
        var stack = Stack([3, 10])  // 直接传入数组数据的初始化
        */
        var stack: Stack = [8, 1]     // 实现 ExpressibleByArrayLiteral 协议后的初始化
        
        stack.push(2)
        stack.push(5)
        print("\(stack)")
        
        stack.pop()
        print("\nafter pop\n\(stack)")
        
        print("peek operation: \(String(describing: stack.peek()))")
        print("stack is empty: \(stack.isEmpty)")
    }

输出:

-----top-----
5
2
1
8
-------------

after pop
-----top-----
2
1
8
-------------
peek operation: Optional(2)
stack is empty: false

查看代码,可以看出栈的实现非常的简单,但是它确是很重要的基本线性数据结构。比如在iOS开发中,使用了 UINavigationController 的 push 和 pop 等,以及在很多的算法实现中栈也是大量使用的。

(关于数据结构类型主要有:线性类型,树类型,图类型)。

练习

有效的括号

题目:

给定一个只包括 '('')''{''}''['']' 的字符串 s ,判断字符串是否有效。

有效字符串需满足:

1.左括号必须用相同类型的右括号闭合。
2.左括号必须以正确的顺序闭合。


来源:(LeetCode)

一个简单的解决方式:

    func isValid(_ s: String) -> Bool {
        var stack = Stack<String.Element>()
        
        for character in s {
            if character == "(" {
                stack.push(character)
            } else if character == ")" {
                if stack.peek() == "(" {
                    stack.pop()
                } else {
                    return false
                }
            } else if character == "{" {
                stack.push(character)
            } else if character == "}" {
                if stack.peek() == "{" {
                    stack.pop()
                } else {
                    return false
                }
            } else if character == "[" {
                stack.push(character)
            } else if character == "]" {
                if stack.peek() == "[" {
                    stack.pop()
                } else {
                    return false
                }
            }
        }
        
        return stack.isEmpty
    }

一个更为简化的实现:

    func isValid(_ s: String) -> Bool {
        var stack = Stack<String.Element>()
        
        for character in s {
            if character == "(" {
                stack.push(")")
            } else if character == "{" {
                stack.push("}")
            } else if character == "[" {
                stack.push("]")
            } else {
                if let element = stack.pop(), element == character {
                    // same character
                } else {
                    return false
                }
            }
        }
        
        return stack.isEmpty
    }

学习链接

raywenderlich/swift-algorithm-club

leetcode