数据结构与算法——栈

  • 基本介绍
    <1> 限制性线性表,元素只能在一端插入和删除的特殊线性表
    <2> 允许插入删除的一端称栈顶Top
    <3> 固定的一端称栈底Button
    <4> 先进后出在这里插入图片描述
    在这里插入图片描述

  • 应用场景
    <1> 子程序的调用
    <2> 递归调用
    <3> 表达式的转换与求值:前 后 中缀表达式
    <4> 二叉树的遍历
    <5> 图的深度优先搜素

  • 数组模拟栈

public class ArrayStack {
	public int maxSize;//栈最大容量
	public int top=-1;//栈顶元素
	public int stack[];
	
	public ArrayStack(int maxSize){
		this.maxSize=maxSize;
		stack = new int[this.maxSize];
	}
	/**
	 * 栈满
	 * @return
	 */
	public boolean isFull(){
		return top==maxSize-1;
	}
	/**
	 * 栈空
	 * @return
	 */
	public boolean isEmpty(){
		return top==-1;
	}
	/**
	 * 入栈
	 * @param value
	 */
	public void push(int value){
		if(isFull()){
			System.out.println("栈满!");
			return;
		}
		top++;
		stack[top]=value;
	}
	/**
	 * 出栈
	 * @return
	 */
	public int pop(){
		if(isEmpty()){
			throw new RuntimeException("栈空!");
		}
		int value=stack[top];
		top--;
		return value;
	}
	/**
	 * 获取栈顶元素
	 * @return
	 */
	public int peek(){
		return stack[top];
	}
	/**
	 * 遍历栈
	 */
	public void list(){
		if(isEmpty()){
			throw new RuntimeException("栈空!");
		}
		for(int i=top;i>=0;i--){
			System.out.print(stack[i]+"\t");
		}
	}
}

  • 链表模拟栈
public class LinkedListStack {
	public LNode top;//栈顶元素
	public LNode head;
	//在链表head.next进行插入 删除
	
	public LinkedListStack(){
		head = new LNode(0,null);
		top=head.next;
	}
	
	/**
	 * 栈空
	 * @return
	 */
	public boolean isEmpty(){
		return top==null;
	}
	/**
	 * 入栈
	 * @param value
	 */
	public void push(LNode stack){
		LNode cur = head.next;
		head.next=stack;
		stack.next=cur;
		top=head.next;
	}
	/**
	 * 出栈
	 * @return
	 */
	public LNode pop(){
		if(isEmpty()){
			throw new RuntimeException("栈空!");
		}
		LNode value=head.next;
		head.next=head.next.next;
		top=head.next;
		return value;
	}
	/**
	 * 获取栈顶元素
	 * @return
	 */
	public int peek(){
		return top.data;
	}
	/**
	 * 遍历栈
	 */
	public void list(){
		if(isEmpty()){
			throw new RuntimeException("栈空!");
		}
		LNode cur=head.next;
		while(cur!=null){
			System.out.print(cur.data+"\t");
			cur=cur.next;
		}
	}

}
  • 栈实现综合计算器(中缀表达式为例)
    思路:
    1.通过一个index遍历表达式
    2.若是数字,直接入数字栈——注意多位数的问题
    1)用一个while循环得到下一个index字符,若是数字则用字符串拼接
    3.若是字符
    1)若此时栈空,直接压入字符栈
    2)若此时栈非空,则判断该字符与栈顶字符的优先级
    1》若 该字符优先级>栈顶字符优先级 直接压栈
    2》若 该字符优先级<=栈顶字符优先级 数字栈弹出两个 字符弹出 一个,计算结果压入数字栈 该字符也压入字符栈
    4.当表达式扫描完毕(index>字符串表达式的长度) 但此时还有一组计算应出栈计算完后, 将结果压到数字栈
    5.最后只有一个数字就是表达式的结果
public class Calculator {
	ArrayStack numStack = new ArrayStack(10);//数字栈
	ArrayStack operStack = new ArrayStack(10);//符号栈
	/**
	 * 判断是不是运算符
	 * @param val
	 * @return
	 */
	public boolean isOper(char val){
		return (val=='+')||(val=='-')||(val=='*')||(val=='/');
		
	}
	/**
	 * 返回运算符的优先级
	 * @param oper
	 * @return
	 */
	public int priority(int oper){
		//此时假定只有=-*/运算
		if(oper=='*'||oper=='/'){
			return 2;
		}else if(oper=='+'||oper=='-'){
			return 1;
		}else{
			return -1;
		}
	}
	/**
	 * 计算
	 * @param num1
	 * @param num2
	 * @param oper
	 * @return
	 */
	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;
	}
	/**
	 * 返回最终结果
	 * @param expression
	 * @return
	 */
	public int calculator(String expression){
		int index=0;//用于扫描
		int num1=0;
		int num2=0;
		int oper=0;//符号
		int res=0;//结果
		char ch;//将扫描到的char保存到ch
		String keepNum="";//拼接多位数
		
		while(true){
			//从表达式中截取一个子符
			ch=expression.substring(index,index+1).charAt(0);
			
			if(isOper(ch)){//是符号
				if(!operStack.isEmpty()){//符号栈非空
					if(priority(ch)<=priority(operStack.peek())){
						//当前优先级<=栈顶优先级
						num1=numStack.pop();
						num2=numStack.pop();
						oper=operStack.pop();
						operStack.push(ch);
						
						res=cal(num1, num2, oper);
						numStack.push(res);
					}else{
						operStack.push(ch);
					}
				}else{
					operStack.push(ch);
				}
			}else{//是数字
				keepNum+=ch;//多位数的拼接
				//判断是不是多位数字
				if(index==expression.length()-1){//如果到最后直接拼接入栈
					numStack.push(Integer.parseInt(keepNum));
				}else{
					if(isOper(expression.substring(index+1,index+2).charAt(0))){//是符号入栈
						numStack.push(Integer.parseInt(keepNum));
						keepNum="";
					}
				}
			}
			index++;
			if(index>=expression.length()){
				break;
			}
		}
		
		//扫描完后还剩下最后一组计算
		while(true){
			if(operStack.isEmpty()){
				break;
			}
			num1=numStack.pop();
			num2=numStack.pop();
			oper=operStack.pop();
			res=cal(num1, num2, oper);
			numStack.push(res);
		}
		return numStack.pop();
	}

}
  • 波兰计算器(前缀表达式的计算器求值)
    思路:从右至左扫描表达式,遇到数字时压入数字栈,遇到运算符时,弹出栈顶两个数做相应计算,其计算结果入栈,重复,直到表达式最左端
  • 逆波兰计算器(后缀表达式的计算器求值)
    思路:
    1.拆分字符串到List中
    2.从左到右扫描List
    3.若是数字,直接压入栈——匹配多位数 使用正则表达式判断 matches("\d+")
    4.若是字符,弹出两个 加上该字符计算 将结果保存在栈中
    5.重复扫描步骤直到扫描完毕
    6.最后只有一个数字就是表达式的结果
    其中包含中缀转后缀
    思路:
    1.中缀表达式转成对应的List
    2.中缀List转成后缀List思路
    1)从左到右扫描中缀List
    2)若是数 放入List
    3)若是符
    1》若栈空 则直接将此符压入栈
    2》若栈非空 则比较优先级
    该运算符优先级>栈顶符号优先级 将改符号压入栈
    该运算符优先级<=栈顶符号优先级 将栈顶符号弹出并放入List 重复3)
    4)若是()
    "("直接压入栈
    ")"依次将栈顶符号弹出并放入List中 直到遇到(为止 注意要舍弃这对()不放List中
    5)扫描到表达式最右端 将栈中剩下的符号依次放入List
public class PolandNotation {
	ArrayStack numStack = new ArrayStack(10);
	/**
	 * 判断是不是运算符
	 * @param val
	 * @return
	 */
	public boolean isOper(char val){
		return (val=='+')||(val=='-')||(val=='*')||(val=='/');
		
	}
	/**
	 * 返回运算符的优先级
	 * @param oper
	 * @return
	 */
	public int priority(int oper){
		//此时假定只有=-*/运算
		if(oper=='*'||oper=='/'){
			return 2;
		}else if(oper=='+'||oper=='-'){
			return 1;
		}else{
			return -1;
		}
	}
	/**
	 * 计算
	 * @param num1
	 * @param num2
	 * @param oper
	 * @return
	 */
	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;
	}
	/**
	 * 将数字字符放入ArrayList中:表达式用空格分开
	 * @param expression
	 * @return
	 */
	public List<String> getListString(String expression){
		String[] split = expression.split(" ");
		List<String> list = new ArrayList<String>();
		for(String ele:split){
			list.add(ele);
		}
		return list;
	}
	/**
	 * 返回最终结果
	 * @param expression
	 * @return
	 */
	public int calculator(List<String> ls){
		int num1=0;
		int num2=0;
		int res=0;//结果
		
		for(String item:ls){
			if(item.matches("\\d+")){//正则表达式 匹配的是 多位数
				numStack.push(Integer.parseInt(item));
			}else{
				num1=numStack.pop();
				num2=numStack.pop();
				res=cal(num1, num2, item.charAt(0));
				numStack.push(res);
			}
		}
		
		return numStack.pop();
		
	}
	
	
	
	
	
	/
	/**
	 * 中缀表达式转成对应的List
	 * @param expression
	 * @return
	 */
	public List<String> toInfixExpressionList(String expression){
		List<String> list = new ArrayList<String>();
		int index=0;
		char c;//遍历的每个字符串
		String str;//对多位数的拼接
		
		do{
			if((c=expression.charAt(index))<48||(c=expression.charAt(index))>57){
				//非数字
				list.add(""+c);
				index++;
			}else{
				//是数--考虑多位
				str="";
				while(index<expression.length()&&(c=expression.charAt(index))>=48&&(c=expression.charAt(index))<=57){
					str+=c;//拼接
					index++;
				}
				list.add(str);
			}
		}while(index<expression.length());
		return list;
	}
	/**
	 * 中缀List转成后缀List
	 * @param ls
	 * @return
	 */
	public List<String> parseSuffixExpressionList(List<String> ls){
		ArrayStack2 stack = new ArrayStack2(10);//暂存符号
		List<String> list = new ArrayList<String>();//存后缀表达式
		Operation op = new Operation();
		
		for(String item:ls){
			if(item.matches("\\d+")){//是数字
				list.add(item);
			}else if(item.equals("(")){
				stack.push(item);
			}else if(item.equals(")")){
				while(!stack.peek().equals("(")){
					list.add(stack.pop());
				}
				stack.pop();
			}else{//是符号
				//栈非空
				while(!stack.isEmpty()&&op.getValue(item)<=op.getValue(stack.peek())){
					list.add(stack.pop());
				}
				
				stack.push(item);
			}
		}
		while(!stack.isEmpty()){
			list.add(""+stack.pop());
		}
		return list;
	}

}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值