目录
一. 栈的概念:
栈是⼀种特殊的线性表,其只允许在固定的一端进行插入和删除元素操作。进行数据插入和删除操作的一端称为栈顶,另一端称为栈底。栈中的数据元素遵守后进先出的原则。
那么,后进先出怎么理解?
如图:
假如,里面的不同颜色代表元素,那么可以知道,这个栈没有元素之前,红色元素是最先进去的,再到绿色元素,最后到蓝色元素,那么,元素出栈时,遵循后进先出原则,也就是蓝色元素先出出栈,再到绿色元素出栈,最后是红色元素出栈。
什么是入栈?出栈?
1. 栈的插入数据操作叫做 入栈 或者 压栈 或者叫 进栈 。
2. 栈的删除数据操作叫做 出栈。
注意:入栈和出栈都是在 栈顶 操作的。
二. 栈的初始化:
栈是利用Stack < > 进行初始化的:
Stack<Integer> stack1 = new Stack<>();
这里构造了一个 引用名为 stack1 的栈,操作元素类型是 整型 。
栈的 方法 的使用:
以下的列表为栈的使用方法:
在这里值得注意的是,empt 方法 返回的类型是 boolean ,如果栈为空返回 true ,否则返回 false .
三 . 关于使用栈实现的题目:
1.括号匹配 。 题目链接
题解代码:
public boolean isValid(String s) {
Stack<Character> stack = new Stack<>();
for(int i =0 ;i < s.length();i++) {
char ch = s.charAt(i);
if(isfull(ch)) {
stack.push(ch);
}else {
//先判断栈是否为空
if(stack.empty()) {
return false;
}
if(stack.peek() == '(' && ch == ')' ||
stack.peek() == '[' && ch == ']' ||
stack.peek() == '{' && ch == '}') {
stack.pop();
} else {
return false;
}
}
}
//字符串遍历完后查看栈是否为空
if(stack.empty()) {
return true;
}
return false;
}
public static boolean isfull(char ch) {
if(ch == '(' || ch == '[' || ch == '{') {
return true;
}
return false;
}
题解思路:
首先,我们要理清一个问题,为什么使用了栈可以解决这个问题。
因为栈的特点就是 后进先出 ,所以当遇到这种左右括号匹配的问题就可以很好解决。
一.大致思路:
先遍历当前的字符串,遇到左括号就放进栈里,遇到右括号就拿出来与栈顶的括号对比,如果是匹配的,栈就出栈一个括号,有三种情况是括号不匹配的:
如果如果最后 字符串遍历完成,且栈为空 说明括号是匹配的,返回true。
二.具体思路:
首先定义一个栈 stack, 遍历通过字符串 得到每一个括号,放在 ch 里。
实现一个方法isfull,判断当前字符 ch 是否为 左括号,如果是就入栈,如果不是就可以拿出来与栈顶元素对比了,但是在那之前,要先判断栈是否为空,如果栈已经空了,说明 当前的是不匹配的,直接返回false 。
进入到对比阶段,通过栈的peek方法查看栈顶元素是否与当前 ch 匹配,匹配的话就出栈,否则返回false 。
当字符串遍历完成后,还要判断当前栈是否为空,如果栈为空,返回true;否则返回false。
2.逆波兰表达式求值。题目链接
题解代码:
public int evalRPN(String[] tokens) {
Stack<Integer> stack = new Stack<>();
for(int i = 0;i < tokens.length;i++) {
String s = tokens[i];
if( !isfull(s)) {
Integer ret = Integer.valueOf(s);
stack.push(ret);
} else {
int num1 = stack.pop();
int num2 = stack.pop();
switch(s) {
case "+":
stack.push(num2+num1);
break;
case "-":
stack.push(num2-num1);
break;
case "*":
stack.push(num2*num1);
break;
case "/":
stack.push(num2/num1);
break;
}
}
}
return stack.pop();
}
public boolean isfull(String s) {
if(s.equals("+") || s.equals("-") || s.equals("*") || s.equals("/")) {
return true;
}
return false;
}
题解思路:
首先,这题的计算是这样理解的:
假如由这个 “ 1 2 3 * + 1 2 * - ” 组成的逆波兰表达式。
首先,定义一个栈,再遍历 这个字符串数组,如果遇到数字,把数字字符串转换成数字放进栈里,如果遇到运算符,则弹出 栈里的两个数字,先出栈的作为右操作数,后出栈作为左操作数,把他们的运算结果再放进栈里,直到遍历完这个字符串数组,此时栈里还剩最后一个元素,再返回这最后一个栈里的元素就是结果。
注意:把数字字符串转换成数字使用了 Integer.valueOf()方法,该方法会尝试解析传入的字符串内容,按照十进制整数的格式规则来识别其中的数字信息,并将其转换为对应的Integer对象。
3.出栈入栈次序匹配。题目链接
题解代码:
Stack<Integer> stack = new Stack<>();
int j = 0;
for(int i =0;i < pushV.length;i++) {
stack.push(pushV[i]);
while(!stack.empty() && j < popV.length && stack.peek() == popV[j]) {
stack.pop();
j++;
}
}
return stack.empty();
题解思路:
首先,给pushV 定义一个 i 下标用来遍历,popV 数组用 j 遍历,元素放到栈里,如果当前栈顶元素是 另一个popV数组的 j 下标的元素,那么弹出当前栈顶元素,popV数组下标往后走一步;
如果当前栈顶元素不是另一个popV数组的 j 下标的元素,那么 让 i 下标遍历pushV数组元素放到栈里,直到当前栈顶元素是 另一个popV数组的 j 下标的元素。
但是要注意,while循环的条件语句不能缺少 !stack.empty() && j < popV.length ;并且还要在其最前面,以防最后的 i 遍历pushV 数组 或者 j 遍历popV 数组导致空指针异常。
最后,如果栈还有元素,说明其 i 下标已经遍历完 pushV数组了,并且栈顶元素与popV j 下标元素不相等,证明两个数组 次序不匹配。stack.empty( )会返回false;否则返回true
4.最小栈。题目链接
题解代码:
Stack<Integer> stack;
Stack<Integer> minStack;
public MinStack() {
stack = new Stack<>();
minStack = new Stack<>();
}
public void push(int val) {
stack.push(val);
if(minStack.empty()) {
minStack.push(val);
} else {
if(val <= minStack.peek()) {
minStack.push(val);
}
}
}
public void pop() {
int ret = stack.pop();
if(minStack.peek() == ret) {
minStack.pop();
}
}
public int top() {
return stack.peek();
}
public int getMin() {
return minStack.peek();
}
题解思路:
我们定义两个栈,一个普通栈,一个最小栈,这里最小栈的特点是每次弹出栈都是其普通栈当中元素的最小值。
1.对于push方法,val 值普通栈都要放,但是对于最小栈来说:
如果val是第一次入最小栈,直接放进,如果 val 不是第一次入最小栈,则判断 val 与 当前最小栈的栈顶元素大小关系,如果 val 比最小栈的栈顶元素 小或者等于,则把 val 放进去。这是为了维护 pop 方法 的效果。
2.对于pop方法,普通栈都要弹出;对于最小栈,如果普通栈弹出的元素是最小栈的栈顶元素,则最小栈也要弹出。
3.对于getMin方法,获取堆栈中的最小元素,由于上面方法的实现。每次弹出最小栈的栈顶元素都是普通栈当中的最小元素。