一、栈的概念
1、栈是一个先入后出的有序结构
2、栈是限制性表中元素的插入和删除只能在线性表的同一端进行的一种特殊线性表。
3、允许插入和删除的一段称为变化端,叫栈顶(Top),另一端叫栈底(Bottom),在栈的最低端。
4、压入元素在栈的最下端,称为压栈(Push),弹出元素从栈顶开始,称为弹栈(Pop)
压栈弹栈图解:
二、使用数组模拟栈【队列也是可以用数组模拟】
1、思路:
1、首先需要一个变量指针top【初始化指向-1,也就是空栈的状态】用来帮助完成压栈和弹栈的操作。
2、当压入数据时,top自增,弹出数据时,top自减
3、当判断是否满栈时,应利用数组的容量和top之间的关系。【top总是指向数组中最后一个元素。与队列有些相似】,可以这么理解,当top指向数组中最后一个元素时,为满栈
2、代码实现
public class Stack {
int maxSize ; //栈容量
int top = -1 ; //栈顶指针
int array[] ;
//构造器
public Stack(int maxSize) {
this.maxSize = maxSize;
array = new int[maxSize] ;
}
//判断是否满栈
public boolean isFull(){
return top == maxSize -1 ;
}
//判断是否空栈
public boolean isEmpty(){
return top == -1 ;
}
//压栈
public void push(int data){
if (isFull()){
System.out.println("栈已经满");
return ;
}
//压栈操作
top++ ;
array[top] = data ;
}
//弹栈
public int pop(){
if (isEmpty()){
System.out.println("栈已经空");
return -1;
}
//弹栈操作【先弹出数据指针减1】
int res = array[top];
top-- ;
return res ;
}
//遍历栈
public void show(){
if (isEmpty()){
System.out.println("栈已经空了");
return ;
}
//遍历应遵循栈的原则,先进后出(倒序遍历)
for (int i = top; i >= 0 ; i--) {
System.out.print("栈中的数据:" + array[i] + "\t");
}
}
//获取栈顶元素
public int peek(){
//并不是弹栈无需自增
return array[top];
}
}
三、使用自己创建的栈判断一个字符串是否是回文数据
1、什么是回文数据?
将一串字符串正反序遍历都相等【如:"aba" 倒叙遍历也是:aba,它就是回文数据。"abc" 倒序遍历是"cba"就不是回文数据】
2、思路:
(1)、利用栈的先进后出的原则,进行判断。
(2)、先将字符串进行遍历【使用charAt()】,逐个压栈。
(3)、压栈完,进行弹栈。将弹出来的字符进行字符串拼接,拼接完的字符串也源字符串相等说明是回文数据。
3、代码实现:
//判断是否是回文数据的方法
public static boolean isHuiWen(String s) {
Stack stack = new Stack(10);
//对字符串逐个遍历,压栈
for (int i = 0; i < s.length(); i++) {
stack.push(s.charAt(i));
}
String s1 = "";//用于弹栈后的字符串拼接
while (!stack.isEmpty()) {
//由于弹出来的是int型数据。所以需要强转一下数据类型
char data = (char) stack.pop();
s1 = s1 + data;
}
//进行判断.这里由于String类已经重写了equals方法,所以无需重写
return s1.equals(s);
}
四、使用栈模拟计算器的基本功能【不带小括号】
思路:
1、创建俩个栈,一个保存符号,一个保存数字。并且创建 index 指针帮助遍历字符串
2、如果扫描的是一个数字,直接压入数字栈。
3、扫描的是一个符号分以下几种情况:
(1)、如果当前的符号栈是空的,就直接入栈
(2)、如果符号栈不为空,比较当前符号优先级和栈中符号的优先级
1)、如果当前符号的优先级小于或者等于栈中的操作符。就需要从数字栈中 弹出俩个操作数,从符号栈弹出符号,进行运算 将运算结果压入数字栈,并把当前符号压入符号栈
2)、如果当前符号优先级比栈中优先级大,则直接入栈
4、当扫描表达式完毕,顺序弹出数字栈和符号栈,进行运算
5、最后数字栈中只有一个运算结果,将其弹出即可。
图解:
代码实现:
对于以上的理解,我们需要额外提供三个方法:1、判断符号优先级 【可自己设定规则】 2、对弹栈的数字和符号进行运算 3、判断是否是一个运算符
//判断符号的优先级
public int OperPre(char oper) {
if (oper == '+' || oper == '-') {
return 1;
} else if (oper == '*' || oper == '/') {
return 2;
} else {
System.out.println("输入的运算符有误");
return -1;
}
}
//进行计算
/**
* @param oper 运算符
* @param num2 操作数
* @param num1 操作数
* @return 运算结果
*/
public int compute(char oper, int num1, int num2) {
int res = 0;//保存运算结果
switch (oper) {
case '+':
res = num1 + num2;
break;
case '-':
//这里需注意一下:由于栈是先进后出,所以先弹出去num2是被减数,num1是减数
res = num2 - num1;
break;
case '*':
res = num1 * num2;
break;
case '/':
//这里也是,先弹出去num2是被除数,num1是除数
res = num2 / num1;
break;
default:
break;
}
return res;
}
//判断是否是一个运算符
public boolean isOper(int oper){
return oper == '+' ||oper == '-' ||oper == '/' ||oper == '*' ;
}
对表达式进行计算:
public static int computeMethod(String s) {
//创建符号栈和数字栈
Stack numStack = new Stack(10);
Stack operStack = new Stack(10);
//指针
int index = 0;
int num1 = 0; //操作数1
int num2 = 0; //操作数2
int oper = 0; //运算符
int res = 0;//保存运算结果
int c;//用于每次扫描接受的字符
String s1 = "";//用于多位数的拼接【操作数有可能是俩位,三位....】
//当指针指向字符串最后一位时要终止循环
while (index < s.length()) {
//对字符串进行截取,转换成字符
c = s.substring(index, index + 1).charAt(0);
//判断c是否是一个运算符
if (operStack.isOper(c)) {
//c是一个运算符
if (!operStack.isEmpty()) {
//符号栈不是空栈
if (operStack.OperPre(c) <= operStack.OperPre(operStack.peek())) {
//当前符号优先级比栈内符号优先级小或者等于时。则弹出俩个数,一个符号进行运算
num1 = numStack.pop();
num2 = numStack.pop();
oper = operStack.pop();
res = numStack.compute(oper, num1, num2);
//把运算结果入栈
numStack.push(res);
//把当前符号入栈
operStack.push(c);
} else {
//当前符号优先级比栈内符号优先级大,直接入栈
operStack.push(c);
}
} else {
//符号栈是空栈。直接入栈
operStack.push(c);
}
} else {
/*
如果是一个数字。直接压栈。
注意:因为截取的是一个字符,用ASCII码表示的,再进行压栈时要减去48
这里还有一个问题:就是当一个操作数是多位数时。直接压栈是不行的。
处理方法:
1、如果扫描的是数,还需要往后边继续扫描看是不是还是一个数字。如果是数字就进行拼接
2、如果扫描的是一个运算符直接压栈
*/
s1 += (c-48);
//这里判断index是否是最后一位,如果是最后一位就不需要进行拼接了直接入栈
//不加这个条件在进行截取的时候可能会越界
if (index == s.length() - 1) {
numStack.push(Integer.parseInt(s1));
} else {
if (operStack.isOper(s.substring(index + 1, index + 2).charAt(0))) {
//如果是一个运算符。直接压栈
numStack.push(Integer.parseInt(s1));
s1 = "";//当处理完一个数据时,务必要将s1清空,不然进行下一个循环时,还保留上一个数据的值
}
}
}
index++; //指针后移
}
//执行到这里,说明字符串已经完成压栈操作
//接下来就是弹栈计算.只要符号栈不为空,就进行运算
while (!operStack.isEmpty()) {
num1 = numStack.pop();
num2 = numStack.pop();
oper = operStack.pop();
res = numStack.compute(oper, num1, num2);
//把运算结果压栈
numStack.push(res);
}
//当整个while循环结果后。数字栈内只剩下一个最后的结果
return numStack.pop();
}