


实现栈的思路分析
数组模拟栈


//数组实现栈
package com.cheung.stack;
public class ArrayStackDemo {
public static void main(String[] args) {
ArrayStack arrayStack = new ArrayStack(10);
arrayStack.push(2);
arrayStack.push(5);
arrayStack.push(4);
arrayStack.pop();
arrayStack.list();
}
}
//定义一个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;
}
//入栈
public void push(int value){
if(isFull()){
System.out.println("Full!");
return;
}
top += 1;
stack[top] = value;
}
//出栈
public int pop(){
if(isEmpty()){
throw new RuntimeException("栈空");
}
int value = stack[top];
top -= 1;
return value;
}
//查看栈内元素情况
public void list(){
if(isEmpty()){
System.out.println("栈空!");
}
for(int i = top; i >= 0; i--){
System.out.printf("stack[%d] = %d\n",i,stack[i]);
}
}
}
栈实现综合计算器 中缀表达式


//没有考虑括号问题
package com.cheung.stack;
public class Calculator {
public static void main(String[] args) {
//根据前面老师思路,完成表达式的运算
String expression = "30+2*6-2";
//创建两个栈,一个数栈,一个符号栈
ArrayStack2 numStack = new ArrayStack2(20);
ArrayStack2 operStack = new ArrayStack2(20);
//定义需要扫描字符串的相关遍历
int index = 0;
int num1 = 0;
int num2 = 0;
int oper = 0;
int res = 0;
char ch = ' '; //将每次扫描得到的char保存到ch
String keepNum = ""; //用于拼接多位数
//开始用while循环扫描字符串
while(true){
//依次得到expression的每一个字符
ch = expression.substring(index, index + 1).charAt(0);
//判断是数字还是符号 符号比较
if(numStack.isOper(ch)){ //是不是运算符
if(!operStack.isEmpty()){
//不为空继续比较,分两种情况
//当前ch优先级小于等于操作数栈栈顶元素就做运算
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 {
//当前ch优先级大于操作数栈栈顶元素就做入栈
operStack.push(ch);
}
}else{
//为空直接入栈
operStack.push(ch);
}
}else {
//是数字,先判断是不是多位数,判断思路:
//1 在处理数时,需要向字符串的index下标位置再往后看一位,如果是数字就进行扫描,如果是符号才入栈。
//2 因此我们定义一个变量字符串,用于拼接
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 = ""; //一定要清空!!!!
}
}
//让index + 1,并判断是否扫描到字符串末尾
index ++;
if(index >= expression.length()){
break;
}
}
//当表达式扫描完毕,就顺序从数栈和符号栈中pop出相应的数和符号,并运行。
while (true){
//如果符号栈为空,则计算到了最后的结果,数栈中就只有一个数字了
if(operStack.isEmpty()){
break;
}else {
num1 = numStack.pop();
num2 = numStack.pop();
oper = operStack.pop();
res = numStack.cal(num1, num2, oper);
//操作结果入数栈
numStack.push(res);
}
}
System.out.println("运算结果为:" + numStack.pop());
}
}
//先创建一个栈
//定义一个ArrayStack 表示一个栈
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 boolean isFull(){
return top == maxSize - 1;
}
//栈空
public boolean isEmpty(){
return top == -1;
}
//入栈
public void push(int value){
if(isFull()){
System.out.println("Full!");
return;
}
top += 1;
stack[top] = value;
}
//出栈
public int pop(){
if(isEmpty()){
throw new RuntimeException("栈空");
}
int value = stack[top];
top -= 1;
return value;
}
//查看栈内元素情况
public void list(){
if(isEmpty()){
System.out.println("栈空!");
}
for(int i = top; i >= 0; i--){
System.out.printf("stack[%d] = %d\n",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;
}
//偷瞄一眼栈顶元素
public int peek(){
return this.stack[top];
}
}
前缀表达式(波兰表达式)


中缀表达式

后缀表达式(逆波兰表达式)


完成逆波兰计算器

package com.cheung.stack;
import java.util.ArrayList;
import java.util.List;
import java.util.Stack;
public class PolandNotation {
public static void main(String[] args) {
//先定义一个逆波兰表达式
// (3+4)*5-6 ----> 3 4 + 5 * 6 -
//为了方便,逆波兰表达式的数字和符号使用空格隔开
String suffixExpression = "3 4 + 5 * 6 -";
//思路
//1 先将suffixExpression放到ArrayList中
//2 将ArrayList传递给一个方法,配合栈完成计算
List<String> rpnList = getListString(suffixExpression);
// System.out.println(rpnList);
System.out.println(calculate(rpnList));
}
//将一个逆波兰表达式,依次将数据和运算符放入到ArrayList中
public static List<String> getListString(String str){
String[] split = str.split(" ");
ArrayList<String> list = new ArrayList<String>();
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("+".equals(item)){
res = num1 + num2;
}else if("-".equals(item)){
res = num1 - num2;
}else if("*".equals(item)){
res = num1 * num2;
}else if("/".equals(item)){
res = num1 / num2;
}else {
throw new RuntimeException("运算符有误");
}
stack.push(String.valueOf(res));
}
}
return Integer.parseInt(stack.pop());
}
}
中缀表达式转后缀表达式




package com.cheung.stack;
import java.util.ArrayList;
import java.util.List;
import java.util.Stack;
public class PolandNotation {
public static void main(String[] args) {
//完成一个将中缀表达式转成后缀表达式的功能
//说明
//1. 1+((2+3)*4)-5 => 转成 1 2 3 + 4 * + 5 -
//2. 因为直接对一个字符串进行操作不太方便,因此我们先将字符串转为中缀表达式对应的List
// 即把"1+((2+3)*4)-5 " => ArrayList[1,+,(,(,2,+,3,),*,4,),-,5]
//3. 将得到的中缀表达式对应的List => 后缀表达式对应的List
// 即 ArrayList[1,+,(,(,2,+,3,),*,4,),-,5] => ArrayList[1,2,3,+,4,*,+,5,-]
String expression = "1+((2+3)*4)-5";
List<String> infixExpressionList = toInfixExpressionList(expression);
System.out.println(infixExpressionList);
List<String> suffixExpressionList = parseSuffixExpressionList(infixExpressionList);
System.out.println(suffixExpressionList);
System.out.println("计算结果为" + calculate(suffixExpressionList));
}
//方法 将得到的中缀表达式对应的List => 后缀表达式对应的List
// 即 ArrayList[1,+,(,(,2,+,3,),*,4,),-,5] => ArrayList[1,2,3,+,4,*,+,5,-]
public static List<String> parseSuffixExpressionList(List<String> ls){
//定义符号栈
Stack<String> s1= new Stack<>();
//说明:因为s2这个栈在整个转换过程中没有pop操作,而且我们还需要逆序输出
//因此比较麻烦,这里我们用List<String>
// Stack<String> s2= new Stack<>();
List<String> s2 = new ArrayList<String>(); //储存中间结果的Lists2
//遍历ls
for(String item : ls){
//如果是一个数,加入s2
if(item.matches("\\d+")){
s2.add(item);
}else if("(".equals(item)){ //如果是一个左括号,也直接入栈
s1.push(item);
}else if(")".equals(item)){ //如果是右括号,则依次弹出s1栈顶的运算符,并压入s2,直到遇到左括号为止,此时将这一对括号丢弃
while (!s1.peek().equals("(")){
s2.add(s1.pop());
}
s1.pop();
}else{
//考虑运算符优先级
//当item的优先级小于等于s1栈顶运算符,将s1栈顶的运算符弹出并加入到s2中,再次转到(4.1)情况,与s1栈顶运算符相比较。
//缺少一个比较优先级高低的方法
while (s1.size() != 0 && Operation.getValue(s1.peek()) >= Operation.getValue(item)){
s2.add(s1.pop());
}
//最后还需要将item压入栈
s1.push(item);
}
}
//将s1剩余运算符加入s2中
while(s1.size() != 0){
s2.add(s1.pop());
}
return s2; //因为是存放在List中,因此按顺序输出就是对应的逆波兰表达式
}
//方法:将中缀表达式转为对应的List
public static List<String> toInfixExpressionList(String s){
//定义一个List,存放中缀表达式 对应的内容
List<String> ls = new ArrayList<String>();
int i = 0;//这是一个指针,用于遍历s
String str; //对多位数对拼接操作
char c; //每遍历一个字符,就放入到c
do{
//如果c是一个非数字,我们就需要加入到ls
if((c = s.charAt(i)) < 48 || (c = s.charAt(i)) > 57){
ls.add(String.valueOf(c));
i++;
}else { //如果是一个数字,需要考虑多位数对问题
str = ""; //先将str 置空
while(i < s.length() && (c = s.charAt(i)) >= 48 && (c = s.charAt(i)) <= 57){
str += c; //拼接
i++;
}
ls.add(str);
}
}while (i < s.length());
return ls;
}
//将一个逆波兰表达式,依次将数据和运算符放入到ArrayList中
public static List<String> getListString(String str){
String[] split = str.split(" ");
ArrayList<String> list = new ArrayList<String>();
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("+".equals(item)){
res = num1 + num2;
}else if("-".equals(item)){
res = num1 - num2;
}else if("*".equals(item)){
res = num1 * num2;
}else if("/".equals(item)){
res = num1 / num2;
}else {
throw new RuntimeException("运算符有误");
}
stack.push(String.valueOf(res));
}
}
return Integer.parseInt(stack.pop());
}
}
//编写一个类,专门比较运算符 优先级高低
class Operation{
private static int ADD = 1;
private static int SUB = 1;
private static int MUL = 2;
private static int DIV = 2;
//写一个方法,返回对应的优先级数字
public static int getValue(String oper){
int result = 0;
switch (oper){
case "+":
result = ADD;
break;
case "-":
result = SUB;
break;
case "*":
result = MUL;
break;
case "/":
result = DIV;
break;
default:
System.out.println("奇怪的运算符");
break;
}
return result;
}
}