一、栈
1、栈是后进先出的结构,可以这么理解:有好几个盘子要叠在一起,最后放上去的盘子,在下次使用的时候会最先抽出去
2、在ios开发中,如果要在App中提阿甲撤销操作 如删除图片,恢复删除的图片,首先的数据结构是栈
3、栈的基本操作包括:push、pop、isEmpty、peek、size
4、栈的实现:
//定义栈的通用协议
protocol Stack {
//持有元素的类型
associatedtype Element
//是否为空
var isEmpty : Bool {get}
//栈的大小
var size : Int {get}
//栈顶元素
var peek : Element? {get}
//进栈
mutating func push (_ newElement : Element)
//出栈
mutating func pop()->Element?
}
//定义一个整数类型的栈
struct IntegerStack : Stack {
typealias Element = Int
private var stack = [Element]()
var isEmpty: Bool {
return stack.isEmpty
}
var size: Int{
return stack.count
}
var peek: Int?{
return stack.last
}
mutating func push(_ newElement: Int) {
stack.append(newElement)
}
mutating func pop() -> Int? {
return stack.popLast()
}
}
二、队列
1、队列是先进先出的结构,可以理解为:现实生活中的排队买票,谁先来排队,谁就先买票
2、ios开发中多线程的GCD和NSOperationQueue就是基于队列实现的
3、队列的基本操作包括:enqueue、dequeue、isEmpty、peek、size
4、队列实现:
//定义队列的通用协议
protocol Queue {
//持有元素的类型
associatedtype Element
//是否为空
var isEmpty : Bool {get}
//队列的大小
var size : Int {get}
//队首元素
var peek : Element? {get}
//入队
mutating func enqueue (_ newElement : Element)
//出队
mutating func dequeue()->Element?
}
//定义一个整数类型的栈
struct IntegerQueue : Queue {
typealias Element = Int
//定义两个数组是因为队列是左边进右边出 即 先进先出
private var left = [Element]()
private var right = [Element]()
var isEmpty: Bool {
return left.isEmpty && right.isEmpty
}
var peek: Int? {
return left.isEmpty ? right.last : left.last
}
var size: Int {
return left.count + right.count
}
mutating func enqueue(_ newElement: Int) {
right.append(newElement)
}
mutating func dequeue() -> Int? {
if left.isEmpty {
left = right.reversed()
right.removeAll()
}
return left.popLast()
}
}
三、栈和队列的相互转换
在处理栈和队列的问题时,最经典的思路是使用两个栈/队列来解决问题 即在原栈/队列的基础上,用一个协助栈/队列来简化算法,这种思路是利用空间换时间。
1、用栈实现队列
伪代码:
//用栈实现队列
struct MyQueue {
//这个栈可以用swift中的数组来代替
var stackA : Stack!
var stackB : Stack!
var isEmpty : Bool {
return stackA.isEmpty && stackB.isEmpty
}
var size : Any{
get{
return stackA.size + stackB.size
}
}
var peek : Any? {
get{
shift()
return stackB.peek
}
}
init() {
stackA = Stack()
stackB = Stack()
}
fileprivate func shift(){
if stackB.isEmpty {
while !stackA.isEmpty {
let element = stackA.pop()
stackB.push(element)
}
}
}
func enqueue(_ newElement : Any){
stackA.push(newElement)
}
func dequeue()-> Any?{
shift()
return stackB.pop()
}
}
2、用队列实现栈
伪代码:
//用队列实现栈
struct MyStack {
var queueA : IntegerQueue!
var queueB : IntegerQueue!
var isEmpty : Bool {
return queueA.isEmpty && queueB.isEmpty
}
var size : Any{
get{
return queueA.size
}
}
var peek : Any? {
get{
shift()
let last = queueA.peek
queueB.enqueue(queueA.dequeue())
swap()
return last
}
}
init() {
queueA = IntegerQueue()
queueB = IntegerQueue()
}
fileprivate func shift(){
while queueA.size != 1 {
queueB.enqueue(queueA.dequeue())
}
}
private func swap(){
(queueA, queueB) = (queueB, queueA)
}
func push(_ newElement : Any){
queueA.enqueue(newElement)
}
func pop()->Any?{
shift()
let pop = queueA.dequeue()
swap()
return pop
}
}
四、直接用数组实现栈和队列的操作
1、数组实现栈
class Stack {
var stack : [Any]!
var isEmpty : Bool {
return stack.isEmpty
}
var peek : Any? {
return stack!.last
}
var size : Int {
return stack.count
}
init() {
stack = [Any]()
}
func push(object : Any) {
stack.append(object)
}
func pop()->Any?{
if !isEmpty {
return stack.removeLast()
}else{
return nil
}
}
}
2、数组实现队列
class Queue {
var left : [Any]!
var right : [Any]!
var isEmpty : Bool {
return left.isEmpty && right.isEmpty
}
var peek : Any? {
return left.isEmpty ? right.first : left.last
}
var size : Int {
return left.count + right.count
}
init() {
left = [Any]()
right = [Any]()
}
func push(object : Any) {
right.append(object)
}
func pop()->Any?{
if left.isEmpty {
left = right.reversed()
right.removeAll()
}
if !left.isEmpty {
return left.removeLast()
}else{
return nil
}
}
}
五、栈和队列的算法题
描述:给出一个文件的绝对路径,要求将其简化(Facebook)
例如:路径是"/home/",简化为"/home"
路径是"/a/b/../../c/",简化为"/c"
路径常识科普:
1). 代表当前路径。例如 /a/. 实际上就是 /a,无论输入多少个 . 都返回当前目录
2)..代表上一级目录。例如 /a/b/.. 实际上就是 /a,也就是说先进入a目录,再进入其下的b目录,再返回b目录的上一层,也就是a目录。
思路:
1)输入和输出都是String,都代表路径
2)将输入 根据 “/” 符号 拆分成String数组,例如 "/a/b/./../d/" 就拆成了一个String数组["a", "b", ".", "..", "d"]
3)用数组创建一个栈并遍历拆分后的 String 数组,对于一般 String ,直接push,对于 ".." 做pop操作,其他情况不做处理
具体实现:
func simplefyPath(path : String)->String{
//用数组实现栈
var pathStack = [String]()
//拆分原路径
let paths = path.components(separatedBy: "/")
for path in paths {
//对于“.”直接跳过
guard path != "." else{
continue
}
//对于 “..”,使用pop
if path == ".." {
if pathStack.count > 0{
pathStack.removeLast()
}
}else if path != "" {
//对于 空数组的情况
pathStack.append(path)
}
}
//将栈中的内容转化为简化后的路径
let res = pathStack.reduce(""){ total, dir in "\(total)/\(dir)" }
//空路径的结果是“/”
return res.isEmpty ? "/" : res
}