栈以及使用栈完成一个计算式的计算
7. 栈
情景带入:
给定一串字符串"7*2*2-5+1-5+3-3",求出字符串的结果
7.1 栈的介绍
-
栈的英文是(stack)
-
栈是一个先进后出(FILO - First In Last Out)的有序列表
-
栈(stack)是限制线性表中元素的插入和删除,只能在线性表的同一端进行的特殊线性表,允许插入和删除的一端,为变化的一端,称为栈顶(Top),另一端为固定的一端,称为栈底(Bottom)
-
根据定义可知,最先放入栈中元素在栈底,最后放入栈中元素在栈顶,而删除元素刚好相反,最后放入的元素最先删除,最先放入的元素最后删除。
-
出栈(pop)和入栈(push)
7.2 栈的应用场景
-
子程序的调用:在跳往子程序前,会先将下一个指令的地址存到堆栈中,直到子程序执行完后再将地址取出,以回到原来的程序中
-
处理递归调用:和子程序的调用类似,只是除了储存下一个指令的地址外,也将参数,区域变量等数据存入堆栈中
-
表达式的转换与求值
-
二叉树的遍历
-
图形的深度优先(depth - first)搜索法
7.3 实现栈的思路
-
使用数组来模拟
-
定义一个top,来表示栈顶,初始值为-1
-
入栈的操作,当有数据加入到栈时,
top++;stack[top] = data; -
出栈的操作,
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 使用栈完成计算器
使用栈完成表达式的计算思路
-
通过一个index值(索引) ,来遍历我们的表达式
-
如果我们发现是个数组,就直接加入数栈
-
如果发现,我们扫描到是一个符号,就分如下情况
-
如果发现当前符号栈为空,直接入栈
-
如果符号栈有操作符,就进行比较,如果当前的操作符的优先级小于或者等于栈中的操作符,就需要从数栈中pop出两个数,再从符号栈中pop出一个符号,进行运算,得到结果,入数栈,然后将当前扫描的操作符入符号栈
-
如果当前的操作符的优先级大于栈中的操作符,直接如符号栈
-
-
当表达式扫描完毕,就顺序的从数栈和符号栈中pop出相应的数和符号,并运行
-
最后在数栈中的数字就是表达式
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 }
