栈:先进后出
队列:先进先出
JVM的栈就是平常所说的一块内存。
此处所说的栈是数据结构
1. 栈(Stack)
1.1 概念
栈:一种特殊的线性表,其只允许在固定的一端进行插入和删除元素操作。进行数据插入和删除操作的一端称为栈 顶,另一端称为栈底。栈中的数据元素遵守后进先出LIFO(Last In First Out)的原则。
压栈:栈的插入操作叫做进栈/压栈/入栈,入数据在栈顶。 出栈:栈的删除操作叫做出栈。出数据在栈顶。


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 栈的模拟实现

import StackEmptyException.StackEmptyException;
import java.util.Arrays;
public class MyStack {
    private int[] elem;
    private int usedSize;
    public MyStack() {
        this.elem = new int[5];
    }
    public void push(int val) {
        if(isFull()) {
            elem = Arrays.copyOf(elem,2*elem.length);
        }
        elem[usedSize] = val;
        usedSize++;
    }
    public boolean isFull() {
        return usedSize == elem.length;
    }
    public int pop() {
        //1、判断栈不为空
        if(empty()) {
            //抛出异常!!
            throw new StackEmptyException("栈为空!");
        }
        //2、开始删除
        return elem[--usedSize];//只需要让usedsize--
    }
    public int peek() {
        //1、判断栈不为空
        if(empty()) {
            //抛出异常!!
            throw new StackEmptyException("栈为空!");
        }
        //2、开始删除
        return elem[usedSize-1];
    }
    public boolean empty() {
        return usedSize == 0;
    }
}1.4 栈的应用场景
1. 将递归转化为循环 比如:逆序打印链表
// 递归方式
        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+" ");
        }
        }
         public void show3(ListNode head) {
        if(head == null) {
            return;
        }
        if(head.next == null) {
            System.out.println(head.val);
            return;
        }
        show3(head.next);
        System.out.println(head.val);
    }
    public void show4() {
        Stack<ListNode> stack = new Stack<>();
        ListNode cur = head;
        while (cur != null) {
            stack.push(cur);
            cur = cur.next;
        }
        //依次出栈
        while (!stack.empty()) {
            ListNode tmp = stack.pop();
            System.out.println(tmp.val);
        }
    }2.中缀表达式转后缀表达式
150. 逆波兰表达式求值 - 力扣(Leetcode)
(10条消息) 中缀表达式转换为后缀表达式_中缀表达式转后缀表达式_石锅拌饭的博客-CSDN博客
//先弹出右操作数---后缀表达式
class Solution {
    public int evalRPN(String[] tokens) {
        Stack<Integer> stack=new Stack<>();
        for(String s:tokens){
            if(!isOpera(s)){
                //数字:放入栈当中
                stack.push(Integer.parseInt(s));//将字符串转成整数
            }
            else{
                //弹出栈顶的两个元素
                int num2=stack.pop();
                int num1=stack.pop();
                switch(s){
                    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();
    }
    public boolean isOpera(String s){
        if(s.equals("+")||s.equals("-")||s.equals("*")||s.equals("/")){
            return true;
        }
        return false;
    }
}3.括号匹配(070243)
20. 有效的括号 - 力扣(Leetcode)
(1)闭合顺序不对([)]
(2)左括号多了(()
(3)右括号多了())
结论:
1.当括号是匹配的时候,最终i遍历完了字符串,并且栈为空
2.遇到不匹配的就直接结束
3.当i遍历完了字符串,但是栈当中仍然有括号,则必定是左括号多了(肯定不匹配)
4.当栈空了,但是字符串还没有遍历完,则必定不匹配
思考:如何判断匹配
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{//是右括号的情况
                if(stack.empty()){
                    return false;
                }
                else{
                    char tmp=stack.peek();
                    if((tmp=='('&&ch==')')||(tmp=='['&&ch==']')||(tmp=='{'&&ch=='}')){
                        //括号匹配成功
                        stack.pop();
                    }
                    else{
                        return false;
                    }
                }
            }
        }
        if(!stack.empty()){
            return false;
        }
        return true;
    }
}4.栈的压入、弹出序列_牛客题霸_牛客网 (nowcoder.com)
1)何时入栈---每次都入
2)什么时候出栈
栈不为空的时候并且栈顶元素和k下标的元素相同时可以出栈
import java.util.*;
public class Solution {
    public boolean IsPopOrder(int [] pushA,int [] popA) {
        Stack<Integer> stack = new Stack<>();
        int j = 0;//变量popA这个数组
        for (int i = 0; i < pushA.length; i++) {
            stack.push(pushA[i]);
            while (!stack.empty() && j < popA.length
                    && stack.peek() == popA[j]) {
                stack.pop();
                j++;
            }
        }
        return stack.empty();
    }
}分析:栈当中的最小值是随时改变的,这个最小值拿的是当前栈中剩下元素的最小的值
随着弹出数据,最小值也是发生改变的,类似于更新的最小值倒着往回取。
1.当栈中第一次存放数据的时候,两个栈中都要存储数据
2.第二次判断大小,看是否要放到min stack中。即第二次开始,每次入栈都需要和最小栈的栈顶元素进行比较,小于的时候才能入栈
3.出栈的时候,每次出栈都要和栈顶元素进行比较,如果和栈顶元素一样,那么两个栈都得出。
155. 最小栈 - 力扣(Leetcode)
import java.util.Stack;
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()) {
            return;
        }
        int val = stack.pop();
        if(val == minStack.peek()) {
            minStack.pop();
        }
    }
    //获取栈顶元素 和 最小栈没有关系
    public int top() {
        if(stack.empty()) {
            return -1;
        }
        return stack.peek();
    }
    //获取元素 不是删除元素
    public int getMin() {
        return minStack.peek();
    }
    
}
2. 队列(Queue)
//从队尾进,队头出
//可以用链表实现
//入栈和出栈操作都是O(1)
//双向链表不管从哪入栈出栈时间复杂度都是O(1)
//如果是单链表,可以考虑头插法进行入栈,出栈,此时时间复杂度为O(1)
//如果是双向链表,入队和出队都可以达到O(1)
//LinkedList也经常被当作栈来使用
 
2.2 队列的使用(队头、队尾--队尾进、队头出 )
 
 
| 方法 | 功能 | 
| boolean offer(E e) | 入队列 | 
| E poll() | 出队列 | 
| peek() | 获取队头元素 | 
| int size() | 获取队列中有效元素个数 | 
| boolean isEmpty() | 检测队列是否为空 | 
public static void main(String[] args) {
        Queue<Integer> q = new LinkedList<>();
        q.offer(1);
        q.offer(2);
        q.offer(3);
        q.offer(4);
        q.offer(5); // 从队尾入队列
        System.out.println(q.size());
        System.out.println(q.peek()); // 获取队头元素
        q.poll();
        System.out.println(q.poll()); // 从队头出队列,并将删除的元素返回
        if(q.isEmpty()){
            System.out.println("队列空");
        }else{
            System.out.println(q.size());
        }
    }2.3 队列模拟实现
队列中既然可以存储元素,那底层肯定要有能够保存元素的空间,通过前面线性表的学习了解到常见的空间类型有 两种:顺序结构 和 链式结构。

public class Queue {
    // 双向链表节点
    public static class ListNode{
        ListNode next;
        ListNode prev;
        int value;
        ListNode(int value){
            this.value = value;
        }
    }
    ListNode first; // 队头
    ListNode last; // 队尾
    int size = 0;
    // 入队列---向双向链表位置插入新节点
    public void offer(int e){
        ListNode newNode = new ListNode(e);
        if(first == null){
            first = newNode;
// last = newNode;
        }else{
            last.next = newNode;
            newNode.prev = last;
// last = newNode;
        }
        last = newNode;
        size++;
    }
    // 出队列---将双向链表第一个节点删除掉
    public int poll(){
// 1. 队列为空
// 2. 队列中只有一个元素----链表中只有一个节点---直接删除
// 3. 队列中有多个元素---链表中有多个节点----将第一个节点删除
        int value = 0;
        if(first == null){
            return null;
        }else if(first == last){
            last = null;
            first = null;
        }else{
            value = first.value;
            first = first.next;
            first.prev.next = null;
            first.prev = null;
        }
        --size;
        return value;
    }
    // 获取队头元素---获取链表中第一个节点的值域
    public int peek(){
        if(first == null){
            return null;
        }
        return first.value;
    }
    public int size() {
        return size;
    }
    public boolean isEmpty(){
        return first == null;
    }
}public class MyQueue {
    static class ListNode {
        public int val;
        public ListNode next;
        public ListNode(int val) {
            this.val = val;
        }
    }
    public ListNode head;
    public ListNode last;
    private int usedSize;
    public void offer(int val) {
        ListNode node = new ListNode(val);
        if(head == null) {
            head = node;
            last = node;
        }else {
            last.next = node;
            last = last.next;
        }
        usedSize++;
    }
    public int getUsedSize() {
        return usedSize;
    }
    public int poll() {
        if(head == null) {
            return -1;
        }
        int val = -1;
        if(head.next == null) {
            val = head.val;
            head = null;
            last = null;
            return val;
        }
        val = head.val;
        head = head.next;
        usedSize--;
        return val;
    }
    public int peek() {
        if(head == null) {
            return -1;
        }
        return head.val;
    }
}2.4 循环队列
实际中我们有时还会使用一种队列叫循环队列。如操作系统课程讲解生产者消费者模型时可以就会使用循环队列。 环形队列通常使用数组实现。

 
 
 2. 下标最前再往前(offffset 小于 array.length): index = (index + array.length - offffset) % array.length

 
622. 设计循环队列 - 力扣(Leetcode)
class MyCircularQueue {
    private int[] elem;
    private int front;//队头下标
    private int rear;//队尾下标
    public MyCircularQueue(int k) {
        this.elem = new int[k+1];
    }
    //入队
    public boolean enQueue(int value) {
        if(isFull()) {
            return false;
        }
        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;
        }
        int index = (rear == 0) ? elem.length-1 : rear-1;
        return elem[index];
    }
    
    public boolean isEmpty() {
        return rear == front;
    }
    
    public boolean isFull() {
        return (rear+1) % elem.length == front;
    }
    public static void main(String[] args) {
        MyCircularQueue myCircularQueue = new MyCircularQueue(3);
        System.out.println(myCircularQueue.enQueue(1));
        System.out.println(myCircularQueue.enQueue(2));
        System.out.println(myCircularQueue.enQueue(3));
        System.out.println(myCircularQueue.enQueue(4));
        System.out.println(myCircularQueue.Rear());//  2
        System.out.println(myCircularQueue.isFull());
        System.out.println(myCircularQueue.deQueue());
        System.out.println(myCircularQueue.enQueue(4));
        System.out.println(myCircularQueue.Rear());
    }
}3. 双端队列 (Deque)

Deque是一个接口,使用时必须创建LinkedList的对象。

Deque<Integer> stack = new ArrayDeque<>();// 双端队列的线性实现Deque<Integer> queue = new LinkedList<>();// 双端队列的链式实现
1)push元素的时候应当放在那里?哪个队列不为空就放在哪里
2)出栈的时候,出不为空的队列size-1元素,剩余元素是要出栈的元素
import java.util.LinkedList;
import java.util.Queue;
class MyStack {
    private Queue<Integer> qu1;
    private Queue<Integer> qu2;
    public MyStack() {
        qu1 = new LinkedList<>();
        qu2 = new LinkedList<>();
    }
    
    public void push(int x) {
        //放到不为空的队列
        if(!qu1.isEmpty()) {
            qu1.offer(x);
        }else if(!qu2.isEmpty()) {
            qu2.offer(x);
        }else {
            //如果都是空的 放到第一个
            qu1.offer(x);
        }
    }
    
    public int pop() {
        //两个队列都是空的: 栈为空
        if(empty()) {
            return -1;
        }
        if(!qu1.isEmpty()) {
            int currentSize = qu1.size();
            for (int i = 0; i < currentSize-1; i++) {
                int x = qu1.poll();
                qu2.offer(x);
            }
            return qu1.poll();//最后一个数据返回
        }
        if(!qu2.isEmpty()) {
            int currentSize = qu2.size();
            for (int i = 0; i < currentSize-1; i++) {
                int x = qu2.poll();
                qu1.offer(x);
            }
            return qu2.poll();//最后一个数据返回
        }
        return -1;
    }
    //peek方法
    public int top() {
        if(empty()) {
            return -1;
        }
        if(!qu1.isEmpty()) {
            int currentSize = qu1.size();
            int x = -1;
            for (int i = 0; i < currentSize; i++) {
                x = qu1.poll();
                qu2.offer(x);
            }
            return x;//最后一个数据返回
        }
        if(!qu2.isEmpty()) {
            int currentSize = qu2.size();
            int x = -1;
            for (int i = 0; i < currentSize; i++) {
                x = qu2.poll();
                qu1.offer(x);
            }
            return x;//最后一个数据返回
        }
        return -1;
    }
    
    public boolean empty() {
        return qu1.isEmpty() && qu2.isEmpty();
    }
}












![[C++][opencv]opencv填充透明色到不规则polygon区域](https://img-blog.csdnimg.cn/579fe75cf947424eb8e4fc124a183967.jpeg)





