目录
1. 栈(Stack)之概念
首先明确:
下面理解一下,先进后出,看图解
分析一下,
1. 栈是先进后出的,那么入栈和出栈的时间复杂度是多少
2. 前面说的顺序存储的方式,那么栈的链式存储是什么样的
3. 如果是以双向链表来看做栈,那么从哪里入哪里出
2. 栈(Stack)之模拟实现
先写一个栈
public int[] elem;
public int usedSize;
public static final int DEFAULT_CAPATI = 10;
public MyStack() {
elem = new int[DEFAULT_CAPATI];
}
1.入栈push()
入栈前先要判断栈是否满的isFull()
public boolean isFull() {
if (usedSize == elem.length) {
return true;
}
return false;
}
public void push(int val) {
//判断栈满
if (isFull()) {
elem = Arrays.copyOf(elem,2*elem.length);
}
elem[usedSize] = val;
usedSize++;
}
2.删除栈顶元素pos()
删除栈顶元素前,先判断栈空isEmpty()
public boolean isEmpty() {
return usedSize == 0;
}
如果栈是空的,还要写一个异常EmptyStackException
public class EmptyStackException extends RuntimeException{
public EmptyStackException() {
}
public EmptyStackException(String msg) {
super(msg);
}
}
public int pop() {
if (isEmpty()) {
throw new EmptyStackException("栈是空的");
}
int oldVal = elem[usedSize-1];
usedSize--;
return oldVal;
}
3.获取栈顶元素,不删除peek()
public int peek() {
if (isEmpty()) {
throw new EmptyStackException("栈是空的");
}
return elem[usedSize-1];
}
4.获取中有效元素个数getUsedSize()
public int getUsedSize() {
return usedSize;
}
3. 栈(Stack)之使用
public static void main(String[] args) {
Stack<Integer> stack = new Stack<>();
stack.push(2);
stack.push(3);
stack.push(4);
stack.push(7);
System.out.println(stack.size());
System.out.println(stack.peek());
stack.pop();
System.out.println(stack.pop());
}
4.栈(Stack)之使用场景
4.1 改变元素的序列
(1)先看牛客上的一道题
分析一下 ,答案选C
分析一下,答案选C
分析一下,答案选B
4.2 将递归转化为循环
写一个逆序打印链表
(1)递归
public class Demo01 {
static class Node{
public int val;
public Node next;
public Node(int val) {
this.val = val;
}
}
public Node head;
public void printList(Node head) {
if (head == null) {
return;
}
if (head.next == null) {
System.out.print(head.val + " ");
return;
}
printList(head.next);
System.out.print(head.val + " ");
}
}
(2)非递归实现
既然栈是先进后出的,那么可以将数字依次放进去,然后再pop取出栈顶元素,打印出来,这不就实现了逆序打印
public void printList2(Node head) {
Stack<Node> stack = new Stack<>();
Node cur = head;
//将元素全部依次放入栈中
while(cur != null) {
stack.push(cur);
cur = cur.next;
}
//给栈pop取出栈顶元素然后拿出val打印
while(!stack.empty()) {
Node top = stack.pop();
System.out.println(top.val + " ");
}
}
4.3 括号匹配
链接 20. 有效的括号 - 力扣(LeetCode)
题目要求
分析一下
上代码
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);
if(ch == '(' || ch == '[' || ch == '{') {
stack.push(ch);
}else {
//此时ch 是右括号
//说明走到右边括号了
if(stack.empty()) {
//遇到有括号了,但是栈空了,说明1.右括号多
return false;
}
char top = stack.peek();
if(ch == ')' && top == '(' || ch == ']' && top == '[' || ch == '}' && top == '{') {
//说明当前字符是匹配的
stack.pop();
}else {
//2.左右括号不匹配
return false;
}
}
}
if(stack.empty()) {
return true;
}else {
//3.说明是左括号多
return false;
}
}
}
4.4 逆波兰表达式求值
链接 150. 逆波兰表达式求值 - 力扣(LeetCode)
在说这道题前先明白 逆波兰表达式又叫做后缀表达式
(1)后缀表达式又可以通过中缀表达式来转化出来(考研-选择题)
所以中缀变后缀三步走
(2)后缀表达式计算结果(代码题)
后缀计算4步走
好了,下面看一下这道题
根据前面的后缀计算4步走验证一下这个例子
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 opera) {
if(opera.equals("+") || opera.equals("-") || opera.equals("*") || opera.equals("/")) {
return true;
}
return false;
}
}
4.5 出栈入栈次序匹配
链接 栈的压入、弹出序列_牛客题霸_牛客网 (nowcoder.com)
题目要求:
分析一下
上代码
import java.util.*;
import java.util.ArrayList;
public class Solution {
public boolean IsPopOrder(int [] pushA,int [] popA) {
Stack<Integer> stack = new Stack<>();
int j = 0;
for(int i = 0; i < pushA.length; i++) {
stack.push(pushA[i]);
while(j <popA.length && !stack.empty() && stack.peek().equals(popA[j])) {
stack.pop();
j++;
}
}
return stack.empty();
}
}