作为一名程序员,若是一点数据结构与算法都不了解,那未来裁员的名单中肯定有你
需要直接看代码学习的,看这就够了!!!!
一、栈的结构
一句话就是:后进先出
如果上面不能够很好理解,那就直接看百度,下面直接看代码那就更难了
二、用栈的案例
案例可以列很多:
- 中缀表达式转后缀表达式(逆波兰表达式)
- 逆序打印单链表
- 迷宫出路
- …………
三、撸代码
这里用数组模拟栈,主要看栈的属性定义,后面的方法都是很简单的:
//数组栈
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(加时请备注:学习资料))
-
Java web从入门到精通电子书
-
Python机器学习电子书
-
Python400集(北京尚学堂)
-
JavaScript项目案例、经典面试题
-
Java300集(入门、精通)
-
Java后端培训机构录集(同事培训内部提供)
-
java重要知识pdf文档(价值连城呀呀,不收藏你会后悔的)