数据结构------栈的学习(java代码实现,收藏就完事了)

作为一名程序员,若是一点数据结构与算法都不了解,那未来裁员的名单中肯定有你
需要直接看代码学习的,看这就够了!!!!

一、栈的结构

一句话就是:后进先出
如果上面不能够很好理解,那就直接看百度,下面直接看代码那就更难了

二、用栈的案例

案例可以列很多:

  1. 中缀表达式转后缀表达式(逆波兰表达式)
  2. 逆序打印单链表
  3. 迷宫出路
  4. …………

三、撸代码

这里用数组模拟栈,主要看栈的属性定义,后面的方法都是很简单的:

//数组栈
class ArrayStack{
	//栈容量(大小)
	private int maxSize;
	//存储数据,用数组来存放数据
	private int[] arrayStack;
	//栈顶
	private int top = -1;
	//初始化
	public ArrayStack(int volume) {
		//初始化栈的大小
		arrayStack = new int[volume];
		this.maxSize = volume;
	}

完整代码包涵测试代码:

package stack;

import java.util.Scanner;

/**
 * 需求分析:用数组来模拟栈的操作
 * @author 1710269824
 *
 */

public class ArrayStackDemo {
	public static void main(String[] args) {
		//创建一个栈
		ArrayStack arrayStack = new ArrayStack(4);
		//程序退出循环的标志
		boolean loop = true;
		//用来接收用户的收入
		String str = "";
		Scanner sc = new Scanner(System.in);
		while(loop)
		{
			System.out.println("exit:表示退出程序 ");
			System.out.println("show:表示遍历栈(打印数据) ");
			System.out.println("push:表示进栈(添加数据)");
			System.out.println("pop:表示出栈");
			System.out.println("请输入你的选择-----------------");
			str = sc.next();
			switch(str)
			{
				case "exit":
					loop = false;
					//资源要关闭
					sc.close();
					break;
				case "show":
					arrayStack.showStack();
					break;
				case "push":
					System.out.println("请输入一个数");
					int value = sc.nextInt();
					arrayStack.push(value);
					break;
				case "pop":
					try
					{
						int v = arrayStack.pop();
						System.out.printf("出栈的数据是%d\n",v);
					}
					catch(Exception e)
					{
						System.out.println(e.getMessage());
					}
					break;
				
				default:
					break;
			}
			
		}
		System.out.println("程序退出了~~~");
	}

}

//数组栈
class ArrayStack{
	//栈容量(大小)
	private int maxSize;
	//存储数据,用数组来存放数据
	private int[] arrayStack;
	//栈顶
	private int top = -1;
	//初始化
	public ArrayStack(int volume) {
		//初始化栈的大小
		arrayStack = new int[volume];
		this.maxSize = volume;
	}
	
	//栈满
	public boolean isFull()
	{
		return top == maxSize - 1;
	}
	
	//栈空
	public boolean isEmty()
	{
		return top == -1;
	}
	
	//进栈
	public void push(int value)
	{
		//判断栈是否满了
		if(isFull())
		{
			System.out.println("栈已经满了~~~");
			return;
		}
		top++;
		arrayStack[top] = value;
	}
	
	//出栈
	public int pop()
	{
		//判断栈是否为空
		if(isEmty())
		{
			throw new RuntimeException("栈内为空,没有数据~~~~");
		}
		int value = arrayStack[top];
		top--;
		return value;
	}
	
	//遍历栈(因为栈是先进后出的,所以遍历是从顶往下遍历)
	public void showStack()
	{
		if(isEmty())
		{
			System.out.println("栈为空,没有数据~~~");
			return;
		}
		for(int i = top; i > -1; i--)
		{
			System.out.printf("arrayStack[%d]=%d\n",i,arrayStack[i]);
		}
	}
	
}

用栈来实现一些案例操作:
需求:用数组栈来模拟实现加减乘除运算 如:"26-3+2"(这里其实就是中缀表达式)
上代码:

package stack;
/**
 * 需求:用数组栈来模拟实现加减乘除运算 如:"2*6-3+2"
 * 分析:其实这就是中缀表达式,人类最直观的表达式,但是计算机不喜欢
 *  1. 通过一个 index  值(索引),来遍历我们的表达式
	2. 如果我们发现是一个数字, 就直接入数栈
	3. 如果发现扫描到是一个符号,  就分如下情况
	3.1 如果发现当前的符号栈为 空,就直接入栈
	3.2 如果符号栈有操作符,就进行比较,如果当前的操作符的优先级小于或者等于栈中的操作符, 就需要从数栈中pop出两个数,在从符号栈中pop出一个符号,进行运算,将得到结果,入数栈,然后将当前的操作符入符号栈, 如果当前的操作符的优先级大于栈中的操作符, 就直接入符号栈.
	4. 当表达式扫描完毕,就顺序的从 数栈和符号栈中pop出相应的数和符号,并运行.
	5. 最后在数栈只有一个数字,就是表达式的结果

 *微信公众号:放牛娃学编程
 *
 */

public class OpeStackDemo {
	public static void main(String[] args) {
		//开始实现加减乘法运算
		String expression = "10*10+10-20";
		String key = "";  //用来查看是否是多位数
		ArrayStack2 numberStack = new ArrayStack2(10);
		ArrayStack2 operStack = new ArrayStack2(10);
		for(int i = 0; i < expression.length(); i++)
		{
			char str = expression.charAt(i);
			if(operStack.isOper(str))
			{
				//判断字符栈是否为空
				if(operStack.isEmty())
				{
					//为空就直接入栈
					operStack.push(str);
				}
				else
				{
					//不为空,要判断运算符号的优先级
					if(operStack.priority(str) > operStack.priority(operStack.peek()))
					{
						operStack.push(str);
					}
					else
					{
						int num1 = numberStack.pop();
						int num2 = numberStack.pop();
						char oper = (char) operStack.pop();
						int sumTemp = numberStack.sumOper(num1, num2, oper);
						numberStack.push(sumTemp);
						operStack.push(str);
					}
				}
			}
			else {
				//为数字
				//这里还需要考虑多位数的情况
				key += str;
				int index = i + 1;
				if(index >= expression.length())
				{
					numberStack.push(Integer.parseInt(key));
				}
				else
				{
					if(numberStack.isOper(expression.charAt(index)))
					{
						numberStack.push(Integer.parseInt(key));
						key = "";
					}
				}
			}
		}
		//遍历完了,栈中的数据直接运算
		while(!operStack.isEmty())
		{
			int num1 = numberStack.pop();
			int num2 = numberStack.pop();
			char oper = (char) operStack.pop();
			int sumTemp = numberStack.sumOper(num1, num2, oper);
			numberStack.push(sumTemp);
		}
		System.out.printf("表达式%s=%d",expression,numberStack.pop());
	}

}
//数组栈
class ArrayStack2{
	//栈容量(大小)
	private int maxSize;
	//存储数据,用数组来存放数据
	private int[] arrayStack;
	//栈顶
	private int top = -1;
	//初始化
	public ArrayStack2(int volume) {
		//初始化栈的大小
		arrayStack = new int[volume];
		this.maxSize = volume;
	}
	
	//栈满
	public boolean isFull()
	{
		return top == maxSize - 1;
	}
	
	//栈空
	public boolean isEmty()
	{
		return top == -1;
	}
	
	//进栈
	public void push(int value)
	{
		//判断栈是否满了
		if(isFull())
		{
			System.out.println("栈已经满了~~~");
			return;
		}
		top++;
		arrayStack[top] = value;
	}
	
	//出栈
	public int pop()
	{
		//判断栈是否为空
		if(isEmty())
		{
			throw new RuntimeException("栈内为空,没有数据~~~~");
		}
		int value = arrayStack[top];
		top--;
		return value;
	}
	
	//查看栈顶元素,但是不出栈
	public int peek()
	{
		return arrayStack[top];
	}
	
	//遍历栈(因为栈是先进后出的,所以遍历是从顶往下遍历)
	public void showStack()
	{
		if(isEmty())
		{
			System.out.println("栈为空,没有数据~~~");
			return;
		}
		for(int i = top; i > -1; i--)
		{
			System.out.printf("arrayStack[%d]=%d\n",i,arrayStack[i]);
		}
	}
	
	//设置运算符的优先级
	public int priority(int oper)
	{
		if(oper == '*' || oper == '/')
		{
			return 1;
		}
		else if(oper == '+' || oper == '-')
		{
			return 0;
		}
		else
		{
			return -1;
		}
	}
	
	//判断是否是运算符
	public boolean isOper(char oper)
	{
		return oper == '*' || oper == '/' || oper == '+' || oper == '-';
	}
	
	//两个数运算操作(从栈中pop出两个数,然后进行运算)
	public int sumOper(int num1, int num2, char oper)
	{
		int sum = 0;
		switch (oper) {
		case '*':
			sum = num1*num2;
			break;
		case '/':
			sum = num2/num1;
			break;
		case '+':
			sum = num2 + num1;
			break;
		case '-':
			sum = num2 - num1;
			break;
		default:
			break;
		}
		return sum;
	}
}

中缀表达式转逆波兰表达式,并用它来运算表达式。

package stack;

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

import com.sun.javadoc.Type;

/**
 * 用后缀表达式来求算术表达式:这是计算机喜欢的操作
 * 比如:"(3+4)*5-6" 它的后缀表达式为:"34+5*6-",后缀表达式又称为逆波兰表达式
 * 	//完成对逆波兰表达式的运算
	 * 1)从左至右扫描,将3和4压入堆栈;
		2)遇到+运算符,因此弹出4和3(4为栈顶元素,3为次顶元素),计算出3+4的值,得7,再将7入栈;
		3)将5入栈;
		4)接下来是×运算符,因此弹出5和7,计算出7×5=35,将35入栈;
		5)将6入栈;
		6)最后是-运算符,计算出35-6的值,即29,由此得出最终结果
 * 后缀表达式运算只需要一个栈就可以完成。
 * @author 1710269824
 *
 */

public class PolanNotation {
	public static void main(String[] args) {
		/*
		//算术表达式(为了方便,这里每个操作数之间用空格隔开)
		String expression = "2 3 +";
		//将字符串放入list中
		List<String> list = getList(expression);
		System.out.printf("表达式%s=%d",expression,sumResult(list));
		*/
		 
		String expression = "(2+3)*5+5";
		List<String> list = toInfixExpressionList(expression);
		System.out.println(expression+"的中缀表达式 "+list);
		//将中缀表达式转成后缀表达式
		List<String> suffixList = parseSuffixExpreesionList(list);
		System.out.println("后缀表达式为:"+suffixList);
		//求结果
		System.out.printf("表达式%s=%d",expression,sumResult(suffixList));
	}
	
	public static int sumResult(List<String> list)
	{
		//这里直接用系统的栈
		Stack<String> plStack = new Stack<String>();
		for(int i = 0; i < list.size(); i++)
		{
			//如果是操作数,就弹出两个数
			//用正则匹配是否是数字
			if(!list.get(i).matches("\\d+"))
			{
				int num1 = Integer.parseInt(plStack.pop());
				int num2 = Integer.parseInt(plStack.pop());
				int sumTemp = cal(num1,num2,list.get(i));
				plStack.push(String.valueOf(sumTemp));
			}
			else
			{
				//如果是数字符,那就直接进栈
				plStack.push(list.get(i));
			}
		}		
		return Integer.parseInt(plStack.pop());
	}
	
	/**
	 * 首先将中缀表达式,也就是人常见的表达式放入list中,如:2*(3-1)  => [2,*,(,3,-,1,)]
	 * 为什么要这样做:方便后面将中缀表达式转变成后缀表达式
	 * @param expression
	 * @return
	 */
	public static List<String> toInfixExpressionList(String expression)
	{
		int i = 0; //用于索引
		String str = ""; //用于拼接处理多位数的情况
		char ch;   //存放索引值
		List<String> list = new ArrayList<String>();
		do {
			//如果是非数字,那就直接添加到list中去
			if(expression.charAt(i) < 48 || expression.charAt(i) > 57)
			{
				ch = expression.charAt(i);
				list.add(""+ch);
				i++;
			}
			else
			{
				//是数字,还要判断是否为多位数
				while(i < expression.length() && expression.charAt(i) >= 48 && expression.charAt(i) <= 57)
				{
					str += expression.charAt(i);
					i++;
				}
				list.add(str);
				str = "";
			}
			
		} while (i < expression.length());
		return list;
	}
	
	
	//将一串后缀表达式字符放入数组中
	public static List<String> getList(String expression)
	{
		String[] strArray = expression.split(" ");
		List<String> list = new ArrayList<String>();
		//遍历字符数组,然后将其加入到list中
		for(String el:strArray)
		{
			list.add(el);
		}
		return list;
	}
	
	/**
	 * 需求:中缀表达式的list转成后缀表达式的list
	 * 分析步骤:
	 *  1) 初始化两个栈:运算符栈s1和储存中间结果的栈s2;
		2) 从左至右扫描中缀表达式;
		3) 遇到操作数时,将其压s2;
		4) 遇到运算符时,比较其与s1栈顶运算符的优先级:
		 1.如果s1为空,或栈顶运算符为左括号“(”,则直接将此运算符入栈;
		 2.否则,若优先级比栈顶运算符的高,也将运算符压入s1;
		 3.否则,将s1栈顶的运算符弹出并压入到s2中,再次转到(4.1)与s1中新的栈顶运算符相比较;
		5) 遇到括号时:(1) 如果是左括号“(”,则直接压入s1
					(2) 如果是右括号“)”,则依次弹出s1栈顶的运算符,并压入s2,直到遇到左括号为止,此时将这一对括号丢弃
		6) 重复步骤2至5,直到表达式的最右边
		7) 将s1中剩余的运算符依次弹出并压入s2
		8)  依次弹出s2中的元素并输出,结果的逆序即为中缀表达式对应的后缀表达式

	 * @param ls
	 * @return
	 */
	public static List<String> parseSuffixExpreesionList(List<String> ls)
	{
		//初始化两个栈、运算符号栈s1和储存中间结果的栈s2
		//因为在操作的过程中,s2始终没有pop(出栈)操作,再加上后面又要逆序输入才是最终的逆波兰表达式结果
		//所以这里为了后续的方便,直接用list2代替栈s2
		Stack<String> s1 = new Stack<String>();
		List<String> s2 = new ArrayList<String>();
		//开始根据逻辑操作编写代码
		for(String item: ls)
		{
			//如果是数
			if(item.matches("\\d+"))
			{
				//将其加入到s2中
				s2.add(item);
			}else if(item.equals("("))
			{
				s1.push(item);
			}else if(item.equals(")"))
			{
				//如果是右括号“)”,则依次弹出s1栈顶的运算符,并压入s2,直到遇到左括号为止,此时将这一对括号丢弃
				while(!s1.peek().equals("("))
				{
					s2.add(s1.pop());
				}
				s1.pop(); //将这一对括号丢弃,消除小括号
			}
			else
			{
				//当item的优先级小于等于s1栈顶运算符,将s1栈顶的运算符弹出并压入到s2中
				//再次转到(4.1)与s1中新的栈顶运算符相比较;
				while(s1.size() != 0 && priority(s1.peek()) >= priority(item))
				{
					s2.add(s1.pop());
				}
				//还需要将item这个符号压入栈中
				s1.push(item);
			}
		}
		//将s1中剩余的运算符依次弹出并压入s2
		while(s1.size() != 0)
		{
			s2.add(s1.pop());
		}
		//最后返回s2,因为这里s2采用的是数组,所以它已经有序,直接输出即是逆波兰表达式
		return s2;
	}
	
	//返回操作运算符的优先级
	public static int priority(String oper)
	{
		if("*".equals(oper) || "/".equals(oper))
		{
			return 1;
		}
		else if("-".equals(oper) || "+".equals(oper))
		{
			return 0;
		}
		else
		{
			return -1;
		}
	}
	
	
	//pop(弹出)两个数来进行运算
	public static int cal(int num1, int num2, String oper)
	{
		int sumTemp = 0;
		switch (oper) {
		case "*":
			sumTemp = num1 * num2;
			break;
		case "+":
			sumTemp = num1 + num2;
			break;
		case "-":
			sumTemp = num2 - num1;
			break;
		case "/":
			sumTemp = num2 / num1;
			break;
		default:
			break;
		}
		return sumTemp;
	}
	
	//
	//写一个方法,返回对应的优先级数字
	public static int getValue(String operation) {
		int result = 0;
		switch (operation) {
		case "+":
			result = 1;
			break;
		case "-":
			result = 1;
			break;
		case "*":
			result = 2;
			break;
		case "/":
			result = 2;
			break;
		default:
			System.out.println("不存在该运算符" + operation);
			break;
		}
		return result;
	}
}

总结:光看是没有用的,还是的撸起代码来

四、分享与交流

最后有兴趣一起交流的,可以关注我的公众号:这里你能够学到很实用的技巧,不是常用的我不说,公众号回复提取码即可获取以下学习资料啦啦啦啦,喜欢就拿去吧!!

(链接时常会失效,若出现此类情况,可以加我微信:17722328325(加时请备注:学习资料))

  1. Java web从入门到精通电子书

  2. Python机器学习电子书

  3. Python400集(北京尚学堂)

  4. JavaScript项目案例、经典面试题

  5. Java300集(入门、精通)

  6. Java后端培训机构录集(同事培训内部提供)

  7. java重要知识pdf文档(价值连城呀呀,不收藏你会后悔的)
    在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/qiukui111/article/details/105189929
今日推荐