一、栈

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
    }