科学计算器就是用到了栈的原理
英文名为:stack
栈是一个先进后出(First in last out)的有序列表
栈是 限制线性表,元素的插入和删除只能在线性表的同一端进行,允许插入和删除的一端称为变化的一端,是栈顶,另外一端为固定的一端,称为栈底。
根据栈的定义,最先放入栈中的元素在栈底,最后放入栈中的元素在栈顶。最先弹出的元素在栈顶,最后弹出的元素在栈底。
压栈(push)和弹栈(pop)的原理图
class MyStack { private int capacity; int[] array; int pointer = -1; public MyStack() { } public MyStack(int capacity) { this.capacity = capacity; this.array = new int[capacity]; } // 压栈的方法(push) public void push(int num) { if (pointer == capacity - 1) { throw new RuntimeException("栈已满,压栈失败"); } else { array[++pointer] = num; } } // 弹栈的方法(pop) public void pop() { if(pointer == -1) { throw new RuntimeException("栈已空,弹栈失败"); } else { array[pointer--] = 0; } } // 遍历栈的方法 public void printStack(){ System.out.println("栈中现存的元素为:"); for (int i = pointer; i >= 0 ; i--) { System.out.printf("%d\t", array[i]); } } }
class StackByList { ListNode header = new ListNode(-1); int capacity; public StackByList() { } public StackByList(int capacity) { this.capacity = capacity; } // 获取当前栈中元素的个数 public int getSize() { ListNode p = header; int count = 0; while(p.next != null) { p = p.next; count++; } return count; } // 往栈中添加元素的方法 public void push(ListNode listNode) { ListNode p = header; if (this.getSize() == capacity) throw new RuntimeException("栈已满,无法添加更多的元素"); listNode.next = p.next; p.next = listNode; } // 移除栈中元素的方法 public void pop() { ListNode p = header; if (p.next == null) throw new RuntimeException("栈已空,弹栈失败"); p.next = p.next.next; } // 打印栈中元素的方法 public void printStack() { ListNode p = header; if (p == null || p.next == null) System.out.println("当前栈中没有元素"); p = p.next; while (p != null) { System.out.printf("%d\t", p.value); p = p.next; } } } class ListNode { int value; ListNode next; public ListNode(int value) { this.value = value; } public ListNode(int value, ListNode next) { this.value = value; this.next = next; } public ListNode() { } }
通过一个指针来扫描这个字符串,遍历我们的表达式
扫描的过程中:
表达式扫描完毕之后,顺序从数栈和符号栈中pop出相应的元素进行运算
最后数栈中只有一个数据,就是这个表达式的结果
class NumsComputer{ // 存操作符的栈 IntStack operaStack = new IntStack(5); // 存操作数的栈 IntStack numsStack = new IntStack(10); // 构造方法 public NumsComputer() { } // 遍历字符串的方法 public void scanString(String string) { char[] chars = string.toCharArray(); for (int i = 0; i < chars.length; i++) { if (chars[i] >= 48 && chars[i] <= 57) numsStack.push(chars[i] - '0'); // 将字符型数据转换为整数型存入 // 在将操作符压入栈中的时候,需要抽象成一个方法 else if (operaStack.pointer == -1) operaStack.push(chars[i]); // 符号栈中元素为空 else pushOperator(chars[i]); } } // 运算方法 public int compute(){ while (operaStack.pointer != -1) { operate(operaStack, numsStack); } int result = numsStack.pop(); return result; } // 获取符号优先级的方法 public static int getPriority(int c) { if (c == '*' || c == '/') { return 1; } else return 0; } // 操作符号压栈的方法 public void pushOperator(char c) { // 如果当前运算符的优先级小于或者等于栈中的优先级 if (getPriority('c') <= getPriority(operaStack.array[operaStack.pointer])){ operaStack.push(c); } else { operaStack.push(c); } // 当前符号优先级大于栈顶的符号优先级 } // 弹栈进行计算的方法 public static void operate(IntStack operaStack, IntStack numsStack) { // 从数栈中弹出两个元素 int a = numsStack.pop(); int b = numsStack.pop(); // 从运算符栈中弹出一个元素 int c = operaStack.pop(); int result = 0; switch (c) { case '+': result = b + a; break; case '-': result = b - a; break; case '*': result = b * a; break; case '/': result = b / a; break; } numsStack.push(result); } } class IntStack { private int capacity; int[] array; int pointer = -1; public IntStack() { } // 获取栈中元素个数 public int getSize() { int count = 0; while (array[count] != 0) count++; return count; } public IntStack(int capacity) { this.capacity = capacity; this.array = new int[capacity]; } // 压栈的方法(push) public void push(int c) { if (++pointer == capacity) { throw new RuntimeException("栈已满,压栈失败"); } array[pointer] = c; } // 弹栈的方法(pop) public int pop() { if (pointer == -1) { throw new RuntimeException("栈已空,弹栈失败"); } else { int c = array[pointer]; array[pointer--] = 0; return c; } } // 遍历栈的方法 public void printStack() { System.out.println("栈中现存的元素为:"); for (int i = pointer; i >= 0; i--) { System.out.print(array[i] + "\t"); } } }
1)概述
(3 + 4) * 5 - 6
对应的前缀表达式为- * + 3 4 5 6
2)计算机求值
从右至左扫描表达式,遇到数字的时候将数字压入栈中,遇到运算符时,弹出栈顶的两个数,用运算符对他们做相应的计算(栈顶元素和次顶元素),并将结果并入栈中,重复上述过程直到表达式最左端,最后运算的到的值即为表达式的结果。
3 + 4
的值,将结果7压入栈中7 * 5
,将结果35压入栈中35 - 6
的值,得到最终的结果1)概述
(3 + 4) * 5 - 6
1)概述
(3 + 4) * 5 -6
对应的后缀表达式为3 4 + 5 * 6 -
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fH6ygEAC-1619919934940)(E:\JavaSpace\上课截图\逆波兰表达式.png)]
3 4
压入栈中+
运算符,因此弹出 4 和 3 ,计算出3 + 4
的值,再将运算结果7压入栈中*
运算符,因此将 5 和 7 弹出栈,计算出5 * 7
的值,将 35 压入栈中-
运算,计算出35 - 6
的值,29 即为计算的最终结果见于4.5.3 2)计算机求值
class PolandNotation { // 接收用户输入的表达式 private String expression = null; // 创建数栈 Stack<Integer> numStack = new Stack<>(); public PolandNotation(String expression) { this.expression = expression; } //扫描表达式的方法 public void compute() { // 将表达式的值转变为一个char数组 char[] chars = expression.toCharArray(); int i = 0; // 对整个char数组进行遍历 while (i < chars.length) { // 如果扫描的是一个符号 if (chars[i] < 48 && chars[i] > 32) { numStack.push(calculate(chars[i], numStack)); i++; } else if (chars[i] == 32) { i++; } // 扫描到的是一个空格 else { String numString = ""; while(i < chars.length && chars[i] != 32) { numString = numString + chars[i]; i++; } int num = Integer.parseInt(numString); numStack.push(num); } } // 只需要将此时栈中的元素弹出来即可 int result = numStack.pop(); System.out.println(result); } // 弹栈计算的方法 public int calculate(char operator, Stack<Integer> numStack) { // 栈中弹出两个元素 int num1 = numStack.pop(); int num2 = numStack.pop(); int result = 0; switch (operator) { case '+': result = num2 + num1; break; case '-': result = num2 - num1; break; case '*': result = num2 * num1; break; case '/': result = num2 / num1; break; } return result; } }
1)算法流程
(
,直接将此运算符入栈(
,直接压入到s1栈中)
,依次弹出s1栈顶的运算符,并压入到s2中,直到遇到左括号为止,此时将这一对括号丢弃2)实现代码
public char[] transform(String expression){ // 将这个中缀表达式变为一个char数组 char[] expressChars = expression.toCharArray(); // 初始化运算符栈 Stack<Character> operaStack = new Stack<>(); // 初始化队列 CycleQueue numQueue = new CycleQueue(50); // 扫描中缀表达式 int i = 0; while (i < expressChars.length) { // 如果为扫描到为一个符号 if (expressChars[i] < 48 && expressChars[i] != ' '){ // 如果这个符号是左括号或者符号栈为空 if (expressChars[i] == 40 || operaStack.empty()){ operaStack.push(expressChars[i++]); } // 符号未右括号的时候将符号栈里的元素弹入到数栈之中 else if (expressChars[i] == 41) { while (true) { char temp = operaStack.pop(); // 碰到左括号后就跳出循环 if (temp == 40) break; else { numQueue.add(temp); // 这里往数栈中添加元素之后再添加空格 numQueue.add(' '); } } i++; } // 判断优先级,如果当前符号的优先级大于栈顶符号的优先级,直接入栈 else if (getPriority(expressChars[i]) > getPriority(operaStack.peek())) { operaStack.push(expressChars[i]); i++; } else { // 当前运算符的优先级小于或者等于栈顶 while (operaStack.size() != 0 && getPriority(expressChars[i]) <= getPriority(operaStack.peek())) { numQueue.add(operaStack.pop()); numQueue.add(' '); } operaStack.push(expressChars[i]); i++; } } else if (expressChars[i] == 32){ // 扫描的是一个空格 i++; } else { // 扫描的是一个数 while (i < expressChars.length && expressChars[i] != 32 && expressChars[i] != ')'){ numQueue.add(expressChars[i]); i++; } numQueue.add(' '); } } //将操作符号栈中的元素放到队列中 while (operaStack.size() != 0) { numQueue.add(operaStack.pop()); numQueue.add(' '); } // 将队列的元素取出来放到数组中 return numQueue.toArray(); }