栈是一种后进先出(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
}