看一个实际需求
(出栈,入栈,先进后出)
请输入一个表达式
计算式:[7*2*2-5+1-5+3-3] 点击计算【如下图】
请问: 计算机底层是如何运算得到结果的?注意不是简单的把算式列出运算,因为我们看这个算式 7 * 2 * 2 - 5, 但是计算机怎么理解这个算式的(对计算机而言,它接收到的就是一个字符串),我们讨论的是这个问题。-> 栈
栈的介绍
-
栈的英文为(stack)
-
栈是一个先入后出(FILO-First In Last Out)的有序列表。
-
栈(stack)是限制线性表中元素的插入和删除只能在线性表的同一端进行的一种特殊线性表。允许插入和删除的一端,为变化的一端,称为栈顶(Top),另一端为固定的一端,称为栈底(Bottom)。
-
根据堆栈的定义可知,最先放入栈中元素在栈底,最后放入的元素在栈顶,而删除元素刚好相反,最后放入的元素最先删除,最先放入的元素最后删除
-
出栈和入栈的概念(如图所示)
栈的几个经典的应用场景
-
子程序的调用:在跳往子程序前,会先将下个指令的地址存到堆栈中,直到子程序执行完后再将地址取出,以回到原来的程序中。
-
处理递归调用:和子程序的调用类似,只是除了储存下一个指令的地址外,也将参数、区域变量等数据存入堆栈中。
-
表达式的转换与求值(实际解决)。
-
二叉树的遍历。
-
图形的深度优先(depth一first)搜索法。
栈的快速入门
-
用数组模拟栈的使用,由于栈是一种有序列表,当然可以使用数组的结构来储存栈的数据内容,下面我们就用数组模拟栈的出栈,入栈等操作。
-
实现思路分析,并画出示意图
-
代码实现了数组模拟的栈
package com.atguigu.chapter18.stack
import scala.io.StdIn
object ArrayStackDemo {
def main(args: Array[String]): Unit = {
//创建给栈
val arrayStack = new ArrayStack(4)
//测试栈的基本使用是否正确
var key = ""
while (true) {
println("show: 表示显示栈")
println("exit: 表示退出程序")
println("push: 表示添加数据到栈")
println("pop: 表示从栈取出数据")
key = StdIn.readLine()
key match {
case "show" => arrayStack.list()
case "push" => {
println("请输入一个数")
val value = StdIn.readInt()
arrayStack.push(value)
}
case "pop" => {
val res = arrayStack.pop()
if (res.isInstanceOf[Exception]) {
println(res.asInstanceOf[Exception].getMessage)
}else {
printf("取出的数为 %d ", res)
}
}
case "exit" => {
System.exit(0)
}
}
}
}
}
class ArrayStack(size: Int) {
val maxSize = size // 栈的大小
var stack = new Array[Int](maxSize)
//栈顶, 初始化为-1
var top = -1
//栈满
def isFull(): Boolean = {
top == maxSize - 1
}
//栈空
def isEmpty(): Boolean = {
top == -1
}
//入栈, 放入数据
def push(value:Int): Unit = {
if (isFull()) {
println("栈满")
return
}
top += 1
stack(top) = value
}
//出栈, 取出数据
def pop(): Any = {
if (isEmpty()) {
return new Exception("栈空")
}
val value = stack(top)
top -= 1
return value
}
//遍历栈
def list(): Unit = {
if (isEmpty()) {
println("栈空,没有数据")
return
}
for(i<- 0 to top reverse) {
printf("stack[%d]=%d ", i, stack(i))
}
}
}
栈实现综合计算器
-
使用栈来实现综合计算器-自定义优先级[priority]
-
代码实现:
package com.atguigu.chapter18.stack
import util.control.Breaks._
//考虑对表达式加入 () object Calculator { def main(args: Array[String]): Unit = {
//val expression = "3101+4*(6-2)" => 开阔思路[编程] | val expression = "7*2*2-5+1-5+3-4"
val numStack = new ArrayStack2(10) val operStack = new ArrayStack2(10)
/* 思路 1 ) 设计两个栈 , 数栈,符号栈 2) 对 exp 进行扫描 , 一个一个的取出 3) 当取出的字符是数时,就直接入数栈 4) 当取出的字符是符号时 4.1 如果当前符号栈没有数据,就直接入栈 4.2 如果当前符号的优先级小于等于符号栈的栈顶的符号的优先级,则取出该符号,并从数栈依次pop 出两个数据,进行运算,将结果重新puhs到数栈,再将当前符号push 到符号栈 4.3 反之,符号直接入符号栈 5) 当整个表达式扫描完毕后,依次从数栈和符号栈取出数据,进行运行,最后在数栈中的数据就是结果. */ var index = 0 var num1 = 0 var num2 = 0 var oper = 0 var res = 0 var ch = ' ' var keepNum = "" // 再进行扫描时,保存上次的数字ch ,并进行拼接 //会循环的取出expression 字符 breakable { while (true) {
//扫描expression ch = expression.substring(index, index + 1)(0)
if (operStack.isOper(ch)) { //如果是操作符..
if (!operStack.isEmpty()) { //如果当前符号的优先级小于等于符号栈的栈顶的符号的优先级,则取出该符号,并从数栈依次 //pop 出两个数据,进行运算,将结果重新puhs到数栈,再将当前符号push 到符号栈 if (operStack.priority(ch) <= operStack.priority(operStack.stack(operStack.top))) { //开始计算 num1 = numStack.pop().toString.toInt num2 = numStack.pop().toString.toInt oper = operStack.pop().toString.toInt res = numStack.cal(num1, num2, oper) //入数字栈 numStack.push(res) //把当前ch入符号栈 operStack.push(ch) } else { //如果当前的符号的优先级大于符号栈顶的符号优先级,直接入栈 operStack.push(ch) } } else { //符号就直接入栈 operStack.push(ch) // '+' => 43 }
} else { // 是数 //处理多位数的逻辑 keepNum += ch
//如果ch 已经是expression 最后一个字符 if (index == expression.length - 1) { numStack.push(keepNum.toInt) }else {
//判断ch 的下一个字符是不是数字, 如果是数字,则进行一次扫描,如果是操作符,就直接入栈 //看到expresson的下一个字符时,不要真正的移动index ,只是探测一下 if (operStack.isOper(expression.substring(index + 1, index + 2)(0))) { //是操作符入栈 numStack.push(keepNum.toInt) keepNum = "" // 清空 } }
//numStack.push((ch + "").toInt) // ? '1' => 49 '3' "1"=> 1 }
//index 后移 index += 1 //判断是否到表达式的最后 if (index >= expression.length()) { break() }
} }
//当整个表达式扫描完毕后,依次从数栈和符号栈取出数据,进行运行,最后在数栈中的数据就是结果 breakable { while (true) { if (operStack.isEmpty()) { break() } //运算 //开始计算 num1 = numStack.pop().toString.toInt num2 = numStack.pop().toString.toInt oper = operStack.pop().toString.toInt res = numStack.cal(num1, num2, oper) numStack.push(res) //入栈 } }
//将数字栈的最后结果pop val res2 = numStack.pop() printf("表达式 %s = %d", expression, res2) } }
//栈,该栈已经测试过了 class ArrayStack2(size: Int) { val maxSize = size // 栈的大小 var stack = new Array[Int](maxSize) //栈顶, 初始化为-1 var top = -1
//栈满 def isFull(): Boolean = { top == maxSize - 1 }
//栈空 def isEmpty(): Boolean = { top == -1 }
//入栈, 放入数据 def push(value: Int): Unit = { if (isFull()) { println("栈满") return } top += 1 stack(top) = value }
//出栈, 取出数据 def pop(): Any = { if (isEmpty()) { return new Exception("栈空") } val value = stack(top) top -= 1 return value }
//遍历栈 def list(): Unit = { if (isEmpty()) { println("栈空,没有数据") return } for (i <- 0 to top reverse) { printf("stack[%d]=%d ", i, stack(i)) } }
//返回运算符的优先级, 是程序员定, 数字越大,优先级越高 // + 1 => 0 *[] /[] => 1 def priority(oper: Int): Int = { if (oper == '*' || oper == '/') { return 1 } else if (oper == '+' || oper == '-') { return 0 } else { -1 //不正确 } }
def isOper(value: Int): Boolean = { value == '+' || value == '-' || value == '/' || value == '*' }
//计算方法 def cal(num1: Int, num2: Int, oper: Int): Int = { var res = 0 oper match { case '+' => { res = num1 + num2 } case '-' => { res = num2 - num1 } case '*' => { res = num1 * num2 } case '/' => { res = num2 / num1 } } res } } |
-
代码实现的思路分析
作业:
-
自己写出约瑟夫的代码
-
编程栈和队列的基本操作.
-
单链表的增删改查写出
-
能够把计算表达式加入 () => 思考