【数据结构与算法】数组实现栈,单链表实现栈,用栈实现综合运算器calculator,前缀表达式,中缀表达式,后缀表达式计算机求值过程,中缀表达式转后缀表达式

数组实现栈:

/**
 * 使用数组来模拟栈

 * 
 * */
package dataStructure;

import java.util.Scanner;

public class ArrayStackDemo {
	public static void main(String[] args) {
		Stack stack = new Stack(4);
		Scanner sc = new Scanner(System.in);
		String key = "";
		boolean flag = true;
		while(flag) {
			System.out.println("1:代表显示栈元素");
			System.out.println("2:代表元素入栈");
			System.out.println("3:代表元素出栈");
			System.out.println("4:表示退出菜单");
			System.out.println("请输入需要操作的菜单代号:");
			key = sc.next();
			switch(key) {
			case "1":
				stack.display();
				break;
			case "2":
				System.out.println("请输入需要入栈的元素");
				int value = sc.nextInt();
				stack.push(value);
				break;
			case "3":
				try {
					System.out.printf("stack[%d] = %d\n",stack.getTop(),stack.pop() );
				}catch(RuntimeException e) {
					e.printStackTrace();
				}
				break;
			case "4":
				flag = false;
				break;
			default:
				break;
			}
		}
	}
}
//栈类
class Stack{
	private int maxSize;
	private int[] stack;
	private int top = -1;
	public Stack(int maxSize) {
		this.maxSize = maxSize;
		stack = new int[maxSize];
	}
	public int getTop() {
		return top;
	}
	//栈满
	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 RuntimeException("栈空,没有数据");
		}
		int value = stack[top];
		top--;
		return value;
	}
	//遍历栈
	public void display() {
		if(isEmpty()) {
			System.out.println("栈空");
			return;
		}
		for(int i = top;i >= 0;i--) {
			System.out.printf("stack[%d] = %d\n",i,stack[i]);
		}
	}
}

单链表实现栈:

/**
 * 使用单链表实现栈
 * 
 * */
package dataStructure;

public class LinkedListStackDemo {
	public static void main(String[] args) {
		LinkedStack list = new LinkedStack();
		list.push(10);
		list.push(20);
		list.push(30);
		list.push(40);
		list.display();
		System.out.println("出栈:");
		list.pop();
		System.out.println("遍历:");
		list.display();
		System.out.println("查看栈顶元素");
		list.peek();
		ListNode node = new ListNode(100);
		System.out.println(node.toString());
	}
}

//链表模拟栈类
class LinkedStack{
	int count = 0;//栈内有效元素个数
	private ListNode top = new ListNode(-1);
	
	//判空
	public boolean isEmpty() {
		return top.getData() == -1;
	}
	//入栈,采用的是头插法
	public void push(int value) {
		//while循环出来之后说明已经找到了最后,并且top移动到链表尾部的位置上
		ListNode newNode = new ListNode(value);
		if(top.getNext() == null) {//插入第一个元素
			top.setNext(newNode);
			count = 1;
			return;
		}
		ListNode cur = top.getNext();
		top.setNext(newNode);
		newNode.setNext(cur);
		count++;
	}
	//出栈
	public void pop() {
		System.out.printf("出栈结点为第%d号元素:%d\n",count,top.getNext().getData());
		top = top.getNext();
		count--;
	}
	//遍历栈
	public void display() {
		if(top.getNext() == null) {
			System.out.println("栈为空~~~");
			return;
		}
		ListNode temp = top;
		int i = count;
		while(temp.getNext() != null) {
			System.out.printf("栈的第%d个元素为:%d\n",i,temp.getNext().getData());
			temp = temp.getNext();
			i--;
		}
		
	}
	//查看栈顶元素
	public void peek() {
		if(isEmpty()) {
			System.out.println("栈为空~~");
		}
		System.out.printf("此时栈顶元素为第%d号%d\n",count,top.getNext().getData());
	}
}


//结点类
class ListNode{
	private int data;
	private ListNode next;
	public ListNode(int data) {
		this.data = data;
	}
	public int getData() {
		return data;
	}
	public void setData(int data) {
		this.data = data;
	}
	public ListNode getNext() {
		return next;
	}
	public void setNext(ListNode next) {
		this.next = next;
	}
	public String toString() {
		return "ListNode{data="+data+"}";
	}
}

用栈实现综合运算器:calculator.中缀表达式

/**
 * 使用数组来模拟栈
综合计算器:
使用栈完成表达式的计算思路
1.通过一个index值(索引) ,来遍历表达式
2.如果我们发现是一个数字,就直接入数字栈
3.如果发现扫描到是一个符号,就分如下情况
	3.1如果发现当前的符号栈为空,就直接入栈
	3.2如果符号栈有操作符,就进行比较如果当前的操作符的优先级小于或者等于栈中
	的操作符,就需要从数字栈中pop出两个数在从符号栈中pop出一个符号, 进行运算,
	将得到结果,入数栈,然后将当前的操作符入符号栈,如果当前的操作符的优先级大
	于栈中的操作符,就直接入符号栈。
4.当表达式扫描完毕,就顺序的从数栈和符号栈中pop出相应的数和符号,并运行.
5.最后在数栈只有一个数字, 就是表达式的结果

 * */
package dataStructure;

import java.util.Scanner;

public class ArrayStackDemo {
	public static void main(String[] args) {
		//表达式
		String expression = "70+2*6-4";
		Stack numStack = new Stack(10);//用于存放数字
		Stack operStack = new Stack(10);//用于存放运算符
		int num1 = 0;
		int num2 = 0;
		int oper = 0;
		char ch =' ';
		int index = 0;
		int res = 0;//存放运算结果
		String KeepNum = "";//用于拼接多位数
		while(true) {
			//依次得到expression的每一个字符
			ch = expression.substring(index, index+1).charAt(0);
			if(operStack.isOper(ch)) {//说明是一个运算符
				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 {//符号栈为空
					operStack.push(ch);
				}
			}else {//说明不是运算符,则是一个数字
				/**
				 * 当expression中存在的数字是多位数时,不能立即入栈
				 * 在处理时,需要向expression的表达式的index后再看一位,如果是数字就继续扫描,直到遇到运算符才入栈
				 * */
				KeepNum += ch;
				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
			index++;
			//判断是否扫描到最后
			if(index >= expression.length()) {
				break;
			}
		}
		//当表达式扫描完毕,就顺序的从数字栈和符号栈中弹出相应的符号,并运行
		while(true) {
			//如果符号栈为空,则数字栈中剩下的元素就是最终结果
			if(operStack.isEmpty()) {
				break;
			}
			num1 = numStack.pop();
			num2 = numStack.pop();
			oper = operStack.pop();
			res = numStack.cal(num1, num2, oper);
			numStack.push(res);
		}
		//弹出栈最后一个元素
		int res2 = numStack.pop();
		System.out.printf("表达式%s = %d", expression,res2);
	}
}
//栈类
class Stack{
	private int maxSize;
	private int[] stack;
	private int top = -1;
	public Stack(int maxSize) {
		this.maxSize = maxSize;
		stack = new int[maxSize];
	}
	public int getTop() {
		return top;
	}
	//栈满
	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 RuntimeException("栈空,没有数据");
		}
		int value = stack[top];
		top--;
		return value;
	}
	//遍历栈
	public void display() {
		if(isEmpty()) {
			System.out.println("栈空");
			return;
		}
		for(int i = top;i >= 0;i--) {
			System.out.printf("stack[%d] = %d\n",i,stack[i]);
		}
	}
	//查看栈顶元素
	public int peek() {
		return stack[top];
	}
	//返回运算符的优先级,优先级是开发者定义,优先级使用数字表示
	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 - 后缀表达式
前缀表达式:
	又称波兰表达式,前缀表达式的运算符位于操作数之前
​	从右至左扫描表达式,遇到数字时,将数字压入堆栈,遇到运算符时,弹出栈项的两个数,用运算符对它们做相应的计算(栈项元素和次顶元素),并将结果入栈;重复上述过程直到表达式最左端,最后运算得出的值即为表达式的结果
中缀表达式:

中缀表达式就是常见的运算表达式

后缀表达式计算机求值过程:
	后缀表达式:又称逆波兰表达式。与前缀表达式相似,只是运算符位于操作数之后
​	从左至右扫描表达式,遇到数字时,将数字压入堆钱,遇到运算符时,弹出栈项的两个数,用运算符对它们做相应的计算(次项元素和栈项元素),并将结果入栈:重复上述过程直到表达式最右端,最后运算得出的值即为表达式的结果
​	例如:
(30+4)*5-6 -> "30 4 + 5 * 6 -"
4*5-8+60+8/2 -> "4 5 * 8 - 60 + 8 2 / +"
//例:
package dataStructure;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Stack;
public class SuffixDemo {
	public static void main(String[] args) {
		//后缀表达式:为方便计算用“ ”隔开
		String expression = "4 5 * 8 - 60 + 8 2 / +";
		List<String> list = transition(expression);
		System.out.println(list.toString());
		System.out.println(test(list));
	}
	//将出入的后缀表达式字符串转换成一个List集合
	public static List<String> transition(String str){
		List<String> list = new ArrayList<>();
		String[] s = str.split(" ");
		for(String data:s) {
			list.add(data);
		}
		return list;
	}
	public static int test(List<String> list) {
		Stack<String> stack = new Stack<>();
		for(String s:list) {
			if(s.matches("\\d+")) {//正则表达式,匹配的是多位数
				stack.push(s);
			}else {
				int num2 = Integer.parseInt(stack.pop());
				int num1 = Integer.parseInt(stack.pop());
				int res = 0;
				if(s.equals("*")) {
					res = num1 * num2;
				}else if(s.equals("+")) {
					res = num1 + num2;
				}else if(s.equals("-")) {
					res = num1 - num2;
				}else if(s.equals("/")) {
					res = num1 / num2;
				}else {
					throw new RuntimeException("运算符有误");
				}
				stack.push(""+res);
			}
			
		}
		return Integer.parseInt(stack.pop());
	}

}

中缀表达式转后缀表达式
中缀表达式方便人操作,后缀表达式方便计算机计算,但是人不太容易写出。
思路:
	1.初始化两个栈:运算符栈s1和和存储中间结果的栈s2
	2:从左向右扫描中缀表达式,遇到操作数时,压入栈s2.
	3:遇到运算符时,比较其与s1栈顶运算符的优先级;
		(1)如果s1为空,或栈顶运算符为‘(’,则直接将此运算符入栈
		(2)否则,若优先级比栈顶运算高,也将此运算符入栈s1
		(3)否则,将s1栈顶的运算符弹出并压入到s2中,再次转到(1)与s1中新的栈顶运算符相比较
	5遇到括号时:
		(1)如果是左括号“(", 则直接压入s1
		(2)如果是右括号“)”,则依次弹出s1栈项的运算符,并压入s2,直到遇到左括号为止,此时将这一对括号丢弃
	6重复步骤25,直到表达式的最右边
	7将s1中剩余的运算符依次弹出并压入s2
	8依次弹出s2中的元素并输出,结果的逆序即为中缀表达式对应的后缀表达式

例:

/**
 * 中缀表达式转后缀表达式
 * 以计算1+((2+3)*4)-5为例
 * 步骤:
 * 1.将中缀表达式存入一个ArrayList中 即ArrayList [1,+,(,(,2,+,3,),*,4,),-,5]
 * 2.按照转换方法步骤将前缀的list转换成后缀的list
 * 		即ArrayList [1,+,(,(,2,+,3,),*,4,),-,5] =》ArrayList [1,2,3,+,4,*,+,5,-]
 * */
package dataStructure;

import java.util.ArrayList;
import java.util.List;
import java.util.Stack;

public class ReversePolish {
	public static void main(String[] args) {
		//定义一个中缀表达式
		String expression = "1+((2+3)*4)-5";
		List<String> list = toInfixExpressionList(expression);
		List<String> list2 = parseSuffixExpressionList(list);
		System.out.println("中缀表达式转换成List集合:"+list.toString());
		System.out.println("中缀表达式转换成后缀表达式List集合:"+list2.toString());
		System.out.println("运算结果是:"+operation(list2));
        /**
         存在错误的运算符号!!!
		存在错误的运算符号!!!
		中缀表达式转换成List集合:[1, +, (, (, 2, +, 3, ), *, 4, ), -, 5]
		中缀表达式转换成后缀表达式List集合:[1, 2, 3, +, 4, *, +, 5, -]
		运算结果是:16
        */
	}
	//------------------------------------------------------------------------------
	//将定义的中缀表达式中的数据存入一个List中
	public static List<String> toInfixExpressionList(String s){
		List<String> list = new ArrayList<String>();
		int index = 0;//用于遍历中缀表达式字符串的指针
		String str;//用于拼接多位数
		char c;//用于暂时存储遍历到的字符
		do {
			//如果是一个非数,则直接加入到list中
			if((c = s.charAt(index)) < 48 || (c = s.charAt(index)) > 57) {
				list.add(""+c);
				index++;
			}else {//如果是一个数,这里需要考虑是否是多位数
				str = "";//将str置为空,考虑考虑到可能上一次使用过
				while(index < s.length() && (c = s.charAt(index)) >= 48 && (c = s.charAt(index)) <= 57) {
					str += c;
					index++;
				}
				list.add(str);
			}
		}while(index < s.length());
		return list;
	}
	//--------------------------------------------------------------------------
	//将得到的中缀表达式对应ArrayList转换成后缀表达式的ArrayList
	public static List<String> parseSuffixExpressionList(List<String> ls){
		//定义两个数据结构。一个栈s1用于存储操作符。一个list集合s2用于存储中间结果,这里不用栈的原因是
		//这个如果是栈,在操作过程中没有弹栈操作,而且最后还需要逆序输出,所以使用list存储更方便,后缀表
		//达式的list按顺序输出即是转换成功后的后缀表达式
		Stack<String> s1 = new Stack<>();
		List<String> s2 = new ArrayList<>();
		//遍历ls
		for(String s : ls) {
			if(s.matches("\\d+")) {//如果是一个数,则加入s2
				s2.add(s);
			}else if(s.equals("(")) {//如果是一个(,加入s1
				s1.push(s);
			}else if(s.equals(")")) {//如果是一个右括号
				//如果是右括号“)”,则依次弹出s1栈顶的运算符,并压入s2,直到遇到左括号为止,此时将这一对括号丢弃
				while(!s1.peek().equals("(")) {
					s2.add(s1.pop());
				}
				s1.pop();//此时弹出栈的是while循环里遇到的那个(。pop之后将其丢弃
			}else {//上面条件都不满足,说明是运算符号
				//当s的优先级小于等于s1栈顶元素的优先级的时候,将s1栈顶的运算符弹出并加入到S2中
				while(s1.size() != 0 && Operator.test(s) <= Operator.test(s1.peek())) {
					s2.add(s1.pop());
				}
				//再将s这个运算符压入栈
				s1.push(s);
			}
		}
		//当中缀表达式的list集合遍历完之后,还需将s1中剩余的运算符依次弹出并加入s2中
		while(s1.size() != 0) {
			s2.add(s1.pop());
		}
		return s2;
	}
    //--------------------------------------------------------------------------
	//后缀表达式的运算方法
	public static int operation(List<String> list) {
		Stack<String> stack = new Stack<>();
		for(String s:list) {
			if(s.matches("\\d+")) {//正则表达式,匹配的是多位数
				stack.push(s);
			}else {
				int num2 = Integer.parseInt(stack.pop());
				int num1 = Integer.parseInt(stack.pop());
				int res = 0;
				if(s.equals("*")) {
					res = num1 * num2;
				}else if(s.equals("+")) {
					res = num1 + num2;
				}else if(s.equals("-")) {
					res = num1 - num2;
				}else if(s.equals("/")) {
					res = num1 / num2;
				}else {
					throw new RuntimeException("运算符有误");
				}
				stack.push(""+res);
			}
			
		}
		return Integer.parseInt(stack.pop());
	}

}
//--------------------------------------------------------------------------
//编写一个类,Operator可以返回一个运算符对应的优先级
class Operator{
	private static int ADD = 1;//加减号的优先级为1
	private static int SUB = 1;
	private static int MUL = 2;//乘除号的优先级为2
	private static int DIV = 2;
	public static int test(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("存在错误的运算符号!!!");
			break;
		}
		return result;
	}
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值