栈以及使用栈完成一个计算式的计算

7. 栈

 

情景带入:

给定一串字符串"7*2*2-5+1-5+3-3",求出字符串的结果

7.1 栈的介绍

  • 栈的英文是(stack)

  • 栈是一个先进后出(FILO - First In Last Out)的有序列表

  • 栈(stack)是限制线性表中元素的插入和删除,只能在线性表的同一端进行的特殊线性表,允许插入和删除的一端,为变化的一端,称为栈顶(Top),另一端为固定的一端,称为栈底(Bottom)

  • 根据定义可知,最先放入栈中元素在栈底,最后放入栈中元素在栈顶,而删除元素刚好相反,最后放入的元素最先删除,最先放入的元素最后删除。

  • 出栈(pop)和入栈(push)

7.2 栈的应用场景

  1. 子程序的调用:在跳往子程序前,会先将下一个指令的地址存到堆栈中,直到子程序执行完后再将地址取出,以回到原来的程序中

  2. 处理递归调用:和子程序的调用类似,只是除了储存下一个指令的地址外,也将参数,区域变量等数据存入堆栈中

  3. 表达式的转换与求值

  4. 二叉树的遍历

  5. 图形的深度优先(depth - first)搜索法

7.3 实现栈的思路

  1. 使用数组来模拟

  2. 定义一个top,来表示栈顶,初始值为-1

  3. 入栈的操作,当有数据加入到栈时,top++;stack[top] = data;

  4. 出栈的操作,int value = stack[top]; top--; return value;

7.3.1 数组实现栈

 1  2 // 定义一个ArrayStack 表示栈
 3 class ArrayStack{
 4     private int maxSize; // 栈的大小
 5     private int[] stack; // 数组,数组模拟栈,数据就放在该数组
 6     private int top = -1; // top 表示栈顶,初始化为-1
 7  8     public ArrayStack(int maxSize) {
 9         this.maxSize = maxSize;
10         stack = new int[this.maxSize];
11     }
12 13     // 判断栈满
14     public boolean isFull(){
15         return top == maxSize - 1;
16     }
17 18     // 栈空
19     public boolean isEmpty(){
20         return top == -1;
21     }
22 23     // 入栈-push
24     public void push(int value){
25         // 先判断是否满
26         if (isFull()){
27             System.out.println("Full");
28             return;
29         }
30         top++;
31         stack[top] = value;
32     }
33     // 出栈 pop 将栈顶的数据取出
34     public int pop(){
35         if (isEmpty()){
36             System.out.println("Empty");
37             // 抛出异常处理
38             throw new RuntimeException("栈空,没有数据");
39         }
40         int value = stack[top];
41         top--;
42         return value;
43     }
44 45     // 显示栈, 遍历栈
46     public void list(){
47         // 遍历时需要从栈顶显示
48         if (isEmpty()){
49             System.out.println("Empty");
50             return;
51         }
52         for (int i = top; i >= 0; i--){
53             System.out.println("stack["+i+"]="+stack[i]);
54         }
55     }
56 57 }

 


测试上面的栈是否可用

 1 public class ArrayStackDemo {
 2     public static void main(String[] args) {
 3         // 测试ArrayStack是否正确
 4         ArrayStack arrayStack = new ArrayStack(4);
 5         String key = "";
 6         boolean loop = true; //  是否退出菜单
 7         Scanner scanner = new Scanner(System.in);
 8  9         while (loop){
10             System.out.println("show:表示显示栈");
11             System.out.println("exit:退出栈");
12             System.out.println("push:表示添加数据栈");
13             System.out.println("pop:从栈中取出数据");
14 15             System.out.println("请输入:");
16             key = scanner.next();
17             switch (key){
18                 case "show":
19                     arrayStack.list();
20                     break;
21                 case "push":
22                     System.out.println("请输入一个数:");
23                     int value = scanner.nextInt();
24                     arrayStack.push(value);
25                     break;
26                 case "pop":
27                     try {
28                         int res = arrayStack.pop();
29                         System.out.println("出栈的数据是"+res);
30                     } catch (Exception e){
31                         System.out.println(e.getMessage());
32                     }
33                     break;
34                 case "exit":
35                     scanner.close();
36                     loop = false;
37                     break;
38             }
39         }
40         System.out.println("程序退出了");
41     }
42 }

 

7.3.2 链表实现栈

  1 package stack;
  2   3 public class LinkeStackDemo {
  4     public static void main(String[] args) {
  5         LinkeStack ls = new LinkeStack();
  6         Node n1 =new Node(1, 10);
  7         Node n3 =new Node(3, 20);
  8         Node n2 =new Node(2, 30);
  9         Node n4 =new Node(4, 40);
 10  11         ls.list();
 12  13         ls.push(n1);
 14         ls.push(n2);
 15         ls.push(n3);
 16         ls.push(n4);
 17         ls.list();
 18  19         Node temp = ls.pop();
 20         System.out.println(temp.getId()+"编号的数值是:" + temp.getVal());
 21         temp = ls.pop();
 22         System.out.println(temp.getId()+"编号的数值是:" + temp.getVal());
 23         temp = ls.pop();
 24         System.out.println(temp.getId()+"编号的数值是:" + temp.getVal());
 25  26     }
 27 }
 28 class LinkeStack{
 29     private Node head = new Node(0,0);
 30  31     // 判断是否为空
 32     public boolean isEmpty(){
 33         return head.getNext() == null;
 34     }
 35     // push 添加数据
 36     public void push(Node node){
 37         Node temp = head;
 38         node.setNext(temp.getNext());
 39         temp.setNext(node);
 40     }
 41  42     // 取出数据
 43     public Node pop(){
 44         if (isEmpty()){
 45             System.out.println("Empty");
 46             throw new RuntimeException("Empty");
 47         }
 48         head = head.getNext();
 49         return head;
 50     }
 51     // 遍历表
 52     public void list(){
 53         if (isEmpty()){
 54             System.out.println("Empty");
 55             return;
 56         }
 57         Node temp = head.getNext();
 58         while (true){
 59             if (temp == null){
 60                 break;
 61             }
 62             System.out.println(temp.getId()+"编号的数值是:" + temp.getVal());
 63             temp = temp.getNext();
 64         }
 65     }
 66 }
 67  68 class Node{
 69     private int id;
 70     private int val;
 71     private Node next;
 72  73     public Node(int id, int val) {
 74         this.id = id;
 75         this.val = val;
 76     }
 77  78     public int getVal() {
 79         return val;
 80     }
 81  82     public void setVal(int val) {
 83         this.val = val;
 84     }
 85  86     public Node(){
 87         this.id = id;
 88     }
 89  90     public int getId() {
 91         return id;
 92     }
 93  94     public void setId(int id) {
 95         this.id = id;
 96     }
 97  98     public Node getNext() {
 99         return next;
100     }
101 102     public void setNext(Node next) {
103         this.next = next;
104     }
105 }

 

7.4 使用栈完成计算器

参考视频

使用栈完成表达式的计算思路

  1. 通过一个index值(索引) ,来遍历我们的表达式

  2. 如果我们发现是个数组,就直接加入数栈

  3. 如果发现,我们扫描到是一个符号,就分如下情况

    1. 如果发现当前符号栈为空,直接入栈

    2. 如果符号栈有操作符,就进行比较,如果当前的操作符的优先级小于或者等于栈中的操作符,就需要从数栈中pop出两个数,再从符号栈中pop出一个符号,进行运算,得到结果,入数栈,然后将当前扫描的操作符入符号栈

    3. 如果当前的操作符的优先级大于栈中的操作符,直接如符号栈

  4. 当表达式扫描完毕,就顺序的从数栈和符号栈中pop出相应的数和符号,并运行

  5. 最后在数栈中的数字就是表达式

  1 package stack;
  2   3 public class Calculator {
  4     public static void main(String[] args) {
  5         // 根据思路,完成表达式的实现
  6         String expression = "30+2*6-2";
  7         // 创建两个栈,数栈,和符号栈
  8         ArrayStack2 numStack = new ArrayStack2(10);
  9         ArrayStack2 operStack = new ArrayStack2(10);
 10         // 定义相关的变量
 11         int index = 0;// 用于扫描
 12         int num1 = 0;
 13         int num2 = 0;
 14         int oper = 0;
 15         int res = 0;
 16         char ch = ' '; // 将每次扫描得到的char保存到ch
 17         String keepNum = ""; // 用于拼接多为数
 18         // 开始while循环的扫描expression
 19         while (true){
 20             // 依次得到expression的每一个字符
 21             ch = expression.substring(index,index + 1).charAt(0);
 22             // 判断ch是什么,然后做相应的处理
 23             if (operStack.isOper(ch)){
 24                 // 判断当前符号栈是否为空
 25                 if (!operStack.isEmpty()){
 26                     // 处理
 27                     if (operStack.priority(ch) <= operStack.priority(operStack.peek())){
 28                         num1 = numStack.pop();
 29                         num2 = numStack.pop();
 30                         oper = operStack.pop();
 31                         res = numStack.cal(num1, num2, oper);
 32                         // 把运算的结果入数栈
 33                         numStack.push(res);
 34                         // 然后把当前的操作符入栈
 35                         operStack.push(ch);
 36                     }else {
 37                         // 如果优先级大于栈顶优先级,直接入栈
 38                         operStack.push(ch);
 39                     }
 40                 } else {
 41                         // 如果为空直接入符号栈
 42                         operStack.push(ch);
 43                 }
 44             } else {
 45                 // numStack.push(ch - 48);
 46                 // 当处理多位数时,不能发现是一个数就立即入栈,因为他可能是多位数
 47                 // 在处理数,需要向 expression 的表达式的index 后再看以为,如果是数就进行扫描,如果是符号才入栈
 48                 // 因此我们需要定义一个变量,用于拼接
 49                 keepNum += ch;
 50                 // 如果ch是最后一位,直接入栈
 51                 if (index == expression.length() - 1){
 52                     numStack.push(Integer.parseInt(keepNum));
 53                 } else {
 54                     // 判断下一位是不是数组,如果是,继续扫描,如果不是,则入栈
 55                     if (operStack.isOper(expression.substring(index + 1, index + 2).charAt(0))) {
 56                         // 如果后一位是运算符,则入栈
 57                         numStack.push(Integer.parseInt(keepNum));
 58                         // 重要!!!!清空keepNum;
 59                         keepNum = "";
 60                     }
 61                 }
 62  63             }
 64             // 让index + 1, 并且判断是否扫描到expression最后
 65             index++;
 66             if (index >= expression.length()){
 67                 break;
 68             }
 69         }
 70  71         // 当扫描完毕后,就顺序的从 数栈 和 符号栈 中pop出相应的数和符号进行运算
 72         while (true){
 73             // 如果符号栈为空,则计算到最后的结果,数栈中只有一个结果
 74             if (operStack.isEmpty()){
 75                 break;
 76             }
 77             num1 = numStack.pop();
 78             num2 = numStack.pop();
 79             oper = operStack.pop();
 80             res = numStack.cal(num1, num2, oper);
 81             numStack.push(res); // 入栈
 82         }
 83         System.out.println("表达式是:"+ expression + " 的结果是"+ numStack.pop());
 84  85     }
 86 }
 87 // 先创建一个栈
 88 // 定义一个ArrayStack 表示栈
 89 class ArrayStack2{
 90     private int maxSize; // 栈的大小
 91     private int[] stack; // 数组,数组模拟栈,数据就放在该数组
 92     private int top = -1; // top 表示栈顶,初始化为-1
 93  94     public ArrayStack2(int maxSize) {
 95         this.maxSize = maxSize;
 96         stack = new int[this.maxSize];
 97     }
 98  99     // 判断栈满
100     public boolean isFull(){
101         return top == maxSize - 1;
102     }
103 104     // 栈空
105     public boolean isEmpty(){
106         return top == -1;
107     }
108 109     // 入栈-push
110     public void push(int value){
111         // 先判断是否满
112         if (isFull()){
113             System.out.println("Full");
114             return;
115         }
116         top++;
117         stack[top] = value;
118     }
119     // 出栈 pop 将栈顶的数据取出
120     public int pop(){
121         if (isEmpty()){
122             System.out.println("Empty");
123             // 抛出异常处理
124             throw new RuntimeException("栈空,没有数据");
125         }
126         int value = stack[top];
127         top--;
128         return value;
129     }
130 131     // 显示栈, 遍历栈
132     public void list(){
133         // 遍历时需要从栈顶显示
134         if (isEmpty()){
135             System.out.println("Empty");
136             return;
137         }
138         for (int i = top; i >= 0; i--){
139             System.out.println("stack["+i+"]="+stack[i]);
140         }
141     }
142     // 返回运算符的优先级,优先级由程序员决定,优先级使用数字表示
143     // 数字越大,优先级越高
144     public int priority(int oper){
145         if (oper =='*' || oper == '/'){
146             return 1;
147         } else if (oper == '+' || oper == '-'){
148             return 0;
149         } else {
150             return -1;// 假定目前表达式只有 + - * / 四个符号
151         }
152     }
153     // 判断是不是个运算符
154     public boolean isOper(char val){
155         return val == '+' || val == '-' || val == '*' || val == '/';
156     }
157 158     // 计算方法
159     public int cal(int num1, int num2, int oper){
160         int res = 0;
161         switch (oper){
162             case '+':
163                 res = num1 + num2;
164                 break;
165             case '-':
166                 res = num2 - num1; // 注意顺序
167                 break;
168             case '*':
169                 res = num1 * num2;
170                 break;
171             case '/':
172                 res = num2 / num1; //  注意顺序
173                 break;
174         }
175         return res;
176     }
177 178     // 增加一个方法,可以返回当前栈顶的值,但不是pop
179     public int peek(){
180         return stack[top];
181     }
182 }

 


 

posted @ 2021-07-31 15:17  笔锋  阅读(120)  评论(0)    收藏  举报