栈(Stack)又名堆栈,作为一个== 先进后出== 的数据结构。
它是一种运算受限的线性表。其限制是仅允许在表的一端进行插入和删除运算。这一端被称为栈顶,相对地,把另一端称为栈底。向一个栈插入新元素又称作进栈、入栈或压栈,它是把新元素放到栈顶元素的上面,使之成为新的栈顶元素;从一个栈删除元素又称作出栈或退栈,它是把栈顶元素删除掉,使其相邻的元素成为新的栈顶元素。
比如:逆序打印链表
在我们没有学习栈以前,我们可能会使用递归的方法进行打印,如下所示:
// 递归方式
void printList(Node head){
if(null != head){
printList1(head.next);
System.out.print(head.val + " ");
}
}
// 循环方式
void printList1(Node head) {
if (null == head) {
return;
}
}
其实我们发现逆序打印链表的本质不就是:排在前面的后打印,排在后面的先打印
这不就是栈先进后出的思想吗
所以我们可以先让链表的每一个依次进栈,再出栈即可,实现如下:
Stack<Node> s = new Stack<>();
// 将链表中的结点保存在栈中
Node cur = head;
while(null != cur){
s.push(cur);
cur = cur.next;
}
// 将栈中的元素出栈
while(!s.empty()){
System.out.print(s.pop().val + " ");
}
给定一个只包括 ‘(’,‘)’,‘{’,‘}’,‘[’,‘]’ 的字符串 s ,判断字符串是否有效。
有效字符串需满足:
class Solution {
public boolean isValid(String s) {
}
}
我们利用栈的方法进行解决:
特殊情况考虑:
class Solution {
public boolean isValid(String s) {
Stack<Character> characterStack = new Stack<>();
for(int i = 0;i < s.length(); i++) {
char ch1 = s.charAt(i);
if(ch1 == '('||ch1 == '{' || ch1 == '[') {
characterStack.push(ch1);
} else {
if(characterStack.empty()) {
return false;
}
char ch2 = characterStack.peek();
if(ch2 == '('&&ch1 == ')' ||ch2 == '{'&&ch1 == '}' ||ch2 == '['&&ch1 == ']') {
characterStack.pop();
} else {
return false;
}
}
}
if(characterStack.empty()) {
return true;
}
return false;
}
}
逻辑提问式类似于算术表达式,对于检索而言,这种表达式并不是最优和最简洁的形式,需要进行必要的转换。
1929年波兰的逻辑学家卢卡西维兹(Jan Lucasiewicz)提出了将运算符放在运算项后面的逻辑表达式,又称“逆波兰表达式”。
采用这种表达式组织逻辑提问式非常方便检索运算,是日本的福岛先生最早将逆波兰表达式应用于情报检索的,故又称为“福岛方法”。 [2]
逆波兰表达式又叫做后缀表达式,是一种没有括号,并严格遵循“从左到右”运算的后缀式表达方法,如下表所示:
首先构造一个运算符栈,此运算符在栈内遵循越往栈顶优先级越高的原则。
读入一个用中缀表示的简单算术表达式,为方便起见,设该简单算术表达式的右端多加上了优先级最低的特殊符号“#”。
从左至右扫描该算术表达式,从第一个字符开始判断,如果该字符是数字,则分析到该数字串的结束并将该数字串直接输出。
如果不是数字,该字符则是运算符,此时需比较优先关系。具体做法是:将该字符与运算符栈顶的运算符的优先关系相比较。如果该字符优先关系高于此运算符栈顶的运算符,则将该运算符入栈。若不是的话,则将栈顶的运算符从栈中弹出,直到栈项运算符的优先级低于当前运算符,将该字符入栈。
重复步骤1~2,直至扫描完整个简单算术表达式,确定所有字符都得到正确处理,便可以将中缀式表示的简单算术表达式转化为逆波兰表示的简单算术表达式。
给你一个字符串数组 tokens ,表示一个根据 逆波兰表示法 表示的算术表达式。
请你计算该表达式。返回一个表示表达式值的整数。
注意:
class Solution {
public int evalRPN(String[] tokens) {
}
}
如果当前字符为数字,则压栈,如果是运算符,则将栈顶两个元素弹出作相应运算,结果再入栈,最后当表达式扫描完后,栈里的就是结果。
注意:先出来的数为左操作数,后出来的数为右操作数
代码实现如下:
class Solution {
public int evalRPN(String[] tokens) {
Stack<Integer> stack = new Stack<>();
for(String x : tokens){
if(!isOperation(x)) {
stack.push(Integer.parseInt(x));
}else {
int num2 = stack.pop();
int num1 = stack.pop();
switch (x) {
case "+":
stack.push(num1+num2);
break;
case "-":
stack.push(num1-num2);
break;
case "*":
stack.push(num1*num2);
break;
case "/":
stack.push(num1/num2);
break;
}
}
}
return stack.pop();
}
private boolean isOperation(String x) {
if (x.equals("+") || x.equals("-") || x.equals("/") || x.equals("*")) {
return true;
}
return false;
}
}
输入两个整数序列,第一个序列表示栈的压入顺序,请判断第二个序列是否可能为该栈的弹出顺序。假设压入栈的所有数字均不相等。例如序列1,2,3,4,5是某栈的压入顺序,序列4,5,3,2,1是该压栈序列对应的一个弹出序列,但4,3,5,1,2就不可能是该压栈序列的弹出序列。
我们就可以用一个栈来模拟。对于入栈序列,只要栈为空,序列肯定要依次入栈。那什么时候出来呢?自然是遇到一个元素等于当前的出栈序列的元素,那我们就放弃入栈,让它先出来。
具体做法:
import java.util.Stack;
public class Solution {
public boolean IsPopOrder(int [] pushA,int [] popA) {
int n = pushA.length;
//辅助栈
Stack<Integer> s = new Stack<>();
//遍历入栈的下标
int j = 0;
//遍历出栈的数组
for(int i = 0; i < n; i++){
//入栈:栈为空或者栈顶不等于出栈数组
while(j < n && (s.isEmpty() || s.peek() != popA[i])){
s.push(pushA[j]);
j++;
}
//栈顶等于出栈数组
if(s.peek() == popA[i])
s.pop();
//不匹配序列
else
return false;
}
return true;
}
}
设计一个支持 push ,pop ,top 操作,并能在常数时间内检索到最小元素的栈。
实现 MinStack 类:
class MinStack {
public MinStack() {
}
public void push(int val) {
}
public void pop() {
}
public int top() {
}
public int getMin() {
}
}
/**
* Your MinStack object will be instantiated and called as such:
* MinStack obj = new MinStack();
* obj.push(val);
* obj.pop();
* int param_3 = obj.top();
* int param_4 = obj.getMin();
*/
我们创建两个栈
private Stack<Integer> stack ;
private 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() {
if(!stack.empty()) {
Integer val = stack.pop();
//维护最小栈
if (val.equals(minStack.peek())) {
minStack.pop();
}
}
}
做一个是否为空的判断
若不为直接返回栈顶元素就好
若为空返回-1
代码实现如下:
// peek
public int top() {
if(!stack.empty()) {
return stack.peek();
}
return -1;
}
直接返回minStack栈顶元素就好
public int getMin() {
return minStack.peek();
}
class MinStack {
private Stack<Integer> stack ;
private 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() {
if(!stack.empty()) {
Integer val = stack.pop();
//维护最小栈
if (val.equals(minStack.peek())) {
minStack.pop();
}
}
}
// peek
public int top() {
if(!stack.empty()) {
return stack.peek();
}
return -1;
}
public int getMin() {
return minStack.peek();
}
}
/**
* Your MinStack object will be instantiated and called as such:
* MinStack obj = new MinStack();
* obj.push(val);
* obj.pop();
* int param_3 = obj.top();
* int param_4 = obj.getMin();
*/
关于《【数据结构】 栈(Stack)的应用场景》就讲解到这儿,感谢大家的支持,欢迎各位留言交流以及批评指正,如果文章对您有帮助或者觉得作者写的还不错可以点一下关注,点赞,收藏支持一下!