目录
一、栈相关基础概念
① 先进先入。不是所有进完才可以出,入的同时也可以出。
②用数组实现栈:入栈和出栈的时间复杂度是O(1)
③用单链表实现栈:
头插法: 入栈和出栈 O(1)
尾插法:入栈和出栈 O(n)
所以用单链表实现栈时一定要头入头出。
④方法 push、peek、pop、size、empty
二、队列相关基础概念
① 队尾进,队头出。底层是个双向链表。
②Queue是个接口,在实例化时必须实例化LinkedList的对象。
Queue<Integer> q = new LinkedList<>();
③用单链表实现队列 ok
④ 用数组实现队列 。必须使用循环数组。后面3.6有代码实现逻辑。
④方法 offer、poll、peek、size、isEmpty
三、OJ题
3.1 不可能的出栈顺序
核心:入的时候同时可以出
3.2 中缀表达式
中缀转后缀:按照自己的理解,从左往右加括号,先乘除后加减。再逐步将运算符移到括号后,移
掉所有的(),就是rug
计算中缀表达式的值:遇见数字入栈,遇到运算符就从栈出两个元素。先出的放在运算符的后面,后出栈的放在运算符前面。将计算结果再入栈。
3.3 括号匹配
实现代码:
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{
// 否则,就与栈顶元素进行比较
// 如果栈是空的,并且遇见的是右括号,直接返回false
// 判断栈空 不能使用stack==null
if(stack.isEmpty()){
return false;
}else{
char peek = stack.pop();
if( (peek == '[' && ch == ']') || (peek == '{' && ch == '}') || (peek == '(' && ch ==')')){
continue;
}else{
return false;
}
}
}
}
if(stack.isEmpty()){
return true;
}else{
return false;
}
}
}
3.4 逆波兰表达式求值
OJ链接:150. 逆波兰表达式求值 - 力扣(LeetCode)
实现代码:
class Solution {
public int evalRPN(String[] tokens) {
Stack<Integer> stack = new Stack<>();
HashSet<String> set = new HashSet<>();
set.add("+");
set.add("-");
set.add("*");
set.add("/");
for(String str:tokens){
// 如果遇见的是不是运算符,是数字,就入栈
if(!set.contains(str)){
stack.push(Integer.parseInt(str));
}else{
// 遇见运算符,就弹出两个数字
int back = stack.pop();
int front = stack.pop();
HashMap<String,Integer> hashMap = new HashMap<>();
hashMap.put("+",front+back);
hashMap.put("-",front-back);
hashMap.put("*",front*back);
// 注意:back有可能为 0
if(back != 0){
hashMap.put("/",front/back);
}
//计算结果,将结果入栈
int result = hashMap.get(str);
stack.push(result);
}
}
return stack.peek();
}
}
3.5 出栈入栈次序匹配
OJ链接:栈的压入、弹出序列_牛客题霸_牛客网 (nowcoder.com)
解题思路:
要点1:定义一个pos,记录popA数组中元素的位置。
要点2:遍历pushA,依次将pushA中的元素入栈,判断栈顶元素和popA中的元素是否相同,相同就出栈,并观察popA中的下一个元素是否和新栈顶元素相等。如果不相等,入栈pushA的下一个元素
实现代码:
import java.util.*;
import java.util.ArrayList;
public class Solution {
public boolean IsPopOrder(int [] pushA,int [] popA) {
Stack<Integer> stack1 = new Stack();
int pos = 0;
for(int i=0;i<pushA.length;i++){
stack1.push(pushA[i]);
while(!stack1.isEmpty() && stack1.peek() == popA[pos]){
stack1.pop();
pos++;
}
}
if(stack1.empty()){
return true;
}
return false;
}
}
3.6 循环队列
OJ链接:622. 设计循环队列 - 力扣(LeetCode)
解题思路:
要点1:为了区分队列空和满,约定:
rear指向数组最后一个元素的下一个位置,下一次添加数据,可以直接往rear位置上放
front == rear 队列空
rear的下一个元素是front,队列满了。(rear+1)%elem.length
要点2:返回队列顶部元素,返回front所在位置元素,front向前移。为了保证front可以从3移到1,front=(front+1)%elem.length
要点3:返回队列最后一个元素。如果rear=0,返回elem[elem.length-1].如果rear不为0,就返回elem[rear-1]
实现代码:
class MyCircularQueue {
private int[] elem;
private int front;
private int rear;
public MyCircularQueue(int k) {
elem = new int[k+1];
}
public boolean enQueue(int value) {
if(isFull()){
return false;
}
if(isEmpty()){
elem[0] = value;
front = 0;
rear = 1;
return true;
}
elem[rear] = value;
rear = (rear+1) % elem.length;
return true;
}
public boolean deQueue() {
if(isEmpty()){
return false;
}
front = (front+1) % elem.length;
return true;
}
public int Front() {
if(isEmpty()){
return -1;
}
return elem[front];
}
public int Rear() {
if(isEmpty()){
return -1;
}
if(rear == 0){
return elem[elem.length-1];
}
return elem[rear-1];
}
public boolean isEmpty() {
return rear == front;
}
public boolean isFull() {
if((rear+1)%elem.length == front){
return true;
}
return false;
}
}
3.7 用队列实现栈
OJ链接:225. 用队列实现栈 - 力扣(LeetCode)
解题思路:
要点1:有两个队列
要点2:第一次放push数据时,默认放到队列1中
要点3:添加元素时,添加到不为空的队列中,这样才能保证顺序
要点4:出栈—》获得栈顶元素—》栈后入先出—》弹出最后一个入栈的元素
因此,需要找出最后一个入队列的元素。将队列的前n-1个元素,放到另外一个队列中,剩下的就是最后一个入队列的元素
实现代码:
class MyStack {
private Queue<Integer> qu1;
private Queue<Integer> qu2;
public MyStack() {
qu1 = new LinkedList<>();
qu2 = new LinkedList<>();
}
public void push(int x) {
// 如果队列2是空的,就往队列1里面放。原因:只有放在不为空的队列中顺序才不会乱
if(qu2.isEmpty()){
qu1.offer(x);
}else{
qu2.offer(x);
}
}
public int pop() {
if(empty()){
return -1;
}
if(qu1.isEmpty()){
// 队列2不为空,队列先进先出,将队列前size-1个元素放到队列1中,剩下的最后一个元素就是最后入队列的,也就是栈的第一个元素,将其返回
int size = qu2.size();
while(size > 1){
qu1.offer(qu2.poll());
size--;
}
int result = qu2.poll();
return result;
}
if(qu2.isEmpty()){
int size = qu1.size();
while(size > 1){
qu2.offer(qu1.poll());
size--;
}
int result = qu1.poll();
return result;
}
return -1;
}
public int top() {
if(empty()){
return -1;
}
if(qu1.isEmpty()){
int size = qu2.size();
while(size > 1){
qu1.offer(qu2.poll());
size--;
}
int result = qu2.poll();
qu1.offer(result);
return result;
}
if(qu2.isEmpty()){
int size = qu1.size();
while(size > 1){
qu2.offer(qu1.poll());
size--;
}
int result = qu1.poll();
qu2.offer(result);
return result;
}
return -1;
}
public boolean empty() {
return qu1.isEmpty()&&qu2.isEmpty();
}
}
3.8 用栈实现队列
OJ链接:232. 用栈实现队列 - 力扣(LeetCode)
解题思路:
要点1:有两个栈。存数据都存到栈1中,取数据都从栈2中取。
要点2:取数据时,如果栈2是空的,就把栈1中的元素全放到栈2中。再从栈2中取。
实现代码:
class MyQueue {
private Stack<Integer> stack1;
private Stack<Integer> stack2;
public MyQueue() {
stack1 = new Stack();
stack2 = new Stack();
}
public void push(int x) {
stack1.push(x);
}
public int pop() {
if(empty()){
return -1;
}
if(stack2.empty()){
while(!stack1.empty()){
stack2.push(stack1.pop());
}
}
return stack2.pop();
}
public int peek() {
if(empty()){
return -1;
}
if(stack2.empty()){
while(!stack1.empty()){
stack2.push(stack1.pop());
}
}
return stack2.peek();
}
public boolean empty() {
return stack1.empty()&&stack2.empty();
}
}
3.9 最小栈
解题思路:
要点1:创建两个栈。一个栈存放所有的元素,另外一个栈存放栈中最小的元素
要点2:第一次push数据,数据存放在两个栈中
要点3:之后存放数据,该数据小于等于最小栈的栈顶元素,才能存放到最小栈和stack中。否则,就只能存放到stack中
实现代码:
class MinStack {
public Stack<Integer> stack;
public Stack<Integer> minStack;
public MinStack() {
stack = new Stack();
minStack = new Stack();
}
public void push(int val) {
// 如果第一次放元素,往stack和minStack里都放
if(stack.empty() && minStack.empty()){
stack.push(val);
minStack.push(val);
return;
}
// 如果存放的数据比minStack元素小,该元素同时放到两个栈中
if(val <= minStack.peek()){
stack.push(val);
minStack.push(val);
return;
}else{
// 否则,就只往stack中放
stack.push(val);
return;
}
}
public void pop() {
int peek = stack.pop();
if(peek == minStack.peek()){
minStack.pop();
}
}
public int top() {
if(stack.empty()){
return -1;
}
return stack.peek();
}
public int getMin() {
if(minStack.empty()){
return -1;
}
return minStack.peek();
}
}