栈
栈的英文(stack)
栈是一个先入后出的有序列表
栈是限制性表中元素的插入和删除只能在线性表的同一端进行的一种特殊线性表。允许插入和删除的一端,为变化的一端,称为栈顶(Top),宁一端为固定的一端,称为栈底(Bottom)
根据栈的定义可知,最先放入栈中元素在栈底,最后放入的元素在栈顶,而删除元素刚好相反,最后放入的元素最先删除,最先放入的元素最后删除
package stack;
import java.util.Scanner;
public class ArrayStackDemo {
public static void main(String[] args) {
ArrayStack as = new ArrayStack(5);
String key="";
boolean loop = true;
Scanner scanner = new Scanner(System.in);
while(loop) {
System.out.println("============程序开始==============");
System.out.println("show: 显示所有数据");
System.out.println("exit: 退出程序");
System.out.println("push: 添加数据");
System.out.println("pop: 取出数据");
System.out.println("请输入你的选择");
key = scanner.next();
switch(key) {
case "show":
as.list();
break;
case "exit":
scanner.close();
loop=false;
break;
case "push":
System.out.println("请输入你要添加的数据……");
int value = scanner.nextInt();
as.push(value);
System.out.println(value+"添加成功");
break;
case "pop":
try {
int res = as.pop();
System.out.println("取出的数据为"+res);
}catch(Exception e) {
System.out.println(e.getMessage());
}
break;
default:
break;
}
}
System.out.println("程序退出");
}
}
//定义一个ArrayStack表示栈
class ArrayStack{
private int maxSize;//栈的大小
private int[] stack;//数组,数组模拟栈,数据就放在该数组
private int top = -1;//top表示栈顶,初始化为-1
//构造器
public ArrayStack(int maxSize) {
this.maxSize = maxSize;
stack = new int[this.maxSize];
}
//判断是否栈满
public boolean isFull() {
return top == maxSize -1;
}
//判断是否栈空
public boolean isEmpty() {
return top == -1;
}
//入栈push
public void push(int value) {
if(isFull()) {
System.out.println("栈满,不能再添加");
return;
}
top++;
stack[top]=value;
}
//出栈-pop
public int pop() {
if(isEmpty()) {
throw new RuntimeException("栈空,没有数据");
}
int value = stack[top];
top--;
return value;
}
//遍历栈
public void list() {
if(isEmpty()) {
System.out.println("栈空,没有数据");
return;
}
//从栈顶开始显示数据
for(int i=top;i>=0;i--) {
System.out.println("stack["+i+"]"+"="+stack[i]);
}
}
}
使用栈完成表达式的计算思路
通过一个index值(索引),来遍历我们的表达式
如果发现是一个数字,就添加到数栈
如果发现是一个符号,就分两种情况
如果当前符号栈为空,就直接入栈
如果栈有操作符,就进行比较,如果当前的操作符的优先级小于或者等于栈中的操作符,就需要从数栈中pop出两个数,在从符号栈中pop出一个符号;进行运算,将得到结果,入数栈,让后将当前的操作符入符号栈如果当前的操作符的优先级大于栈中的操作符,就直接入符号栈
当表达式扫描完毕,就顺序的从数栈符号中pop出相应的数和符号,并运行
最后在数栈只有一个数字,就是表达式的结果
栈实现综合计算器
package stack;
public class Calculator {
public static void main(String[] args) {
//定义表达式
String expression = "30+2*6-2";
//创建两个栈,数栈,符号栈
ArrayStack2 numStack = new ArrayStack2(10);
ArrayStack2 operStack = new ArrayStack2(10);
//定义需要的相关变量
int index = 0;
int num1 = 0;
int num2 = 0;
int oper = 0;
int res= 0;
char ch = ' ';//每次扫描的结果保存到ch
String keepNum ="";
while(true) {
//从0开始取,取1个,把取出来的字符串转字符
ch = expression.substring(index,index+1).charAt(0);
//判断是不是操作符
if(operStack.isOper(ch)) {
//判断符号栈是不是为null
if(!operStack.isEmpty()) {
//判断优先级
//把表达式的操作符,和符号栈顶的操作符进行比较
if(operStack.priority(ch)<=operStack.priority(operStack.peek())) {
//小于或者等于需要弹出数栈的两个顶层和符号栈的顶层
num1 = numStack.pop();
num2 = numStack.pop();
oper = operStack.pop();
res = numStack.cal(num1, num2, oper);
//把运算符的结果入数栈
numStack.push(res);
//操作符入符号栈
operStack.push(ch);
}else {
operStack.push(ch);
}
}else {
//符号栈为null,直接入栈
operStack.push(ch);
}
}else {
//不是操作符
//numStack.push(ch-48);这种方法不能够处理多位数,需要对字符串进行拼接
keepNum += ch;
//如果ch已经是expression最后一位就直接入栈
if(index == expression.length()-1) {
numStack.push(Integer.parseInt(keepNum));
}else {
//判断下一个字符是不是数字,如果是数字就继续扫描,不是就入栈
if(operStack.isOper(expression.substring(index+1,index+2).charAt(0))) {
numStack.push(Integer.parseInt(keepNum));
//keepNum清空
keepNum="";
}
}
}
//需要把扫描的索引增加
//让index +1,并判断是否扫描到expression最后
index++;
if(index == expression.length()) {
//扫描结束
break;
}
}
while(true) {
//符号栈为null,则计算到最后的结果,数字只有一个数字
if(operStack.isEmpty()) {
break;
}
num1 = numStack.pop();
num2 = numStack.pop();
oper = operStack.pop();
res = numStack.cal(num1, num2, oper);
numStack.push(res);//入栈
}
System.out.println("计算结果为"+numStack.pop());
}
}
//先创建一个栈
class ArrayStack2{
private int maxSize;//栈的大小
private int[] stack;//数组,数组模拟栈,数据就放在该数组
private int top = -1;//top表示栈顶,初始化为-1
//构造器
public ArrayStack2(int maxSize) {
this.maxSize = maxSize;
stack = new int[this.maxSize];
}
//偷看栈顶的值
public int peek() {
return stack[top];
}
//判断是否栈满
public boolean isFull() {
return top == maxSize -1;
}
//判断是否栈空
public boolean isEmpty() {
return top == -1;
}
//入栈push
public void push(int value) {
if(isFull()) {
System.out.println("栈满,不能再添加");
return;
}
top++;
stack[top]=value;
}
//出栈-pop
public int pop() {
if(isEmpty()) {
throw new RuntimeException("栈空,没有数据");
}
int value = stack[top];
top--;
return value;
}
//遍历栈
public void list() {
if(isEmpty()) {
System.out.println("栈空,没有数据");
return;
}
//从栈顶开始显示数据
for(int i=top;i>=0;i--) {
System.out.println("stack["+i+"]"+"="+stack[i]);
}
}
//运算符的优先级
public int priority(int oper) {
if(oper == '*' || oper == '/') {
return 1;
} else if(oper == '+' || oper == '-') {
return 0;
}else {
return -1;
}
}
//判断是不是运算符
public boolean isOper(char val) {
return val =='+'|| val=='-'||val=='*'||val=='/';
}
//计算方法
public int cal(int num1,int num2 ,int oper) {
int res=0;
switch(oper) {
case '+':
res = num1+num2;
break;
case '-':
res = num2-num1;
break;
case '*':
res = num1*num2;
break;
case '/':
res = num2+num1;
break;
default:
break;
}
return res;
}
}
栈的三种表达式
前缀,中缀,后缀表达式(逆波兰表达式)
前缀表达式又称波兰式
前缀表达式的运算符位于操作数之前比如:(3+4)*5-6对应的前缀表达式就是 - * + 3 4 5 6
前缀表达式在计算机中的求值
从右到左扫描表达式,遇到数字式,将数字压入堆栈,遇到运算符时,弹出栈顶的两个数,用运算符对他们做相应的运算(栈顶元素和次顶元素),并将结果入栈:重复上述过程直到表达式最左端,最后运算得出的值即为表达式的结果
中缀表达式
中缀表达式就是常见的运算表达式,入(3+4)*5-6
中缀表达式的求值是我们人最熟悉的,但是对计算机来说却不好操作,因此,在计算结果时,往往会将中缀表达式转成其他表达式来操作
后缀表达式
后缀表达式又称逆波兰表达式,与前缀表达式相似,只是运算符位于操作数之后
逆波兰计算器
逆波兰表达式:"3 4 + 5 * 6 - ";
1.从左至右扫描,将3和4压入堆栈;
2.遇到+运算符,因此弹出4和3,计算3+4的值,得7,再将7入栈;
3.将5入栈
4.接下来是x运算符,因此弹出5和7,将35入栈
5.将6入栈;
6.最后是-运算符,得出结果
package stack;
import java.util.ArrayList;
import java.util.List;
import java.util.Stack;
public class PolandNotation {
public static void main(String[] args) {
//先定义逆波兰表达式
String suffixExpression = "3 4 + 5 * 6 - ";
//思路
//1. 先将 "3 4 + 5 * 6 - "放到ArrayList中
//1.将ArrayList传递给一个方法,遍历ArrayList 配合栈完成计算
List
int res = calculate(list);
System.out.println(res);
}
//将一个逆波兰表达式,依次将数据和运算符 放入到ArrayList中
public static List<String> getListString(String suffixExpression){
//将 suffixExpression 分割
//切割字符串,返回字符串集合
String[] split = suffixExpression.split(" ");
List<String> list = new ArrayList<String>();
//遍历字符串集合
//list集合添加元素
for(String ele :split) {
list.add(ele);
}
return list;
}
//完成对逆波兰表达式的运算
public static int calculate(List<String> ls) {
Stack<String> stack = new Stack<String>();
//遍历传递进来的ls
for(String item:ls) {
//使用正则表达式来区分符号和数字
//匹配多个数字
if(item.matches("\\d+")) {
//是数字,入栈
stack.push(item);
}else {
//是运算符,弹出来,运算
int num2 = Integer.parseInt(stack.pop());
int num1 = Integer.parseInt(stack.pop());
int res =0;
if(item.equals("+")) {
res = num1+num2;
}else if(item.equals("-")) {
res = num1 -num2;
}else if(item.equals("*")){
res = num1*num2;
}else if(item.equals("/")){
res = num1/num2;
}else {
System.out.println("不要瞎搞");
}
//把res入栈
stack.push(res+"");
}
}
//最后留在stack中的数据是运算结果
return Integer.parseInt(stack.pop());
}
}
中缀转后缀表达式思路分析
初始化两个栈:运算符s1和储存中间结果的栈s2
从左至右扫描中缀表达式;
遇到操作数时,将其压s2;
遇到运算符是比较其与s1栈顶运算符的优先级;
如果s1栈为null,或栈顶运算符为左括号,则直接将此运算符入栈;
否则,若优先级比栈顶运算符的高,也将运算符压入s1;
否则,将s1栈顶的运算符弹出并压入到s2中,再次转到(a)与s1中新的栈顶运算符相比较;
遇到括号时:
如果是左括号,则直接压入s1
如果是右扩后,则依次弹出s1栈顶的运算符,并压入s2,直到遇到左括号为止,此时将这一对括号丢弃
重复步骤2-5,直到表达式的最右边
将s1中剩余的运算符依次弹出并压入s2
依次弹出s2中的元素并输出,结果的逆序即为中缀表达式对应的后缀表达式