栈
栈
栈是一种新进后出的数据结构
用数组实现一个栈
public class ArrStack { //用数组模仿栈 private int maxsize;//定义栈的容量 private int top=-1;//栈顶指针 private int buttom=-1;//栈底指针 private int[] stack;//栈数组 public ArrStack(int maxsize){ //栈的构造器 this.maxsize=maxsize; stack = new int[maxsize]; } private boolean isFull(){ //判断栈是否为满 return top==maxsize-1; } private boolean isEmpty(){ //判断栈是否为空 return top==buttom; } public int pop(){ //出栈操作 取出栈顶元素,将栈顶指针减一 if(isEmpty()){ throw new RuntimeException("栈空,不能取出数据"); } int value=stack[top]; top--; return value; } public void push(int value){ //入栈操作 将栈顶指针加一,将要加入的元素存入该位置 if(isFull()){ System.out.println("栈满,不能添加数据"); return; } top++; stack[top]=value; } public void showall(){ //遍历 if(isEmpty()){ System.out.println("栈是空的"); return; } for (int i = 0; i <top+1 ; i++) { System.out.printf("stack[%d]=%d\n",i,stack[i]); } } }
public class LikendStack { //使用双向链表构建栈 private Node head=new Node(-1);//栈底指针 private Node tail = head;//栈顶 private Node temp;//临时变量,用于栈底指针的修改 private boolean isEmpty(){ //判断栈是否为空(链表可以无限添加,不需要判断是否满) return head==tail; } public int pop(){ //出栈 if(isEmpty()){ throw new RuntimeException("栈空,不能取出数据"); } int value= tail.no; tail=tail.pre;//将链表尾部变更为原链表尾部的上一个元素 return value; } public void push(int value){ //入栈 Node node = new Node(value); tail.next=node; temp=tail; tail=node;//将新加入的链表添加到链表尾部,并将尾部更新为节点 node.pre=temp; } public void showall(){ //遍历 Node node = tail; while(true){ System.out.println(node.no); if (node.pre==head){ break; } node=node.pre; } } class Node{ int no; public Node next; public Node pre; public Node(int no){ this.no=no; } } }
模仿一个简易计算器
(中缀表达式)
public class Calculator { public static void main(String[] args) { /* 用栈实现一个简易计算器的基本思路: 1.通过index(索引)来遍历表达式 2.如果发现是一个数字则入数栈 3.发现是一个符号则入符号栈 如果当前的符号优先级小于或等于栈中的符号,从数栈中取出两个数,从符号栈中取出一个符号 进行运算,将结果存入数栈,并将当前符号入符号栈。 如果当前的符号优先级大于栈中的符号,并将当前符号入符号栈。 4.表达式扫描完毕以后,顺序的从数栈中取出数据,从符号栈中取出相应的数据进行运算,将每一次的结果入数栈 5.最后数栈中只有一个数字,就是我们需要的结果 */ String forString = "3+5+8*3+9/3+20";//将要处理的运算表达式 ArrStack2 numStack = new ArrStack2(10);//存储数字的栈 ArrStack2 operStack = new ArrStack2(10);//存储符号的栈 int index=0;//扫描字符串的索引 int num1=0; int num2 = 0; int oper = 0; int result = 0; char ch=' ';//存储每一个扫描到的字符 String keepnum=""; while(true){ ch = forString.substring(index,index+1).charAt(0); if(operStack.isOper(ch)){ //如果是一个符号,有两种处理逻辑 if(!operStack.isEmpty()){ /* 如果当前的符号优先级小于或等于栈中的符号,从数栈中取出两个数,从符号栈中取出一个符号 进行运算,将结果存入数栈,并将当前符号入符号栈。*/ if(operStack.priority(ch)<=operStack.priority(operStack.peektop())){ num1=numStack.pop(); num2=numStack.pop(); oper=operStack.pop(); result=numStack.caltor(num1,num2,oper); numStack.push(result); operStack.push(ch); }else{ // 如果当前的符号优先级大于栈中的符号,并将当前符号入符号栈。 operStack.push(ch); } }else{ //如果是空,则直接入栈 operStack.push(ch); } }else { //numStack.push(ch-48);//将字符1转化为数字1(ASCII码表) //当发现是一个数字时不能马上入栈,可能是一个多位数 //应该继续扫描后一个,如果是数字,进行拼接,如果不是入栈 keepnum+=ch; if(index==forString.length()-1){ numStack.push(Integer.parseInt(keepnum)); //避免字符串的索引越界 }else { if (operStack.isOper(forString.substring(index+1,index+2).charAt(0))){ numStack.push(Integer.parseInt(keepnum)); keepnum=""; } } } index++; if(index>=forString.length()){ break; } } while(true){ // 4.表达式扫描完毕以后,顺序的从数栈中取出数据,从符号栈中取出相应的数据进行运算,将每一次的结果入数栈 // 5.最后数栈中只有一个数字,就是我们需要的结果 if (operStack.isEmpty()) break; num1=numStack.pop(); num2=numStack.pop(); oper=operStack.pop(); result=numStack.caltor(num1,num2,oper); numStack.push(result); } System.out.printf("表达式的结果是%d",numStack.pop()); } static class ArrStack2 { //用数组模仿栈 private int maxsize;//定义栈的容量 private int top = -1;//栈顶指针 private int buttom = -1;//栈底指针 private int[] stack;//栈数组 public ArrStack2(int maxsize) { //栈的构造器 this.maxsize = maxsize; stack = new int[maxsize]; } private boolean isFull() { //判断栈是否为满 return top == maxsize - 1; } private boolean isEmpty() { //判断栈是否为空 return top == buttom; } public int pop() { //出栈操作 if (isEmpty()) { throw new RuntimeException("栈空,不能取出数据"); } int value = stack[top]; top--; return value; } public void push(int value) { //入栈操作 if (isFull()) { System.out.println("栈满,不能添加数据"); return; } top++; stack[top] = value; } //返回运算符的优先级,靠程序员自己确定 //数字越大优先级越高 public int priority(int oper){ if(oper=='+'||oper=='-'){ return 1; }else if(oper=='*'||oper=='/'){ return 2; }else{ return -1; } } //判断是否是一个运算符 public boolean isOper(int value){ return value=='+'||value=='-'||value=='*'||value=='/'; } //计算方法 public int caltor(int num1,int num2,int oper){ int result=0; switch (oper){ case '+':result=num1+num2; break; case '-':result=num2-num1;//根据入栈和出栈顺序,注意计算顺序,除法同理 break; case '*':result=num1*num2; break; case '/':result=num2/num1; break; default:break; } return result; } //查看栈顶数据,但是并不取出 public int peektop(){ return stack[top]; } } }
前缀、中缀、后缀表达式
前缀表达式:又被称为波兰表达式,特点是运算符都在数字之前
以 (3+4)*5-6为例中的前缀表达式为: - * + 3 4 5 6
中缀表达式:就是我们最常见的表达式,虽然符合我们个人的习惯,但是并不利于计算机 处理
后缀表达式:又称逆波兰表达式,特点是运算符都在数字之后
中缀表达式转后缀表达式
1.初始化两个栈,运算符栈s1和存储中间结果的栈s2
2.从左至右扫描中缀表达式
3.遇到操作数时,将其压入s2
4.遇到运算符时,比较其余s1栈顶元素的优先级:
a.如果s1为空或者栈顶运算符为左括号“(”,直接即将运算符入栈;
b.否则,若优先级比栈顶元素高也直接将该运算符入栈;
c.否则,将s1栈顶元素压入s2中,再次转到4的开始于新的s1栈顶元素比较
5.遇到括号:
a.若是左括号“(”,直接入s1;
b.若是有括号“)”,则依次弹出s1栈顶运算符,并将其压入s2,知道遇到左括号为 止,并将左括号弹出s1(左括号与有括号均不入栈)。
6.重复2-5,直到表达式最右边;
7.将s1剩余的运算符依次压入s2
8.依次弹出s2中的元素并输出,结果的逆序即为中缀表达式对应的后缀表达式
后缀表达式在计算机中的处理
从左至右扫描表达式,遇到数字时将数字压入栈,遇到运算符时弹出栈顶的两个元素作相应运算,并将结果入栈,重复上述步骤,直到表达式最右端。
浙公网安备 33010602011771号