一、栈Stack
1.1 概念
一种特殊的线性表,只允许在固定的一段进行插入和删除元素操作。进行数据的插入和删除操作的一段称为栈顶,另一端称为栈低。栈中的元素遵循后进先出 LIFO(Last In First Out)的原则。
举例:在word中,如果要想进行添加、删除。修改相关信息,则当用户选择撤销时,程序将会返回上一个操作状态。
1.2 栈的使用
方法 | 功能 |
Stack() | 构造一个空栈 |
E push(E e) | 将e入栈,并返回e |
E pop() | 将栈顶元素出栈并返回 |
E peek() | 获取栈顶元素 |
int size() | 获取栈中有效元素个数 |
boolean empty() | 检测栈是否为空 |
public static void main(String[] args) {
Stack<Integer> s = new Stack();
s.push(1);
s.push(2);
s.push(3);
s.push(4);
System.out.println(s.size()); // 获取栈中有效元素个数---> 4
System.out.println(s.peek()); // 获取栈顶元素---> 4
s.pop(); // 4出栈,栈中剩余1 2 3,栈顶元素为3
System.out.println(s.pop()); // 3出栈,栈中剩余1 2 栈顶元素为3
if(s.empty()){
System.out.println("栈空");
}else{
System.out.println(s.size());
}
}
1.3 模拟实现栈
Stack继承了Vector,Vector和ArrayList类似,都是动态的顺序表,不同的是Vector是线程安全的.
public class MyStack {
public int[] elem;
public int usedSize;
public MyStack() {
this.elem = new int[10];
}
public void push(int val) {
if(isFull()) {
this.elem = Arrays.copyOf(elem,2*elem.length);
}
elem[usedSize++] = val;
}
private boolean isFull() {
return usedSize == elem.length;
}
public int pop() {
if(isEmpty()) {
throw new EmptyStackException("pop()空栈异常");
}
int val = elem[usedSize - 1];
usedSize--;
return val;
}
public int peek() {
if(isEmpty()) {
throw new EmptyStackException("peek()空栈异常");
}
return elem[usedSize - 1];
}
private boolean isEmpty() {
return usedSize == 0;
}
}
1.4 栈的应用场景
1. 改变元素的序列
若进栈序列为 1,2,3,4 ,进栈过程中可以出栈,则下列不可能的一个出栈序列是()
A: 1,4,3,2 B: 2,3,4,1 C: 3,1,4,2 D: 3,4,2,1
解析:错误出栈序列的解析如下,正确出栈序列解析同下
2.一个栈的初始状态为空。现将元素1、2、3、4、5、A、B、C、D、E依次入栈,然后再依次出栈,则元素出栈的顺序是( )。
A: 12345ABCDE B: EDCBA54321 C: ABCDE12345 D: 54321EDCBA
解析:根据栈中元素遵循先进后出的原则,得出栈顺序为EDCBA54321
2. 将递归转化为循环链表
// 递归方式
void printList(Node head){
if(null != head){
printList(head.next);
System.out.print(head.val + " ");
}
}
// 循环方式
void printList(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 + " ");
}
}
3. 括号匹配
首先,判断获取的字符是否是左括号,如果是则插入到栈中;如果栈为空则返回false;如果前两种情况都不是,则此时为右括号,然后判断栈顶元素和当前字符进行匹配,若满足条件则删除栈顶元素,否则返回false。最后当字符串遍历完后,如果栈中还有元素,则此时左括号多,返回false。如果栈为空,则返回true。
class Solution {
public boolean isValid(String s) {
//创建一个空栈
Stack<Character> stack = new Stack<>();
for(int i = 0;i < s.length();i++) {
//遍历字符串获取字符
char ch = s.charAt(i);
//如果ch为左括号,则放入栈中
if(ch == '(' || ch == '[' || ch == '{' ) {
stack.push(ch);
}else {
//此时ch为右括号
//情况1:栈中没有左括号进行匹配,右括号多
if(stack.isEmpty()){
return false;
}
//情况2:栈中右左括号,需判断栈顶ch2(右括号)是否和ch(右括号)匹配
char ch2 = stack.peek();
if( ch2 == '(' && ch == ')'
|| ch2 == '[' && ch == ']'
|| ch2 == '{' && ch == '}' ) {
stack.pop();
}else {
//此时,栈不为空,但括号不匹配
return false;
}
}
}
//字符串已经遍历完了,若栈不为空,则左括号多
if(!stack.isEmpty()) {
return false;
}
return true;
}
}
4. 逆波兰表达式
class Solution {
public int evalRPN(String[] tokens) {
Stack<Integer> stack = new Stack<>();
for(String str : tokens) {
if(isNumber(str)) {
int x = Integer.parseInt(str);
stack.push(x);
}else {
int val2 = stack.pop();
int val1 = stack.pop();
switch (str) {
case "+":
stack.push(val1 + val2);
break;
case "-":
stack.push(val1 - val2);
break;
case "*":
stack.push(val1 * val2);
break;
case "/":
stack.push(val1 / val2);
break;
}
}
}
return stack.pop();
}
private boolean isNumber(String str) {
return !(str.equals("+")
|| str.equals("-" )
|| str.equals("*")
|| str.equals("/"));
}
}
5. 出栈入栈次序匹配
解析:遍历pushV数组,每次入栈一个元素后,将栈顶元素与popV中下标为j所对应的元素进行比较,如果一样,则可以出栈;不一样,i++。在遍历时,可能会出现多个相同,所以需用到循环
public class Solution {
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
*
*
* @param pushV int整型一维数组
* @param popV int整型一维数组
* @return bool布尔型
*/
public boolean IsPopOrder (int[] pushV, int[] popV) {
// write code here
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();
}
}
6.最小栈
class MinStack {
public Stack<Integer> stack;
public Stack<Integer> minStack;
public MinStack() {
stack = new Stack<>();
minStack = new Stack<>();
}
public void push(int val) {
stack.push(val);
if(minStack.isEmpty() || val <= minStack.peek()) {
minStack.push(val);
}else {
return;
}
}
public void pop() {
if(minStack.isEmpty()) {
return;
}
int popVal = stack.pop();
if(popVal == minStack.peek()) {
minStack.pop();
}
}
public int top() {
if(minStack.isEmpty()) {
return -1;
}
return stack.peek();
}
public int getMin() {
if(minStack.isEmpty()) {
return -1;
}
return minStack.peek();
}
}