文章目录
前言
说起栈,我们容易想到可以用其来进行表达式的求解;相比较于队列,它们的特征完全相反,栈是先进后出的有序队列,详细介绍如下:
一、栈的介绍
1.简介:
- 栈是一个先入后出的有序列表。
- 栈是限制线性表中元素的插入和删除只能在线性表的同一端进行的一种特殊线性表。
允许插入和删除的一端,为变化的一端,称为栈顶,另一端为固定的一端,称为栈底。 - 根据栈的定义,最先放入栈中元素在栈底,最后放入的元素在栈顶;
- 删除元素刚好相反,最后放入的元素最先删除,最先放入的元素最后删除;
2.应用场景:
1. 表达式的转换[中缀表达式转后缀表达式]与求值。
2. 子程序的调用:在跳往子程序前,会先将下个指令的地址存到堆栈中。
直到子程序执行完后再将地址取出,以回到原来的程序中。
3. 处理递归调用:和子程序的调用类似,只是除了储存下一个指令的地址外
也将参数、区域变量等数据存入堆栈中。
4. 二叉树的遍历。
5. 图形的深度优先搜索法。
二、栈的思路分析(数组)及代码实现
1. 栈的类的代码实现:
实现 栈的 思路分析
1. 使用数组来模拟栈
2. 定义一个 top 来表示栈顶,初始化 为 -1
3. 入栈的操作,当有数据加入到栈时, top++; stack[top] = data;
4. 出栈的操作, int value = stack[top]; top--, return value
5. 其实就像图片上所示:
代码如下:
class ArrayStack{
private int maxSize;//栈的大小
private int[] stack;//数组模拟栈
private int top = -1;//初始化为-1
//构造器(栈的大小)
public ArrayStack(int maxSize){
this.maxSize = maxSize;
this.stack = new int[maxSize];
}
}
2.栈满的判断:
代码如下:
//栈满
public boolean isFull(){
return (top == maxSize - 1);
}
3.栈空的判断:
代码如下:
//栈空
public boolean isEmpty(){
return (top == -1);
}
4.入栈:
1. 先判断栈是否满;
2. 栈不满的话可以添加数据
3. top++
4. 取值
代码如下:
public void push(int data){
//先判断是否满
if(isFull()){
System.out.println("栈满, 入栈失败...");
return;
}
top++;//指向下个空白的空间,以便入栈
stack[top] = data;//存入栈数组
}
5.出栈:
1. 判断栈是否为空
2. 栈不为空的话可以出栈
3. 先取栈顶元素(最后返回)
4. 再将指针top下移即可(使栈顶数据丢失)
代码如下:
public int pop(){
if(isEmpty()){
throw new RuntimeException("栈空,出栈失败...");
}
return stack[top--];
}
6.栈的遍历:
1. 判空
2. 从栈顶开始一个一个访问输出即可
代码如下:
public void show(){
if(isEmpty()){
System.out.println("栈空.....");
return;
}
for(int i = top ; i > -1 ; i--){
System.out.printf("stack[%d] = %d\n" , i , stack[i]);
}
}
三、用栈实现表达式求解
1.思路分析:
1. 通过一个 index 值(索引),来遍历我们的表达式
2. 如果我们发现是一个数字, 就直接入数栈
3. 如果发现扫描到是一个符号, 就分如下情况
3.1 如果发现当前的符号栈为 空,就直接入栈
3.2 如果符号栈有操作符,就进行比较,如果当前的操作符的优先级小于或者等于栈中的操作符, 就需要从数栈中pop出两个数;
同时在从符号栈中pop出一个符号进行运算,将得到结果,入数栈,然后将当前的操作符入符号栈;
如果当前的操作符的优先级大于栈中的操作符, 就直接入符号栈.
4. 当表达式扫描完毕,就顺序的从 数栈和符号栈中pop出相应的数和符号,并运行.
5. 最后在数栈只有一个数字,就是表达式的结果
2.设置并获取运算符优先级的方法:
根据上面的分析可知,为了实现表达式的求解,我们需要完成一个获取运算符优先级的方法:
//返回运算符的优先级(数字越大, 优先级越高)
public int priority(int oper){
if(oper == '*' || oper == '/'){
return 1;
}
if(oper == '+' || oper == '-'){
return 0;
}
return -1;
}
3.判断字符是否是运算符:
public boolean isOper(int oper){
if(oper == '+' || oper == '-' || oper == '*' || oper == '/'){
return true;
}
return false;
}
4.进行计算的方法:
这里需要注意,先出栈的是第二操作数,后出栈的是第一操作数;
所以在此我们用num2 / num1;及num2- num1
public int calculate(int num1 , int operator , int num2){
int res = 0;
switch (operator){
case '+':
res = num2 + num1;
break;
case '-':
res = num2 - num1;
break;
case '*':
res = num2 * num1;
break;
case '/':
res = num2 / num1;
break;
default:
break;
}
return res;
}
对于表达式的处理及计算:
ch用来接收每次的扫描的字符;
keeper是用来处理大于9的数字计算;
代码逻辑就是上面的思路分析一致:
1. 通过一个 index 值(索引),来遍历我们的表达式
2. 如果我们发现是一个数字, 就直接入数栈
3. 如果发现扫描到是一个符号, 就分如下情况
3.1 如果发现当前的符号栈为 空,就直接入栈
3.2 如果符号栈有操作符,就进行比较,如果当前的操作符的优先级小于或者等于栈中的操作符, 就需要从数栈中pop出两个数;
同时在从符号栈中pop出一个符号进行运算,将得到结果,入数栈,然后将当前的操作符入符号栈;
如果当前的操作符的优先级大于栈中的操作符, 就直接入符号栈.
4. 当表达式扫描完毕,就顺序的从 数栈和符号栈中pop出相应的数和符号,并运行.
5. 最后在数栈只有一个数字,就是表达式的结果
public static void main(String[] args) {
String str = "1*14+13/1";
//两个栈:
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 = ' '; //每次扫描的结果
String keeper = ""; //拼接多位数
while(true){
ch = str.substring(index , index + 1).charAt(0);
if(operStack.isOper(ch)){//是操作符
if(operStack.isEmpty()){
operStack.push(ch);
}else{
if(operStack.priority(ch) <= operStack.peak()){
num1 = numStack.pop();
num2 = numStack.pop();
oper = operStack.pop();
res = numStack.calculate(num1 , oper , num2);
numStack.push(res);
operStack.push(ch);
}else{
operStack.push(ch);
}
}
}else{//是数字,直接入数字栈
keeper += ch;
//如果ch已经是exception的最后一位就直接入栈
if(index == str.length() - 1){
numStack.push(Integer.parseInt(keeper));
keeper= "";
}else{
//判断下位数是否为数字,为数字就继续拼接,否则入数栈
if(operStack.isOper(str.charAt(index + 1))){
numStack.push(Integer.parseInt(keeper));
keeper= "";
}
}
}
index = index + 1;
if(index >= str.length()){
break;
}
}
//表达式扫描完,当operStack中为空的时候,数栈里面就是结果
while(true){
if(operStack.isEmpty()){
break;
}
//符号栈不为空,继续进行运算:
num1 = numStack.pop();
num2 = numStack.pop();
oper = operStack.pop();
res = numStack.calculate(num1 , oper , num2);
//结果入栈
numStack.push(res);
}
System.out.printf("%s = %d" , str , numStack.pop());
}
总结
主要是对于多位数的处理需要思考一下,其它的按照逻辑来就可以;完整代码如下:
package 栈;
/**
* @author Weilei Zhang
*/
public class Calculator {
public static void main(String[] args) {
String str = "1*14+13/1";
//两个栈:
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 = ' '; //每次扫描的结果
String keeper = ""; //拼接多位数
while(true){
ch = str.substring(index , index + 1).charAt(0);
if(operStack.isOper(ch)){//是操作符
if(operStack.isEmpty()){
operStack.push(ch);
}else{
if(operStack.priority(ch) <= operStack.peak()){
num1 = numStack.pop();
num2 = numStack.pop();
oper = operStack.pop();
res = numStack.calculate(num1 , oper , num2);
numStack.push(res);
operStack.push(ch);
}else{
operStack.push(ch);
}
}
}else{//是数字,直接入数字栈
keeper += ch;
//如果ch已经是exception的最后一位就直接入栈
if(index == str.length() - 1){
numStack.push(Integer.parseInt(keeper));
keeper= "";
}else{
//判断下位数是否为数字,为数字就继续拼接,否则入数栈
if(operStack.isOper(str.charAt(index + 1))){
numStack.push(Integer.parseInt(keeper));
keeper= "";
}
}
}
index = index + 1;
if(index >= str.length()){
break;
}
}
//表达式扫描完,当operStack中为空的时候,数栈里面就是结果
while(true){
if(operStack.isEmpty()){
break;
}
num1 = numStack.pop();
num2 = numStack.pop();
oper = operStack.pop();
res = numStack.calculate(num1 , oper , num2);
numStack.push(res);
}
System.out.printf("%s = %d" , str , numStack.pop());
}
}
class ArrayStack2 {
private int maxSize;
private int[] stack;
private int top = -1;
//构造器
public ArrayStack2(int maxSize) {
this.maxSize = maxSize;
this.stack = new int[maxSize];
}
//栈空
public boolean isEmpty() {
return (top == -1);
}
//栈满
public boolean isFull() {
return (top == maxSize - 1);
}
//入栈;
public void push(int data) {
//先判断是否满
if (isFull()) {
System.out.println("栈满, 入栈失败...");
return;
}
top++;
stack[top] = data;
}
//出栈;
public int pop() {
if (isEmpty()) {
throw new RuntimeException("栈空,出栈失败...");
}
return stack[top--];
}
//显示数据:
public void show() {
if (isEmpty()) {
System.out.println("栈空.....");
return;
}
for (int i = top; i > -1; i--) {
System.out.printf("stack[%d] = %d\n", i, stack[i]);
}
}
//返回运算符的优先级(数字越大, 优先级越高)
public int priority(int oper){
if(oper == '*' || oper == '/'){
return 1;
}
if(oper == '+' || oper == '-'){
return 0;
}
return -1;
}
//判断是否为运算符
public boolean isOper(int oper){
if(oper == '+' || oper == '-' || oper == '*' || oper == '/'){
return true;
}
return false;
}
//计算的方法:
public int calculate(int num1 , int operator , int num2){
int res = 0;
switch (operator){
case '+':
res = num2 + num1;
break;
case '-':
res = num2 - num1;
break;
case '*':
res = num2 * num1;
break;
case '/':
res = num2 / num1;
break;
default:
break;
}
return res;
}
//返回当前栈顶的值,不出栈
public int peak(){
return stack[top];
}
}