堆栈

抽象数据类型描述

具有一定操作约束的线性表,只在一端(栈顶,Top)做插入,删除

  • 插入数据:入栈(Push)
  • 删除数据:出栈(Pop)
  • 后入先出:Last In First Out(LIFO)

类型名称:堆栈(Stack)

数据对象集:一个 有0个或多个元素的有穷线性表

操作集

  • 创建空堆栈
  • 判断堆栈是否已满
  • 将元素压入堆栈
  • 判断堆栈是否为空
  • 删除并返回栈顶元素

堆栈的顺序存储实现

通常由一个一维数组和一个记录栈顶元素位置的变量组成

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
class Stack<T:Hashable> {
    var data : Array<T> = Array();
    var top : Int = -1
    
    
    func push(_ item:T) -> Void {
        data.append(item)
        top += 1
    }
    
    func pop() -> T? {
        if top == -1 {
            print("空堆栈")
            return nil
        }
        top -= 1
        return data.popLast()
    }
}

let stack = Stack<Int>()
stack.push(0)
stack.push(1)

print(stack.pop())
print(stack.top)

堆栈的链式存储实现

堆栈的链式存储结构实际上是一个单链表,叫做链栈,插入和删除操作只能在链栈的栈顶进行

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
class StackNode<T:Hashable> {
    var data : T?
    var next : StackNode?
}

class Stack<T:Hashable> {
    var top = StackNode<T>()// 指向栈顶,本身无意义
    
    func isEmpty() -> Bool {
        if top.next == nil {
            return true
        }
        else {
            return false
        }
    }
    
    func push(_ item:T) -> Void {
        let node = StackNode<T>();
        node.data = item;
        node.next = top.next;
        top.next = node
    }
    
    func pop() -> T? {
        if isEmpty() {
            print("空堆栈")
            return nil
        }
        
        let node = top.next
        top.next = top.next?.next
        return node!.data
    }
}

let stack = Stack<String>()
stack.push("aaa")
stack.push("bbb")
print(stack.top.next?.data)
print(stack.pop())
print(stack.pop())
print(stack.isEmpty())
stack.push("cccc")
print(stack.top.next?.data)

堆栈应用

后缀表达式求值

  • 从左向右扫描,逐个处理运算数和运算符号
  • 遇到运算数,入栈
  • 遇到运算符,出栈两个运算数,做运算
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
class ExpressionOperation {
    func input(expression:[String]) -> Int {
        let stack = Stack<Int>()
        
        for value in expression {
            if isInt(string: value) {
                stack.push(Int(value)!)
            }
            else {
                let a = stack.pop()!
                let b = stack.pop()!
                var ret = 0
                
                switch value {
                    case "+":
                        ret = a + b;
                    case "-":
                        ret = b - a;
                    case "*":
                        ret = a * b;
                    case "/":
                        ret = b / a;
             
                    default:
                        ret = 0
                }
                stack.push(ret)
            }
        }
        return stack.top.next!.data!
    }
    
    func isInt(string: String) -> Bool {
        let scan: Scanner = Scanner(string: string)
        var val:Int = 0
        return scan.scanInt(&val) && scan.isAtEnd
    }
}

let test1 = ["1","1","+","2","*","2","-","1","/"]
let test2 = ["2","9","3","/","+","4","-"]
let exp = ExpressionOperation()
exp.input(expression: test)

中缀表达式求值

基本策略:将中缀表达式转换为后缀表示式,然后求值

  • 从头到尾读取中缀表达式的每个元素
  • 运算数:直接输出
  • 左括号:入栈
  • 右括号:将栈顶的运算符出栈并输出,直到遇到左括号(出栈,不输出)
  • 运算符:
    • 若优先级大于栈顶运算符时,则入栈
    • 若优先级小于栈顶运算符时,将栈顶运算符出栈并输出,再比较新的栈顶运算符,直到该运算符优先级大于栈顶运算符为止,然后该运算符入栈
    • 若对各个元素处理完毕,则把堆栈中留存的运算符一并输出

其他应用

  • 函数调用及递归实现
  • 深度优先搜索
  • 回溯算法