Swift 语言没有内设有栈这种数据结构,这里我们利用数组来实现下栈。
栈的特点
应用
在开发应用中,涉及到的添加和删除或者撤销的操作,可以用到栈结构
iOS 中 navigation 对 view controller 的 push 和 pop 的操作也是用到栈的这种结构
局部变量的内存管理也是栈结构
在寻找离开迷宫的路径也是栈的这种结构,比如撤回上一次的决定
我们插入点 Swift 的基础知识
值类型和引用类型
Swift 中结构体和类都可以用来自定义类型,在 Objective-C 编程中,结构体和类的区别很大,该用结构体还是类,往往一眼就能做出决定。但是,在 Swift 中,结构体不再像之前那么单纯,它里面增添了很多面向对象的特性,从而使得结构体可以执行一些原本只有类才能执行的任务。官方的建议:
如果类型需要传值,就用结构体。这样可以保证赋值或者传参时,类型是复制的
如果类型不支持或者没有必要支持子类继承,就用结构体
如果类型所要实现的功能只是简单的存储数据,就考虑用结构体
如果需要保证数据独立,就用结构体,因为结构体属于值类型,而类属于引用类型
而栈主要用来存储数据,所以我们选择使用结构体
基本实现
/**
Swift 实现 栈结构
*/
//注释1
public struct Stack<Element>{
//声明一个泛型数组
private var storage: [Element] = []
public init () { }
public init (_ element: [Element]) {
storage = element
}
//注释2
public mutating func push(_ element: Element) {
storage.append(element)
}
//注释3
@discardableResult
public mutating func pop() -> Element? {
storage.popLast()
}
public func peek() -> Element? {
storage.last
}
public var isEmpty: Bool {
peek() == nil
}
}
var stack = Stack<Int>()
stack.push(1)
stack.push(2)
stack.push(3)
stack.push(4)
print(stack)
//打印效果:Stack<Int>(elements: [1,2,3,4])
复制代码
1>Swift 中声明泛型的语法,在类型名后面紧跟一对尖括号(<>),尖括号内部使用特定的符号作为类型占位符。占位符可以是任何字母,也可以是字符串,在 Swift 中一般使用一个大写的字母 T 作为类型占位符
2>结构体是值类型,值类型的方法是不能对自身(self)进行修改的,如果非要对自身(self)进行修改,就必须用关键字 mutating 进行标记
3>@discardableResult 是 Swift 中的一个特性,用于抑制函数或者方法返回值被调用而没有使用其结果时的警告
扩展
1.遵循 CustomStringConvertible 协议,让打印 Stack 的 log 简洁,美观。 将数组里面的元素按照堆栈的样式打印出来。 由于你是将元素添加到数组的尾部,所以首先要做的是反转数组。然后用 joined(separator:) 方法来拼接数组里面的元素,元素之间用 separator 隔开
extension Stack: CustomStringConvertible {
public var description: String {
//把 storage 数组转成字符串数组,然后反转,最后把元素用换行符拼接起来
"""
---top---
\(storage.map{"\($0)"}.reversed().joined(separator: "\n"))
---------
"""
}
}
let array = ["a","b","c","d"]
var stack = Stack(array)
print(stack)
stack.pop()
/*打印效果:
---top---
d
c
b
a
---------
*/
复制代码
2.在创建 Stack 实例的时候,使用 var stringStack: Stack = Stack() 或者 var intStack = Stack() 语法可能显得太啰嗦,如果希望像数组一样使用 var doubleStack: Stack = [1.0, 2.0, 3.0] 字面量语法,就必须遵守 ExpressibleByArrayLiteral 协议,实现相应的方法:
extension Stack: ExpressibleByArrayLiteral{
public init(arrayLiteral elements: Element...){
storage = elements
}
}
var stack:Stack = [1.0,2.0,3.0,4.0]
print(stack)
stack.pop()
复制代码
所以完整的代码:
/**
Swift 实现 栈结构
*/
public struct Stack<Element>{
private var storage: [Element] = []
public init () { }
public init (_ element: [Element]) {
storage = element
}
public mutating func push(_ element: Element) {
storage.append(element)
}
@discardableResult
public mutating func pop() -> Element? {
storage.popLast()
}
public func peek() -> Element? {
storage.last
}
public var isEmpty: Bool {
peek() == nil
}
}
extension Stack: CustomStringConvertible {
public var description: String {
//把 storage 数组转成字符串数组,然后反转,最后把元素用换行符拼接起来
"""
---top---
\(storage.map{"\($0)"}.reversed().joined(separator: "\n"))
---------
"""
}
}
extension Stack: ExpressibleByArrayLiteral{
public init(arrayLiteral elements: Element...){
storage = elements
}
}
复制代码
代码项目地址 Github
题目:
1.反转数组
[1,2,3,4,5] 反转后:[5,4,3,2,1]
代码实现:
let array: [Int] = [1, 2, 3, 4, 5]
// 数组实现 时间复杂度 O(n) 空间复杂度 O(1)
func printInReverse(_ array:[Int]){
var currentArray = array
if currentArray.count < 2 {
print(currentArray.description)
}
var low = 0
var high = currentArray.count-1
while low < high {
(currentArray[low],currentArray[high]) = (currentArray[high],currentArray[low])
low += 1
high -= 1
}
print(currentArray.description)
}
printInReverse(array)
// 输出 [5,4,3,2,1]
复制代码
这个问题用数组就可以解决,但是如果要用栈的结构解决的话,代码如下:
let array: [Int] = [1, 2, 3, 4, 5]
// 栈实现
func printInReverse2(_ array:[Int]){
var stack = Stack(array)
while stack.isEmpty == false {
print(stack.pop()!)
}
}
printInReverse2(array)
复制代码
2.匹配花括号()
// 1 h((e))llo(world)() // balanced parentheses
// 2 (hello world // unbalanced parentheses
代码实现:
func checkParentheses(_ testString: String) -> Bool{
//判断字符串的长度
if testString.count < 2 {
return false
}
//拆分字符串成字符串数组
let array = testString.map({"\($0)"})
var stack = Stack<String>()
for item in array {
if item == "(" {
stack.push(item)
}else if(item == ")") {
if let element = stack.peek() {
if element == "(" {
// 如果匹配,就移除栈顶元素 "("
stack.pop()
}
}else{
stack.push(item)
}
}else{
}
}
// 判断栈是否为空:就是看花括号是否一一匹配了
return stack.isEmpty == true
}
let testString1 = "h((e))llo(world)()"
print(checkParentheses(testString1)) // true
复制代码
Facebook 的一道面试题:
Given an absolute path for a file (Unix-style), simplify it.
For example:
path = "/home/", => "/home"
path = "/a/./b/../../c/", => "/c"
代码实现:
func simplifyPath(path: String) -> String {
// 判断传入的字符
if path.count == 0 {
return ""
}
if path.count == 1 && path == "." {
return path
}
//利用 "/" 拆分原路径 string 成数组
let pathArray = path.split(separator: "/")
print(pathArray) // 输出 ["a", ".", "b", "..", "..", "c"] 正式环境可以删掉
// 创建一个 String 类型的栈
var pathStack = Stack<String>()
for char in pathArray {
switch char {
case ".":
break
case "..":
pathStack.pop()
default:
pathStack.push(String(char))
}
}
// 判断 Stack 是否为空
if pathStack.isEmpty == true {
return "/"
}else{
let top = pathStack.peek()
return "/" + (top ?? "")
}
}
let path_test = "/a/./b/../../c/"
print(simplifyPath(path: path_test))
// 输出: /c
复制代码
其实这道题用数组也是可以实现的,这里就不放代码了,思路都一样。
评论