数组实现栈
思路
- 使用数组模拟栈
- 定义一个top标识为栈顶,初始化为-1
- 入栈操作,当有数据加入到栈时,top++ ;stack[top] = data;
- 出栈操作,value=stack[top]; top–
代码实现
/**
* 思路
* 1、使用数组模拟栈
* 2、定义一个top标识为栈顶,初始化为-1
* 3、入栈操作,当有数据加入到栈时,top++ ;stack[top] = data;
* 4、出栈操作,value=stack[top]; top--
*/
public class ArrayStackDemo {
public static void main(String[] args) {
//测试
ArrayStack arrayStack = new ArrayStack(5);
String key = "";
boolean loop = true;
Scanner scanner = new Scanner(System.in);
while (loop) {
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":
arrayStack.list();
break;
case "push":
System.out.println("请输入一个值");
int value = scanner.nextInt();
arrayStack.push(value);
break;
case "pop":
try {
int pop = arrayStack.pop();
System.out.printf("出栈数据:%d",pop);
System.out.println();
} catch (Exception e) {
// e.printStackTrace();
System.out.println(e.getMessage());
}
break;
case "exit":
scanner.close();
loop = false;
break;
default:
break;
}
}
System.out.println("程序退出。。。");
}
}
/**
* 定义一个类,表示栈
*/
class ArrayStack{
private int maxSize; //栈大小
private int[] stack; //数组模拟栈,数据放入其中
private int top = -1; //栈顶,初始化为-1
public ArrayStack(int maxSize){
this.maxSize = maxSize;
stack = new int[maxSize];
}
//判断栈满
public boolean isFull() {
return top == maxSize - 1;
}
//判断栈空
public boolean isEmpty() {
return top == -1;
}
//入栈
public void push(int value) {
//判断是否栈满
if (isFull()) {
System.out.println("栈满");
return;
}
top++;
stack[top] = value;
}
//出栈,将栈顶数据返回
public int pop() {
if (isEmpty()) {
throw new NullPointerException("栈空,没有数据");
}
int value = stack[top];
top--;
return value;
}
//遍历栈
public void list() {
if (isEmpty()) {
throw new NullPointerException("栈空");
}
for (int i = top; i >= 0; i--) {
System.out.printf("stack[%d]=%d\n",i,stack[i]);
}
}
}
综合计算器实现
表达式:
- 前缀表达式(波兰表达式)
- 中缀表达式(正常数学计算表达式)
- 后缀表达式(逆波兰表达式)
计算器实现:使用后缀表达式实现计算器计算。
中缀表达式转后缀表达式(逆波兰表达式)思路:
- 初始化两个栈: 运算符栈s1和存储中间结果的栈s2
- 从左至右扫描中缀表达式
- 遇到操作数(数字)时,将其压入s2
- 遇到运算符时,比较s1栈顶运算符的优先级
4.1、s1栈为空,或栈顶运算符为左括号“(”,则将此运算符入栈;
4.2、否则,若当前运算符优先级比栈顶优先级符号高,也将运算符压入s1
4.3、否则,将s1栈顶的运算符弹出并压入到s2中,再次转到(4.1)与s1中新的栈顶运算符比较 - 若遇到右括号
5.1、如果是左括号“(”,则压入s1
5.2、如果是右括号")",则依次弹出运算符,并压入s2,直到遇到左括号为止,此时将这一对括号丢弃 - 重复2至5的步骤,直到表达式的最右边
- 将s1中剩余的运算符依次弹出并压入s2
- 依次弹出s2中的元素并输出,结果的逆序即为中缀表达式对应的后缀表达式
代码实现:
/**
* 前缀表达式(波兰表达式)
* 中缀表达式
* 后缀表达式(逆波兰表达式)
*
* 后缀表达式(逆波兰表达式)实现 计算器
*/
/**
* 中缀表达式(正常的表达式) 转后拽表达式(逆波兰表达式)
* 1、初始化两个栈: 运算符栈s1和存储中间结果的栈s2
* 2、从左至右扫描中缀表达式
* 3、遇到操作数时,将其压入s2
* 4、遇到运算符时,比较s1栈顶运算符的优先级
* 4.1、s1栈为空,或栈顶运算符为左括号“(”,则将此运算符入栈;
* 4.2、否则,若当前运算符优先级比栈顶优先级符号高,也将运算符压入s1
* 4.3、否则,将s1栈顶的运算符弹出并压入到s2中,再次转到(4.1)与s1中新的栈顶运算符比较
* 5、若遇到右括号
* 5.1、如果是左括号“(”,则压入s1
* 5.2、如果是右括号")",则依次弹出运算符,并压入s2,直到遇到左括号为止,此时将这一对括号丢弃
* 6、重复2至5的步骤,直到表达式的最右边
* 7、将s1中剩余的运算符依次弹出并压入s2
* 8、依次弹出s2中的元素并输出,结果的逆序即为中缀表达式对应的后缀表达式
*/
public class PolandNotation {
private static String suffixExpression = "1+((2+3)*4)-5";
public static void main(String[] args) {
//先定义一个逆波兰表达式
//实例: (3+4)*5-6 => 3 4 + 5 * 6 -
//为了方便,逆波兰表达式的数字和符号用空格隔开
// String suffixExp = "3 4 + 5 * 6 -";
// //1、先将"3 4 + 5 * 6 -" 放入ArrayList中
// //2、将ArrayList 传递给一个方法,配合栈完成计算
// List<String> expList = getListStr(suffixExp);
// System.out.println(calculate(expList));
System.out.println("中缀表达式");
List<String> list = toInfixExpressionList(suffixExpression);
System.out.println(list);
System.out.println("后缀表达式");
List<String> suffixExpression = parseSuffixExpressionList(list);
System.out.println(suffixExpression);
System.out.println("计算结果");
int calculate = calculate(suffixExpression);
System.out.println(calculate);
}
/**
* 将得到的中缀表达式的List转化为后缀表达式List
* 即ArrayList[1,+,(,(,2,3,),*,4,),-,5] => ArrayList[1,2,3,+,4,*,+,5,-]
*/
public static List<String> parseSuffixExpressionList(List<String> list){
Stack<String> s1 = new Stack<>(); //符号栈
List<String> s2 = new ArrayList<>(); //存储中间结果(即后缀表达式)
//遍历中缀表达式字符集合list
for (String item : list) {
//如果是一个数字就入s2
if (item.matches("\\d+")) { //正则表达式匹配
s2.add(item);
}else if (item.equals("(")) { //左括号 直接入符号栈
s1.push(item);
}else if (item.equals(")")) { //如果是右括号
//如果是右括号")",则依次弹出运算符,并压入s2,直到遇到左括号为止,此时将这一对括号丢弃
while (true) {
String str = s1.pop();
if (str.equals("(")) { //看到与当前右括号最接近的左括号跳出循环
break;
}
if (str.equals(")") || str.equals("(")) {
continue;
}
s2.add(str);
}
}else {
//当item的优先级小于等于栈顶运算符的优先级,
//将s1栈顶的运算符弹出并加入到s2中,再次转到(4.1)与s1中新的栈顶运算符比较
while (s1.size() != 0 && Operation.getValue(s1.peek()) >= Operation.getValue(item)){
s2.add(s1.pop());
}
//还需要将item入栈s1
s1.push(item);
}
}
//把s1剩余的运算符加入s2
while (s1.size() != 0) {
s2.add(s1.pop());
}
return s2; //输出后缀表达式
}
/**
* 中缀表达式转后缀表达式,思路:
* 1、1+((2+3)*4)-5 => 1 2 3 + 4 * + 5 -
* 2、因为直接对str操作麻烦,因此现将“1+((2+3)*4)-5” =>中缀表达式转换为List,即ArrayList[1,+,(,(,2,3,),*,4,),-,5]
*/
//将中缀表达式转为List
public static List<String> toInfixExpressionList(String str) {
List<String> list = new ArrayList<>();
int i = 0; //一个指针,用于遍历中缀表达式字符串
String val = ""; //拼接多为数
char ch;
while (true) {
if (i >= str.length()) {
if (val != ""){
list.add(val);
}
break;
}
//如果ch是一个非数字,直接加入list中,0~9 的Ascall 码 48~57
if ((ch = str.charAt(i)) < 48 || (ch = str.charAt(i)) > 57) {
if (val != "") {
list.add(val);
}
val = "";
list.add(ch + "");
}else {
val += ch;
}
i++;
}
return list;
}
//完成对逆波兰表达式的运算
public static int calculate(List<String> expList) {
//创建一个栈
Stack<String> stack = new Stack<>();
expList.forEach(item -> {
//这可使用正则表达式取数据
if (item.matches("\\d+")) { //匹配多位数
stack.push(item);
}else { //运算符
//pop出两个数,并运算,再入栈
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 {
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 operation) {
int result = 0;
switch (operation){
case "+":
result = ADD;
break;
case "-":
result = SUB;
break;
case "*":
result = MUL;
break;
case "/":
result = DIV;
break;
default:
System.out.println("不存在改运算符:" + operation);
break;
}
return result;
}
}