栈结构(stack)
栈的特性
栈是先入后出的数据结构,栈顶(top)是最后插入的数据和最先删除数据(变化端)。拥有出栈(pop),入栈(push),遍历,判空,判满等操作,可以通过数组或者链表实现。
栈的应用场景
1.计算表达式的值或者中缀表达式与后缀表达式的转换
2.二叉树的遍历
3.图的深度优先搜索。
栈的数组实现(Java)
不可变容量的栈
class ArrayStack{
private int maxSize; //栈的最大容量
private int[] stack ; //栈数组(也可以存其他数据类型)
private int top=-1; //栈顶指针
//构造器
public ArrayStack(int maxSize){
this.maxSize=maxSize;
this.stack = new int[maxSize];
}
public boolean isFull(){
return top==maxSize-1;
}
public boolean idEmpty(){
return top==-1;
}
//入栈操作,先判断栈是否满
public void push(int data){
if(isFull()){
System.out.println("栈满无法加入数据");
return;
}
top++;
stack[top]=data;
}
//出栈操作,先判断栈是否为空
public int pop(){
if(idEmpty()){
throw new RuntimeException("栈空");
}
int val = stack[top];
top--;
return val;
}
//查看栈顶元素
public int peek(){
if(idEmpty()){
throw new RuntimeException("栈空");
}
return stack[top];
}
//显示操作 即从栈顶开始遍历
public void list(){
if(idEmpty()){
System.out.println("栈空");
return;
}
for(int i=top;i>=0;i--){
System.out.printf("stack[%d]=%d\n",i,stack[i]);
}
}
}
使用栈实现计算器(计算中缀表达式)
思路(无括号)
数栈:存放数
符号栈:存放符号
- 索引(index)用来遍历计算表达式:如5*6+3/3-2,(中缀表达式)
- 遍历到数时将数入数栈
- 遍历到符号时:① 若符号栈中为空则直接将符号入栈
② 若符号栈中有元素,且当前的符号优先级小于等于栈顶符号优先级,则将数栈弹出两个值,符号栈弹出一个符号进行运算(减法运算时是用后弹出的数减去先弹出的数,除法相同)。运算后的值入数栈,当前的符号入符号栈。
③ 若符号栈中有元素,且当前符号优先级大于栈顶符号优先级,则直接将符号入符号栈。 - 表达式遍历完毕后,依次弹出两个数和一个符号进行计算,计算后的值入数栈,直到最后数栈栈顶的值就是计算结果。
/* 为了方便功能实现,在上述实现的stack类中添加了三种方法
1.返回计算符优先级方法(只考虑加减乘除,其他运算也可以自己添加)
2.判断遍历到的表达式元素是否为计算符方法
3.计算两数和一个计算符方法,返回结果*/
//返回优先级
public int operPriority(char oper){
if(oper=='+'||oper=='-'){
return 0;
}else if(oper=='*'||oper=='/'){
return 1;
}else
return -1;
}
//判断索引遍历到的字符是否是运算符
public boolean isOper(char data){
return data=='+'||data=='-'||data=='*'||data=='/';
}
//计算两个数和相应的运算符
public int calculate(int num1,int num2,char oper){
int result=0; //初始计算结果;
if(oper=='+'){
result=num1+num2;
}else if(oper=='-'){
result=num2-num1; //这里注意减法的顺序
}else if(oper=='*'){
result=num1*num2;
}else if(oper=='/'){
result=num2/num1;
}
return result;
}
再按上述分析的思路完成计算中缀表达式的方法,代码如下
public static void calMidexpersion(String midexpersion){
ArrayStack numStack = new ArrayStack(20); //数栈
ArrayStack operStack = new ArrayStack(10); //计算符栈
int index = 0;
int num1 = 0; //初始化两个数,用来临时存储数栈弹出的两个操作数;
int num2 = 0;
char oper = '+'; //初始化计算符,用来临时存储弹出的操作符;
int res = 0; //初始化结果,临时存储结果;
char ch = ' '; //初始化每次从表达式中遍历到的字符;
String keepNum = ""; //初始化临时存储多位数字符串;
//遍历表达式
while (true){
ch = midexpersion.charAt(index);
//如果遍历到的字符是计算符
if(operStack.isOper(ch)){
if(!operStack.isEmpty()){ //且计算符栈不为空
//判断当前运算符和栈顶运算符的优先级
//当前优先级小于等于栈顶优先级
if(operStack.operPriority(ch)<=operStack.operPriority((char) operStack.peek())){
num1 = numStack.pop();
num2 = numStack.pop();
oper = (char) operStack.pop();
res = operStack.calculate(num1,num2,oper);
numStack.push(res);
operStack.push(ch);
}else{ //当前优先级大于栈顶优先级
operStack.push(ch);
}
}else { //计算符栈为空
operStack.push(ch);
}
}else { //如果遍历到的字符是数
keepNum+=ch;
//注意边界条件,不要越界
if(index==midexpersion.length()-1){
numStack.push(Integer.parseInt(keepNum));
keepNum=""; //清空
}else {
//如果当前数的表达式后一位是计算符则直接将数入栈,
//如果数的后一位还是数则继续扫描遍历
if (operStack.isOper(midexpersion.charAt(index + 1))) {
numStack.push(Integer.parseInt(keepNum));
keepNum = ""; //清空
}
}
}
index++;
if(index >=midexpersion.length()){
break;
}
}
//将数栈和计算符栈依次弹出进行计算
while (true){
if(operStack.isEmpty()){
break;
}
num1=numStack.pop();
num2=numStack.pop();
oper=(char)operStack.pop();
res = numStack.calculate(num1,num2,oper);
numStack.push(res);
}
System.out.printf("%s=%d",midexpersion,numStack.peek());
}
小结
上述代码还有很多可以改进的地方:在用数组实现栈的时候,数组类型可以考虑使用泛型,这样使用的范围更广泛。在实现计算中缀表达式的时候,尚未考虑到输入错误字符的情况,代码健壮性不足。
本文介绍了栈的特性,包括先入后出的数据结构,以及常见应用场景如计算表达式、二叉树遍历和图的深度优先搜索。详细讲解了使用Java数组实现栈,并展示了如何利用栈来实现计算器,解析中缀表达式,同时提供了思路和简化版代码。文章结尾指出,实际应用中还需考虑数组类型的泛型化以及输入错误处理的健壮性问题。
807

被折叠的 条评论
为什么被折叠?



