当前位置: 首页 > news >正文

【数据结构】栈(Stack)、队列(Queue)、双端队列(Deque) —— 有码有图有真相

目录

栈和队列

1. 栈(Stack)

1.1 概念

1.2 栈的使用(原始方法)

1.3 栈的模拟实现

【小结】

2. 栈的应用场景

1、改变元素的序列

2、将递归转化为循环

3、逆波兰表达式求值

4、括号匹配

5、出栈入栈次序匹配

6、最小栈

【概念区分】栈、虚拟机栈、栈帧有什么区别呢?

3. 队列(Queue)

3.1 概念

3.2 队列的使用(原生方法)

3.3 队列的模拟实现

3.4 循环队列

4. 双端队列 (Deque)

【面试题】

1、用队列模拟实现栈 (普通队列和普通栈)

2、用栈模拟实现队列(普通栈和普通队列)

【总结】


栈和队列

Vector基本上过时了,这里我们不进行讲解。

  • LinkedList 不仅能当做链表(用List接口引用),也能当做队列(用Deque接口和Queue接口引用)
  • 主要看你用哪种接口调用LinkedList(一定是LinkedList实现的接口),LinkedList 就表现哪种形式。在使用方法的时候必须满足接口中组织数据的形式。
  • 主要看用哪种方式维护LinkedList的

1. 栈(Stack)

栈是一种数据结构,实体类,特点先进后出。如果数据要支持先进后出这种特点,就要选用数据结构栈这种方式来存储或组织数据。

  • 数据结构中,结构就是来描述组织数据的方式的,之所以会有很多数据结构,是因为我们有不同的需求,组织的数据方式不同,有了很多种存储或组织数据的方式,所以就有了很多的数据结构。
  • 栈只是一种数据结构,我们可以用数组(顺序表),单链表,双链表来实现栈(维护栈,但是在使用方法的时候必须满足先进后出的形式。
  • 栈的底层本来就是一个数组,因为stack类继承了Vector类,而Vector类底层就是一个数组在存储数据。

1.1 概念

栈:一种特殊的线性表,其只允许在固定的一端进行插入和删除元素操作

进行数据插入和删除操作的一端称为栈顶,另一端称为栈底。栈中的数据元素遵守后进先出LIFO(Last In First Out)的原则

  • 压栈:栈的插入操作叫做进栈/压栈/入栈,入数据在栈顶
  • 出栈:栈的删除操作叫做出栈。出数据在栈顶

栈在现实生活中的例子

子弹压弹以及发射。羽毛球放入桶中以及拿出来。

1.2 栈的使用(原始方法)

Stack类中的方法原生方法比较少。size方法继承于父类

方法功能
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()); // 获取栈中有效元素个数---> 4System.out.println(s.peek()); // 获取栈顶元素---> 4s.pop(); // 4出栈,栈中剩余1 2 3,栈顶元素为3System.out.println(s.pop()); // 3出栈,栈中剩余1 2 栈顶元素为2if(s.empty()){System.out.println("栈空");}else{System.out.println(s.size());}
}

1.3 栈的模拟实现

栈的底层本就是一个数组,可以用数组(顺序表),单链表,双链表模拟实现栈,但是在使用方法的时候必须满足先进后出的形式。

从上图中可以看到,Stack继承了Vector,Vector和ArrayList类似都是动态的顺序表,不同的是Vector是线程安全的。

1、顺序栈(用数组实现栈)

  • 里面的数据是拿数组(顺序表,动态扩容的数组)来组织实现的。
  • 栈的底层其实就是一个数组,但是我们提供的方法必须满足先进后出的形式。

 数组长度与数组中有效元素个数一定要分清楚。

这里usedSize,不仅可以记录有效元素个数,还能标记当前存储数据元素下标。

public class MyStack implements IStack {private int[] elem;private int usedSize;//1. 记录有效元素个数//2. 存储数据元素下标private static final int DEFAULT_CAPACITY = 10;public MyStack() {elem = new int[DEFAULT_CAPACITY];}//入栈@Overridepublic void push(int x) {if (full()) {elem = Arrays.copyOf(elem, elem.length * 2);}this.elem[usedSize] = x;usedSize++;}//出栈@Overridepublic int pop() throws StackEmptyException {if (empty()) {throw new StackEmptyException("栈为空!");}int old = elem[usedSize - 1];usedSize--;//相当于是删除// 如果里面为引用类型// elem[usedSize] = null;return old;}//获取栈顶元素,只查看不删除@Overridepublic int peek() {if (empty()) {throw new StackEmptyException("栈为空!");}return this.elem[usedSize -1];}//栈中有效元素个数@Overridepublic int size() {return this.usedSize;}//栈是否为空@Overridepublic boolean empty() {return usedSize == 0;}//栈中元素是否满了@Overridepublic boolean full() {return usedSize == elem.length;}//遍历打印//注意:该方法并不是栈中的方法,为了方便看测试结果给出的public void display() {for (int i = 0; i < usedSize; i++) {System.out.print(elem[i] + " ");}System.out.println();}
}

栈为空异常类:

public class StackEmptyException extends RuntimeException{public StackEmptyException(String message) {super(message);}
}

2、用链表实现栈(链式栈)

  • 用单向链表实现栈:其中入栈用头插,出栈也是在头部,时间复杂度为O(1)
  • 用双向链表实现栈:头尾都可以作为出栈和入栈。时间复杂度为O(1)

【小结】

数组维护(实现)栈

  • 栈的底层本质就是数组(动态扩容的数组)
  • 出栈和入栈时间复杂度都是为O(1)

链表实现栈 (链式栈):

单链表维护栈

  • 需要满足时间复杂度为O(1),所以从头部插入(入栈),从头部删除(出栈)
  • 如果从尾部插入和删除,非常的麻烦(插入的时候需要找尾节点,删除的时候还需要找尾结点)时间复杂度都为O(n)

双向链表维护栈

  • 从头部插入(入栈),从头部删除(出栈)
  • 从尾部插入(入栈),从尾部删除(出栈)
  • 不管从哪里入栈和出栈时间复杂度都是O(1)

栈的底层本就是一个数组,可以用数组(顺序表),单链表,双链表模拟实现栈但是在使用方法的时候必须满足先进后出的形式。

2. 栈的应用场景

1、改变元素的序列

2、将递归转化为循环

比如:逆序打印链表

用递归方法实现逆序打印链表

递归找两个条件:1、终止条件。2、找自己本身

//递归打印链表
public void display3(LinkedList pHead) {if (pHead == null) {return;}if (head.next == null) {System.out.println(pHead.val + " ");}display3(pHead.next);System.out.println(pHead.val + " ");
}

递归其实就是栈的形式,每次递归都要在栈上开辟栈帧,用栈模拟递归的形式。

利用栈(链式栈,用链表实现栈)去打印逆序链表

思路: 栈是先进后出的,所以把链表中的节点放进栈中,然后出栈打印,就能逆序打印出链表了

//循环方法 利用栈
public void display4() {Stack<ListNode> stack = new Stack<>();ListNode cur = head;//将链表中的节点保存到栈中while (cur != null) {stack.push(cur);cur = cur.next;}//遍历栈while (!stack.empty()) {System.out.print(stack.pop() + " ");}System.out.println();
}

3、逆波兰表达式求值

给你一个字符串数组 tokens ,表示一个根据 逆波兰表示法 表示的算术表达式。

请你计算该表达式。返回一个表示表达式值的整数。OJ链接

什么是逆波兰表达式:

  • 将中缀表达式" 1 + ( ( 2 + 3 ) * 4 ) - 5 "转换为后缀表达式(逆波兰表达式)为:" 1 2 3 + 4 * + 5 - "
  • 计算器怎么知道字符( + - * / )的优先级的高低呢(怎么识别括号的),一般在计算器中运算会把中缀表达式转换为后缀表达式,再通过后缀表达式计算这个表达式的值。
  • 中缀表达式转换为后缀表达式(逆波兰表达式)步骤,选择题或填空题小技巧

第一步:从左到右,先乘除后加减,都填上括号

第二步:对应的运算符,挪倒右边括号的外面,只挪一次

第三步:把所有的括号都去掉,就得到了后缀表达式(逆波兰表达式)

拿到后缀表达式了,怎么去计算这个后缀表达式结果呢?

思路:

  1. 遍历后缀表达式,把后缀表达式中的元素依次放进栈中,如果遇到了运算符,则弹出栈顶的两个元素,
  2. 第一个元素作为运算符的右操作数第二个元素作为运算符的左操作数,进行计算,只要遇到运算符就弹出栈顶两个元素进行运算,运算后的结果再次入栈,
  3. 然后接着让后缀表达式中的元素入栈,直到后缀表达式的元素全部计算完。

遍历后缀表达式,只要是数字就入栈,是字符就弹出栈顶的两个元素,参与运算,最后把运算之后的结果,再次入栈。

这里给了字符串数组,避免二位数的数字被拆分开。

public int evalRPN(String[] tokens) {//创建一个栈Stack<Integer> stack = new Stack<>();//遍历后缀表达式for (String x : tokens) {//如果是操作数,放到栈中if (!isOperation(x)) {//这里将String类类型拆行为int基本类型并压栈//压栈时自动装箱stack.push(Integer.parseInt(x));} else {//如果是运算符则需要弹出栈顶两个元素,作为操作数进行运算//第一个为右边操作数,第二个左边操作数int num2 = stack.pop();int num1 = stack.pop();/*if (x.equals("+")) {stack.push(num1 + num2);}if (x.equals("-")) {stack.push(num1 - num2);}if (x.equals("*")) {stack.push(num1 * num2);}if (x.equals("/")) {stack.push(num1 / num2);}*///这里用switch语句更简洁,如果用if语句形式太多太复杂//判断是哪种运算符,并进行运行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;}}}//走完for循环,栈内就剩一个元素了 即计算后的结果,直接返回return stack.pop();
}//判断是否是运算符
private boolean isOperation(String s) {if (s.equals("+") || s.equals("-") || s.equals("*") || s.equals("/")) {return true;} else {return false;}
}

4、括号匹配

给定一个只包括 '(',')','{','}','[',']' 的字符串 s ,判断字符串是否有效。OJ链接

有效字符串需满足:左括号必须用相同类型的右括号闭合。左括号必须以正确的顺序闭合。每个右括号都有一个对应的相同类型的左括号。

分为几种情况:

  • 匹配。字符串遍历完成且栈为空。
  • 不匹配。
  • 一部分匹配
  • 左括号多:字符串遍历完成,栈不空
  • 右括号多:字符串没有遍历完,栈空了

思路:

  1. 遍历字符串,让左括号入栈,遇到右括号从栈中弹出元素,
  2. 看该元素是否是与右括号匹配的左括号(如果是有效的字符串,则一定是第一个右括号与最后一个左括号先匹配),
  3. 字符串遍历完了同时栈也空了,说明匹配的。
//括号匹配
public boolean isValid(String s) {Stack<Character> stack = new Stack<>();//1.遍历字符串for (int i = 0; i < s.length(); i++) {char ch = s.charAt(i);//2.判断是否是左括号if (ch == '(' || ch == '{' || ch == '[') {stack.push(ch);} else {//3.遇到了右括号//判断是否是第一次遇见右括号 同时判断了字符串没有遍历完,栈就为空的情况if (stack.empty()) {return false;}char ch2 = stack.peek();//ch2是左括号,获取栈顶元素不删除 此时ch是右括号//出栈的左括号是否跟此时右括号匹配if ((ch2 == '(' && ch == ')') || (ch2 == '{' && ch == '}') || (ch2 == '[' && ch == ']')) {stack.pop();} else {return false;}}}if(!stack.empty()) {return false;}return true;
}

5、出栈入栈次序匹配

输入两个整数序列,第一个序列表示栈的压入顺序,请判断第二个序列是否可能为该栈的弹出顺序。假设压入栈的所有数字均不相等。OJ链接

例如序列1,2,3,4,5是某栈的压入顺序,序列4,5,3,2,1是该压栈序列对应的一个弹出序列,但4,3,5,1,2就不可能是该压栈序列的弹出序列。

思路:

  1. 遍历第一个序列进行压栈,每次压栈后栈顶的元素与第二个序列中的元素进行匹配,
  2. 如果一样,第二个序列 j 往后走继续比较,如果不同,第一个序列继续压栈然后再比较,直到第一个序列遍历完,
  3. 如果栈中没有元素,则出栈入栈次序匹配,如果栈中还有元素没有匹配,则出栈入栈次序不匹配。
//出栈入栈次序匹配
public boolean IsPopOrder (int[] pushV, int[] popV) {Stack<Integer> stack = new Stack<>();int j = 0;//遍历第一个序列并压栈for (int i = 0; i < popV.length; i++) {stack.push(pushV[i]);//判断栈顶元素是否与第二序列j位置的元素是否相同while (!stack.empty() && j < popV.length && stack.peek() == popV[j]) {stack.pop();j++;}}//栈中是否还有元素return stack.empty();/*if (!stack.empty()) {return false;}return true;*/
}
  • 这里注意stack.peek() == popV[j] ,我们需要对stack是否为空和popV[j]是否越界进行判断。
  • 有可能栈空了,j 还没走完
  • 有可能 j 走完了,栈还没为空
  • 还有一点:stack.peek() == popV[j] 比较的时候尽量用 equals 进行比较。
  • Integer的取值范围是 [-128,127]之间,如果弹出的元素在这个范围之内用 == 比较没问题,如果超过了这个范围 这个语句表达的结果就是false

6、最小栈

设计一个支持 push ,pop ,top 操作,并能在常数时间内检索到最小元素的栈。即时间复杂度为O(1) OJ链接

实现 MinStack 类:

MinStack() 初始化堆栈对象。

void push(int val) 将元素val推入堆栈。

void pop() 删除堆栈顶部的元素。

int top() 获取堆栈顶部的元素。

int getMin() 获取堆栈中的最小元素。

  • poptop 和 getMin 操作总是在 非空栈 上调用。

问题分析:

  • 如果只有一个栈,把元素全部压进去,而最小的元素在栈底,那我们必须把栈底上面的元素都出栈,才能得到这个最小的元素,
  • 如果有n个元素,那么检索到最小元素就不是常数时间了(即要在O(1)的时间内拿到最小值)。且在出栈的过程中最小值是在变化的。

思路:

  1. 所以必须有两个栈,一个普通栈Stack,一个最小栈minStack。 在第一次往Stack中压栈的时候,无论放值多大或多小,我们都要维护这个值,把它压入minStack栈中,
  2. 再次往Stack中压栈时,压栈的元素要与minStack栈顶元素比较,如果小于或等于minStack栈顶元素,我们也要维护这个元素,在Stack中压栈的同时也要把该元素压入minStack栈中,如果大于只在Stack中压栈,
  3. 当压栈序列遍历完后,最小栈minStack栈顶元素就是这个压栈序列的最小元素了,能在常数时间内检索到最小元素的栈。
  4. 出任何元素的时候如果这个元素在最小栈minStack中有维护的话,minStack栈中也需要出栈。且在出栈的过程中最小值是在变化的。

应题目要求,pop,top和getMin 操作总是在 非空栈 上调用,所以这些方法不用再判断栈是否为空。

import java.util.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.push(val);if (minStack.empty()) {minStack.push(val);return;}int peekVal = minStack.peek();//-1 < 3if (val <= peekVal) {minStack.push(val);}}//出栈public void pop() {int val = stack.pop();if (val == minStack.peek()) {minStack.pop();}}// peek 获得当前普通栈的栈顶元素,不删除public int top() {return stack.peek();}//最小栈的peek,每次通过这个方法获取 最小值public int getMin() {return minStack.peek();}
}

当写的代码出错了,一定记得调试:

  1. 看哪个测试用例错了
  2. 拿着这个测试用例去调试画图
  3. 一定记住不能光看代码!!!看代码是看不出来的
  4. 如果通过肉眼就能看到代码的问题那为什么我们要有编译器呢?直接记事本写代码不就好了。

【概念区分】栈、虚拟机栈、栈帧有什么区别呢?

  • 栈是一种先进后出的数据结构
  • 虚拟机栈是JVM划分的一块内存
  • 栈帧:运行程序调用方法时会在虚拟机当中,给这个方法开辟一块内存(空间),开辟的空间就叫作栈帧。

每个函数(方法)在运行时,jvm都会创建一个栈帧,然后将栈帧压入到虚拟机栈中,当函数调用结束时,该函数对应的栈帧会从虚拟机栈中出栈。

注意:每个方法在栈帧中结构都是一样,大小可能不同,栈帧的大小在程序编译时已经确定好的。

3. 队列(Queue)

3.1 概念

队列:只允许在一端进行插入数据操作,在另一端进行删除数据操作的特殊线性表,队列具有先进先出FIFO(FirstIn First Out)

  • 入队列:进行插入操作的一端称为队尾(Tail/Rear)
  • 出队列:进行删除操作的一端称为队头 (Head/Front)

3.2 队列的使用(原生方法)

在Java中,Queue是个接口底层是通过链表实现的

Queue接口里有两组方法,即add,remove,element与offer,poll,peek是对应的,但它们是有区别的,下面会讲。常用的为后者。size和isEmpty方法是继承父类的。

方法功能
boolean offer(E e)入队列
E poll()出队列
peek()获取队头元素(查看元素,不删除)
int size()获取队列中有效元素个数
boolean isEmpty()检测队列是否为空

注意:Queue是个接口,在实例化时必须实例化LinkedList的对象,因为LinkedList实现了Queue接口。

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());}
}
//执行结果
5
1
2
3

3.3 队列的模拟实现

队列中既然可以存储元素,那底层肯定要有能够保存元素的空间,通过前面线性表的学习了解到常见的空间类型有两种:顺序结构 和 链式结构同学们思考下:队列的实现使用顺序结构还是链式结构好?链式实现(使用较多),数组(顺序表)实现。

队列的底层本就是一个双向链表,可以用数组(顺序表),单链表,双链表模拟实现队列,但是在使用方法的时候必须满足先进先出的形式。

1、用链式实现队列(链式队列)

单链表实现队列:

  • 入:头插法 -> O(1)     出:删除链表最后一个(需要找尾巴) O(n)
  • 入:尾插法 -> O(N)(需要找尾巴)    出:删除链表第一个 O(n)
  • 需要满足出队列 入队列,时间复杂度为O(1),队列的特性。

双链表实现队列(用的最多)

  • 入:头插法 -> O(1)    出:删除链表最后一个 O(1)
  • 入:尾插法 -> O(1)    出:删除链表第一个 O(1)

单链表实现队列哪种形式 时间复杂度都是O(n),有局限性;而双链表实现队列哪种形式 时间复杂度都是O(1),而且队列的底层本来就是双链表实现的,所以使用双链表实现较多。

双向链表模拟实现队列:

//双向链表模拟实现队列
public class MyLinkedListQueue {//节点,内部类public static class ListNode {public int val;public ListNode prev;public ListNode next;public ListNode(int val) {this.val = val;}}public ListNode head;public ListNode last;public int usedSize;//入队列,尾插public boolean offer(int val) {ListNode node = new ListNode(val);if (head == null) {head = node;last = node;} else {last.prev.next = node;node.prev = last;last = node;}usedSize++;return true;}//出队列,头删public int poll() throws QueueEmptyException {if (head == null)  {throw new QueueEmptyException("队列为空!");}int retVal =  head.val;if (head.next == null) {head = null;last = null;} else {head = head.next;head.prev = null;}usedSize--;return retVal;}//获取队头元素,不删除public int peek() throws QueueEmptyException {if (head == null) {throw new QueueEmptyException("队列为空!");}return head.val;}public int size() {return usedSize;}public boolean empty() {return head == null;}
}

队列为空的自定义异常类

public class QueueEmptyException extends RuntimeException {public QueueEmptyException(String message) {super(message);}
}

单向链表模拟实现队列:

  • 这里我们为了更方便模拟实现,在单链表中定义了head指向头节点,last 指向尾节点,usedSize记录节点个数。
  • 需要满足时间复杂度为O(1),队列的特性。只能从尾入,从头删,时间复杂度就都是O(1)了;如果从头进,从尾删,时间复杂度仍为O(n),因为尾结点删除你找不到上一个节点是谁了(单向的)。
  • 如果是双向链表实现就没有局限,所以双向链表才是最通用的链表。
//单向链表模拟实现队列
public class MySingleListQueue {//节点,内部类public static class ListNode {public int val;public ListNode next;public ListNode(int val) {this.val = val;}}public ListNode head;public ListNode last;public int usedSize;//入队列,尾插public boolean offer(int val) {ListNode node = new ListNode(val);if (head == null) {head = node;last = node;} else {last.next = node;last = node;}usedSize++;return true;}//出队列,头删public int poll() throws QueueEmptyException {if (head == null)  {throw new QueueEmptyException("队列为空!");}int retVal =  head.val;if (head.next == null) {head = null;last = null;} else {head = head.next;}usedSize--;return retVal;}//获取队头元素,不删除public int peek() throws QueueEmptyException {if (head == null) {throw new QueueEmptyException("队列为空!");}return head.val;}public int size() {return usedSize;}public boolean empty() {return head == null;}
}

2、用数组实现队列(顺序队列)

数组(顺序表)实现队列会出现下面情况,从reat位置入(队尾),从front位置出(队头),如果数组后面已经满了,而前面因为删除了元素空出了,怎么才能利用起来前面的空间呢,

rear是当前可以存放数据元素下标。

如果说让元素整体往前挪,也需要消耗时间;我们能不能让reat再回去,所以我们引出了环形队列(循环队列)。

下面的循环队列,就是用数组(顺序表)模拟实现的队列。

3.4 循环队列

实际中我们有时还会使用一种队列叫循环队列。如操作系统课程讲解生产者消费者模型时可以就会使用循环队列。环形队列通常使用数组实现。

rear是当前可以存放数据元素的下标,与数组模拟实现栈中useSize异曲同工之妙。

rear可能放元素放满了,与front相遇;或者front出元素数组为空了,与rear相遇。

问题1、rear和front相遇了,如何区分空与满

解决空还是满,有很多种方案:

  1. 使用usedSize进行记录
  2. 浪费一个空间来表示满
  3. 使用标记

这里我们主要讲解第二种方案:

例如:这里要放89我们可以先让rear判断下一个是否是front,如果下一个是front证明满了不能放了。否则没满。即每次放元素之先rear判断一下。

  • 如果front与rear相遇,就认为是空的。
  • 如果reat下一个是front,就认为是满的。

问题2、rear怎么从7下标,来到0下标

  • rear从7下标到0下标,同时兼顾1下标到2下标这种普通情况。
  • rear = (rear + 1) % len
  • front = (front + 1) % len

设计一个循环队列。OJ链接

class MyCircularQueue {public int[] elem;//public int usedSize;public int front;//表示队头public int rear;//表示队尾public MyCircularQueue(int k) {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 front == rear;}//检查循环队列是否已满public boolean isFull() {return (rear + 1) % elem.length == front;}
}

这里有两点要注意的:

  1. ​​​rear是当前可以存放数据元素的下标,获取队尾元素时会有一种极端情况,如果此时rear是0下标位置,找队尾下标就是下标为7的位置,不能直接通过rear-1 来获得队尾下标。
  2. 传入参数k空间容量大小,但是我们是通过浪费一个空间来表示满的情况,需要数组多申请一个,即new  k+1个空间的数组。或者定义usedSize,通过数据的个数来表示front与rear相遇时是满的还是空的情况,就不存在浪费空间的说法了。

4. 双端队列 (Deque)

双端队列(deque)是指允许两端都可以进行入队和出队操作的队列,deque 是 “double ended queue” 的简称。那就说明元素可以从队头 出队和入队,也可以从队尾 出队和入队。

Deque是一个接口,使用时必须创建LinkedList的对象。底层是双向链表实现的

我们发现,Deque中包含了很多方法:

  • Deque底层有 offer(),poll(),peek()  和 add(),remove(),element() 
  • Queue底层也有 offer(),poll(),peek()  和 add(),remove(),element() 
  • 它们属于两组方法,功能互相对应。
  • 区别:底层代码注解为,add 插不了元素会抛异常,对于offer 来说不会抛异常,认为offer 方法优于add

在实际工程中使用Deque接口是比较多的,栈和队列均可以使用该接口:

Deque<Integer> stack = new ArrayDeque<>();//双端队列的线性实现
Deque<Integer> queue/deque = new LinkedList<>();//双端队列的链式实现
  •  不仅可以当做队列,也可以当做栈。
  • 双端队列线性实现 -》 常用当做栈使用,因为栈底层本身就是数组。
  • 双端队列链式实现 -》 常用当做队列或双端队列使用,因为队列或双端队列底层本身就是链表(双向链表)

【面试题】

他实现了我: 必须是他类中的方法,遵循我的形式,来模拟实现我的形式。

1、用队列模拟实现栈 (普通队列和普通栈)

请你仅使用两个队列实现一个后入先出(LIFO)的栈,并支持普通栈的全部四种操作(pushtoppop 和 empty)。OJ链接

问题分析:

  • 队列是先进先出的,栈是先进后出的,这两个矛盾的,所以要用队列实现栈,一个队列是无法实现栈的。需要两个队列实现栈。
  • 用队列实现栈 ,那拿什么实现队列呢,用Linkedlist(双向链表)实现队列。
  • 为什么不用ArrayDeque (数组)实现链表呢,这就把问题拉回到了用数组和链表区别的问题了。因为数组实现队列有一个缺点就是扩容问题,可能会浪费空间;用Linkedlist实现队列比较多。
  • 用链表也可以实现栈,用数组也可以实现栈,但是用数组实现的比较多,用链表实现栈每次需要维护它的指向,所以具体使用看场景。

思路:

  • 一个队列是无法实现栈的,需要两个队列实现栈。实例化两个队列,一个为qu1,一个为qu2。
  • ”入栈“操作让元素进入非空的队列,如果非空的队列为qu1 ,则让元素入队列(qu1),就是用队列实现了"入栈"的操作,此时”出栈“操作 则是让qu1中的size-1个元素出队列,然后依次人队列(qu2)中,此时qu1中还剩一个元素,出队列,就是用队列实现了"出栈"的操作;
  • 如果非空的队列为qu2,则让元素入队列(qu2),就是用队列实现了"入栈"的操作,此时”出栈“操作 则是让qu2中的size-1个元素出队列,然后依次人队列(qu1)中,此时qu2中还剩一个元素,出队列,就是用队列实现了"出栈"的操作;
  • 如果两个队列都是空的话,则让元素入队列(qu1),就是用队列实现了"入栈"的操作。

  1. ”入栈“:元素放到不为空的队列中 , 如果两个队列都为空那么就放到第一个队列当中。
  2. ”出栈“:元素不为空的队列,出size-1个元素到另一个队列当中
  3. 当两个队列都为空的时候,那么说明模拟的栈是空的
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;//或者抛出自定义异常}//判断哪个队列不为空//出size - 1个元素到另一队列中if (!qu1.isEmpty()) {int size = qu1.size();//进来后,que1队列出size - 1个元素到qu2中for (int i = 0; i < size - 1; i++) {//for (int i = 0; i < qu1.size()-1; i++) {//这里的for循环不能这样写,因为qu1中的size一直在变,//每次qu1中元素出栈后底层的size就会减减,会导致循环次数减少int x = qu1.poll();qu2.offer(x);}return qu1.poll();} else {int size = qu2.size();//进来后,que2队列出size - 1个元素到qu1中for (int i = 0; i < size - 1; i++) {int x = qu2.poll();qu1.offer(x);}return qu2.poll();}}//队列中模拟实现栈的获得栈顶元素操作public int top() {//两个队列都为空if (empty()) {return -1;//或者抛出自定义异常}//判断哪个队列不为空//出size个元素到另一队列中if (!qu1.isEmpty()) {int size = qu1.size();int x = -1;//进来后,que1队列出size个元素到qu2中for (int i = 0; i < size; i++) {x = qu1.poll();qu2.offer(x);}return x;} else {int size = qu2.size();int x = -1;//进来后,que2队列出size个元素到qu1中for (int i = 0; i < size; i++) {x = qu2.poll();qu1.offer(x);}return x;}}//队列中模拟实现栈的是否为空操作public boolean empty() {//两个队列同时为空时,说明模拟实现的栈是空的return qu1.isEmpty() && qu2.isEmpty();}
}

这里代码要注意两点:

  1. 队列中模拟实现栈的出栈操作和获得栈顶元素时,for遍历一个队列size - 1个元素出队列到另一队列时,size() - 1 不能作为for循环的判断条件。因为每次元素从该队列出去,size()在不断减少(变化),会导致循环次数减少。定义一个变量记录一下size()的值。
  2. 队列中模拟实现栈的获得栈顶元素操作时,我们需要一个队列中的size个元素全部到另一个队列中,定义一个变量,每次出队列到另一个队列的元素都记录一下,最后一次记录的就是需要的元素。

2、用栈模拟实现队列(普通栈和普通队列)

请你仅使用两个栈实现先入先出队列。队列应当支持一般队列支持的所有操作(pushpoppeekempty) OJ链接

思路:

  • 一个栈是无法实现队列的,需要两个栈实现队列。实例化两个栈,一个是s1,一个是s2。
  • “入队列”操作让元素进入s1栈中,就是用栈实现了“入队列”操作。
  • “出队列”操作让 s1栈中的元素 全部(size()个)依次出栈,然后进入s2栈中,此时s2栈中的元素顺序与之前s1栈中的元素顺序是相反的,然后出s2栈 栈顶的元素,就用栈实现了“出队列”的操作。

  • “入队”:入到s1中
  • “出队”:s2栈为空,s1中的元素一个一个全部倒放到第二个队列当中。
//用栈模拟实现队列(普通栈和普通队列)
class MyQueue {private Deque<Integer> s1;private Deque<Integer> s2;//private Stack<Integer> s1;//private Stack<Integer> s2;public MyQueue() {s1 = new ArrayDeque<>();s2 = new ArrayDeque<>();}//栈中模拟实现队列的入队列操作public void push(int x) {s1.push(x);}//栈中模拟实现队列的出队列操作public int pop() {if (empty()) {return -1;//或抛出自定义异常,但力扣上认为-1是正确的}if (s2.isEmpty()) {while (!s1.isEmpty()) {s2.push(s1.pop());}}//代码走到这里s2中一定有元素return s2.pop();}//栈中模拟实现队列的获得队头元素操作public int peek() {if (empty()) {return -1;//或抛出自定义异常,但力扣上认为-1是正确的}if (s2.isEmpty()) {while (!s1.isEmpty()) {s2.push(s1.pop());}}//代码走到这里s2中一定有元素return s2.peek();}//栈中模拟实现队列的是否为空操作public boolean empty() {return s1.isEmpty() && s2.isEmpty();}
}

这里同样也要注意:栈中模拟实现队列的出栈操作和获得队头元素时,如果使用for遍历一个栈size个元素出栈到另一栈时,size()不能作为for循环的判断条件。因为每次元素从该栈出去,size()在不断减少(变化),会导致循环次数减少。定义一个变量记录一下size()的值。

【总结】

分清楚一点

栈(Stack)是普通类 先进后出

  • 栈可以实例化,可以被数组(顺序表)、单链表、双链表实现;栈继承了Vector类,而Vector类底层就是一个数组在存储数据,所以栈的底层其实就是数组(顺序表)
  • 通过数组(顺序表)、单链表、双链表实现的栈调用方法必须遵先进后出的形式。 拿数组(顺序表)实现的栈最常用。
  • 用Stack 去引用自己的对象其实不常用。Stack<Integer> stack = new Stack<>(); 不常用
  • 用ArrayDeque双端队列的线性实现,来代替实现栈。Deque<Integer> stack = new ArrayDeque<>(); 常用
  • ArrayDeque类 底层也是数组(顺序表结构)

队列(Queue)是一个接口 先进先出

  • 队列不能实例化,但可以被数组(顺序表)、单链表、双链表实现;队列的底层是被LinkedList类实现的,LinkedList底层又是双向链表,所以队列的底层其实就是双向链表。
  • 通过数组(顺序表)、单链表、双链表实现的队列调用方法必须遵循先进先出的形式。
  • 拿LinkedList(双向链表)来实现队列最常用。Queue<Integer> queue = new LinkedList<>();

双端队列(Deque)是一个接口 双端进出都可以

  • 双端队列不能实例化,但可以被数组(顺序表)、单链表、双链表实现;双端队列的底层是被LinkedList类实现的,LinkedList底层又是双向链表,所以双端队列的底层其实就是双向链表
  • 通过数组(顺序表)、单链表、双链表实现的队列调用方法必须遵循的双端进出都可以形式。
  • 拿LinkedList(双向链表)来实现双端队列最常用。Deque<Integer> deque/queue = new LinkedList<>();

拿接口去引用对象 与 拿对象本身来引用自己对象有什么区别?

  • 区别:拿对象本身来引用自己对象包含的方法多,能调用的方法多。
  • 一般拿接口去实现具体类使用较多,因为可读性比较高,你知道要调用的是谁的方法;而如果拿实体类自己去实体化对象,不知道以什么方式去用的,方法过多


好啦Y(^o^)Y,本节内容到此就结束了。下一篇内容一定会火速更新!!!

后续还会持续更新数据结构与算法方面的内容,还请大家多多关注本up,第一时间获取新鲜的知识。

如果觉得文章不错,别忘了一键三连哟!

相关文章:

【数据结构】栈(Stack)、队列(Queue)、双端队列(Deque) —— 有码有图有真相

目录 栈和队列 1. 栈&#xff08;Stack&#xff09; 1.1 概念 1.2 栈的使用&#xff08;原始方法&#xff09; 1.3 栈的模拟实现 【小结】 2. 栈的应用场景 1、改变元素的序列 2、将递归转化为循环 3、逆波兰表达式求值 4、括号匹配 5、出栈入栈次序匹配 6、最小栈…...

windows清除电脑开机密码,可保留原本的系统和资料,不重装系统

前言 很久的一台电脑没有使用了&#xff0c;开机密码忘了&#xff0c;进不去系统 方法 1.将一个闲置u盘设置成pe盘&#xff08;注意&#xff0c;这个操作会清空原来u盘的数据&#xff0c;需要在配置前将重要数据转移走&#xff0c;数据无价&#xff0c;别因为配置这个丢了重…...

NLP高频面试题(九)——大模型常见的几种解码方案

大模型常见的几种解码方案 在自然语言生成任务中&#xff0c;如何从模型生成的概率分布中选择合适的词汇&#xff0c;是影响文本质量的关键问题。常见的解码方法包括贪心搜索&#xff08;Greedy Search&#xff09;、束搜索&#xff08;Beam Search&#xff09;、随机采样&…...

「低延迟+快速集成:Amazon IVS如何重塑实时互动视频体验?」

引言&#xff1a;实时视频的爆发与开发痛点 随着直播电商、在线教育、云游戏的兴起&#xff0c;实时视频互动成为用户体验的核心。但自建视频服务面临高成本、高延迟、运维复杂等挑战。Amazon IVS&#xff08;Interactive Video Service&#xff09;作为亚马逊云科技推出的全托…...

JVM垃圾回收笔记02-垃圾回收器

文章目录 前言1.串行(Serial 收集器/Serial Old 收集器)Serial 收集器Serial Old 收集器相关参数-XX:UseSerialGC 2.吞吐量优先(Parallel Scavenge 收集器/Parallel Old 收集器)Parallel Scavenge 收集器Parallel Old 收集器相关参数-XX:UseParallelGC ~ -XX:UseParallelOldGC-…...

Agent Team 多智能体系统解析

引言 在人工智能技术高速发展的今天&#xff0c;"多智能体协作系统"&#xff08;Agent Team&#xff09;正成为突破效率瓶颈的关键技术。与传统的单体AI不同&#xff0c;这种由多个专业化智能体组成的协同网络&#xff0c;通过分工协作和动态调整&#xff0c;展现出…...

LintCode第1712题 - 和相同的二元子数组

描述 在由若干 0 和 1 组成的数组 A 中&#xff0c;有多少个和为 S 的非空子数组 样例 1: 输入&#xff1a;A [1,0,1,0,1], S 2 输出&#xff1a;4 解释&#xff1a; 如下面黑体所示&#xff0c;有 4 个满足题目要求的子数组&#xff1a; [1,0,1] [1,0,1] [1,0,1,0] [0,1,…...

网络HTTPS协议

Https HTTPS&#xff08;Hypertext Transfer Protocol Secure&#xff09;是 HTTP 协议的加密版本&#xff0c;它使用 SSL/TLS 协议来加密客户端和服务器之间的通信。具体来说&#xff1a; • 加密通信&#xff1a;在用户请求访问一个 HTTPS 网站时&#xff0c;客户端&#x…...

0322-数据库、前后端

前端 <!DOCTYPE html> <html> <head> <meta charset"UTF-8"> <title>Insert title here</title> <script srcjs/jquery-3.7.1.min.js></script> <script> //jquaryajax发起请求 //传参形式不同 post用data{}…...

六十天前端强化训练之第二十六天之Vue Router 动态路由参数大师级详解

欢迎来到编程星辰海的博客讲解 看完可以给一个免费的三连吗&#xff0c;谢谢大佬&#xff01; 目录 一、知识讲解 1. Vue Router 核心概念 2. 动态路由参数原理 3. 参数传递方案对比 二、核心代码示例 1. 完整路由配置 2. 参数接收组件 3. 导航操作示例 三、实现效果示…...

L2TP实验

一、拓朴图 二、实验配置 1.基础配置 1.1接口IP及服务配置 [PPPoE Client]interface GigabitEthernet 0/0/0 [PPPoE Client-GigabitEthernet0/0/0]service-manage all permit [NAS]interface GigabitEthernet 0/0/0 [NAS-GigabitEthernet0/0/0]ip add 192.168.0.2 24 [NAS-Gi…...

uni-app——数据缓存API

数据缓存API 在 uni-app 开发中&#xff0c;数据缓存 API 起着重要作用&#xff0c;它能够将需要的数据保存到本地&#xff0c;同时也提供了获取本地缓存数据、移除缓存数据以及清理缓存数据的功能。在实际项目里&#xff0c;数据缓存 API 常被用于存储会员登录状态信息、购物…...

不做颠覆者,甘为连接器,在技术叠层中培育智能新物种

--- 一、技术融合的必然&#xff1a;从“非此即彼”到“兼容共生” 当大模型的热浪撞上传统IT的礁石&#xff0c;企业智能化的真相浮出水面&#xff1a; 新旧技术的“量子纠缠”&#xff1a;MySQL与向量数据库共享数据总线&#xff0c;规则引擎与大模型共处决策链路 需求进…...

尝试在软考65天前开始成为软件设计师-计算机网络

OSI/RM 七层模型 层次名功能主要协议7应用层实现具体应用功能 FTP(文件传输)、HTTP、Telnet、 POP3(邮件)SMTP(邮件) ------- DHCP、TFTP(小文件)、 SNMP、 DNS(域名) 6表示层数据格式,加密,压缩.....5会话层建立,管理&终止对话4传输层端到端连接TCP,UDP3网络层分组传输&a…...

JDBC 连接字连接 KingbaseES支持主从负载均衡参数说明。

JDBC 连接字符串是用于连接 KingbaseES&#xff08;人大金仓数据库&#xff09;的&#xff0c;支持主从负载均衡。让我们逐一解析各个参数的作用&#xff0c;并探讨如何调整到最优。 参数解析 jdbc:kingbase8://10.10.14.19:54321/xxx_onlinejdbc:kingbase8://&#xff1a;指定…...

OpenCV旋转估计(4)生成一个字符串表示的匹配图函数 matchesGraphAsString()

操作系统&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 编程语言&#xff1a;C11 算法描述 matchesGraphAsString 函数是OpenCV库中的一部分&#xff0c;位于 cv::detail 命名空间下。这个函数的主要作用是生成一个字符串表示的匹配图&am…...

扣子平台知识库不能上传成功

扣子平台知识库不能上传成功 目录 扣子平台知识库不能上传成功查看模板复制头部到自己的excel中json数据转为excel或者csv&#xff08;一定使用excel&#xff0c;csv总是报错&#xff09; 查看模板复制头部到自己的excel中 json数据转为excel或者csv&#xff08;一定使用excel&…...

CIFAR10 数据集自定义处理方法

CIFAR10 数据集自定义处理方法 可以自定义训练集和测试集中不同类别的样本的数量。可用于模拟类别不平衡问题&#xff0c;存在混淆数据问题。 import torch import torchvision.datasets as dsets import torchvision.transforms as transforms from torch.utils.data import…...

当发现提示少文件,少目录时时,external.css的内容

[ERROR ]17:30:44| Loger: 处理群消息时发生错误&#xff1a;[Errno 2] No such file or directory: \\venv\\lib\\site-packages\\ncatbot\\utils\\template/external.css venv\\lib\\site-packages\\ncatbot\\utils\\template/external.css ["https://stackpath.boots…...

OpenHarmony 开源鸿蒙北向开发——linux使用make交叉编译第三方库

这几天搞鸿蒙&#xff0c;需要编译一些第三方库到鸿蒙系统使用。 头疼死了&#xff0c;搞了一个多星期总算搞定了。 开贴记坑。 一、SDK下载 1.下载 在linux下使用命令 wget https://cidownload.openharmony.cn/version/Master_Version/OpenHarmony_5.1.0.54/20250313_02…...

【计算机网络】网络简介

文章目录 1. 局域网与广域网1.1 局域网1.2 广域网 2. 路由器和交换机3. 五元组3.1 IP和端口3.2 协议3.3 协议分层 4. OSI七层网络协议5. TCP/IP五层模型5.1 TCP/IP模型介绍5.2 网络设备所在分层 6. 封装与分用6.1 数据包的称谓6.2 封装6.3 分用 1. 局域网与广域网 1.1 局域网 …...

k8s--集群内的pod调用集群外的服务

关于如何让同一个局域网内的Kubernetes服务的Pod访问同一局域网中的电脑上的服务。 可能的解决方案包括使用ClusterIP、NodePort、Headless Service、HostNetwork、ExternalIPs&#xff0c;或者直接使用Pod网络。每种方法都有不同的适用场景&#xff0c;需要逐一分析。 例如&…...

高性能边缘计算网关-高算力web组态PLC网关

高性能EG8200Pro边缘计算算力网关-超强处理能力 样机申请测试&#xff1a;免费测试超30天&#xff08;https://www.iotrouter.com/prototype/&#xff09; 产品主要特点和特色功能 设备概览与连接能力 设备型号&#xff1a;EG8200P。主要特点&#xff1a; 支持多种工业协议&am…...

计算机视觉总结

以下是针对上述问题的详细解答,并结合代码示例进行说明: 1. 改进YOLOv5人脸检测模块,复杂光照场景准确率从98.2%提升至99.5% 优化具体过程: 光照补偿:在数据预处理阶段,采用自适应光照补偿算法,对图像进行实时增强,以减少光照变化对人脸检测的影响。数据增强:在训练…...

【Golang】defer与recover的组合使用

在Go语言中&#xff0c;defer和recover是两个关键特性&#xff0c;通常结合使用以处理资源管理和异常恢复。以下是它们的核心应用场景及使用示例&#xff1a; 1. defer 的应用场景 defer用于延迟执行函数调用&#xff0c;确保在函数退出前执行特定操作。主要用途包括&#xff…...

Beyond Compare 4注册激活方法

Beyond Compare 4 注册码 --- BEGIN LICENSE KEY --- H1bJTd2SauPv5Garuaq0Ig43uqq5NJOEw94wxdZTpU-pFB9GmyPk677gJ vC1Ro6sbAvKR4pVwtxdCfuoZDb6hJ5bVQKqlfihJfSYZt-xVrVU270Ja hFbqTmYskatMTgPyjvv99CF2Te8ecYs2SPxyZAF0YwOCNOWmsyqN5y9t q2Kw2pjoiDs5gIH-uw5U49JzOB6otS7kT…...

[C++游戏开发基础]:构造函数浅析,8000+字长文

构造函数 构造函数是一种特殊的成员函数,在创建非聚合类类型对象后会自动被调用。当定义一个非聚合类类型对象时,编译器会检查是否能找到一个可以访问的构造函数,该构造函数与调用者提供的初始化值(如果有的情况下)相匹配。 如果找到一个可访问的匹配构造函数&#xff0c;将为…...

【Go】切片

知识点关键概念切片声明var slice []int初始化切片slice : []int{1,2,3}make() 创建切片make([]int, len, cap)获取长度和容量len(slice), cap(slice)追加元素slice append(slice, value)切片截取slice[start:end]&#xff08;返回子切片&#xff09;拷贝切片copy(dest, src)&…...

MySQL 设置允许远程连接完整指南:安全与效率并重

一、为什么需要远程连接MySQL&#xff1f; 在分布式系统架构中&#xff0c;应用程序与数据库往往部署在不同服务器。例如&#xff1a; Web服务器&#xff08;如NginxPHP&#xff09;需要连接独立的MySQL数据库数据分析师通过BI工具直连生产库多服务器集群间的数据同步 但直接…...

Cursor IDE 入门指南

什么是 Cursor? Cursor 是一款集成了 AI 功能的现代代码编辑器&#xff0c;基于 VSCode 开发&#xff0c;专为提高开发效率而设计。它内置强大的 AI 助手功能&#xff0c;能够理解代码、生成代码、解决问题&#xff0c;帮助开发者更快、更智能地完成编程任务。 基础功能 1.…...

32.[前端开发-JavaScript基础]Day09-元素操作-window滚动-事件处理-事件委托

JavasScript事件处理 1 认识事件处理 认识事件(Event) 常见的事件列表 认识事件流 2 事件冒泡捕获 事件冒泡和事件捕获 事件捕获和冒泡的过程 3 事件对象event 事件对象 event常见的属性和方法 事件处理中的this 4 EventTarget使用 EventTarget类 5 事件委托模式 事件委托&am…...

【工具变量】中国各地级市是否属于“信息惠民国家试点城市”匹配数据(2010-2024年)

数据来源&#xff1a;国家等12部门联合发布的《关于加快实施信息惠民工程有关工作的通知》 数据说明&#xff1a;内含原始文件和匹配结果&#xff0c;当试点城市在2014年及以后&#xff0c;赋值为1&#xff1b;试点城市在2014年之前或该城市从未实施信息惠民试点工程&#x…...

windows安装配置FFmpeg教程

1.先访问官网&#xff1a;https://www.gyan.dev/ffmpeg/builds/ 2.选择安装包Windows builds from gyan.dev 3. 下滑找到release bulids部分&#xff0c;选择ffmpeg-7.0.2-essentials_build.zip 4. 然后解压将bin目录添加path系统变量&#xff1a;\ffmpeg-7.0.2-essentials_bui…...

Wispr Flow,AI语言转文字工具

Wispr Flow是什么 Wispr Flow 是AI语音转文本工具&#xff0c;基于先进的AI技术&#xff0c;帮助用户在任何应用程序中实现快速语音转文字。 Wispr Flow支持100多种语言&#xff0c;具备自动编辑、上下文感知和低音量识别等功能&#xff0c;大幅提升写作和沟通效率。Wispr Fl…...

风暴潮、潮汐潮流模拟:ROMS模型如何精准预测海洋现象?

海洋数值模拟的崛起与 ROMS 的关键角色 &#x1f30a;在海洋科学的浪潮中&#xff0c;海洋数值模拟正以迅猛之势崛起&#xff0c;成为科研与实际应用领域不可或缺的利器。ROMS&#xff08;Regional Ocean Modeling System&#xff09;作为其中的佼佼者&#xff0c;凭借其高效、…...

【Rust】集合的使用——Rust语言基础16

文章目录 1. 前言2. Vector2.1. 构建一个 vector2.2. 获取 vector 中的元素2.3. 遍历 vector2.4. 使用枚举来储存多种类型 3. String3.1. 新建字符串3.2. 更新字符串3.3. 字符串的内部结构3.3.1. 字符串如何访问内部元素&#xff1f;3.3.2. 字节、标量值和字形簇 3.4. 字符串 s…...

Kafka集成Debezium监听postgresql变更

下载postgres的插件&#xff1a;https://debezium.io/documentation/reference/2.7/install.html 2.7版本支持postgresql12数据库。 debezium-connector-postgres-2.7.4.Final-plugin.tar.gz 上传插件并解压 mkdir /usr/local/kafka/kafka_2.12-2.2.1/connector cd /usr/local…...

自动学习和优化过程,实现更加精准的预测和决策的智慧交通开源了

智慧交通视觉监控平台是一款功能强大且简单易用的实时算法视频监控系统。它的愿景是最底层打通各大芯片厂商相互间的壁垒&#xff0c;省去繁琐重复的适配流程&#xff0c;实现芯片、算法、应用的全流程组合&#xff0c;从而大大减少企业级应用约95%的开发成本。通过高效的实时视…...

第2.2节 Android Jacoco插件覆盖率采集

JaCoCo&#xff08;Java Code Coverage&#xff09;是一款开源的代码覆盖率分析工具&#xff0c;适用于Java和Android项目。它通过插桩技术统计测试过程中代码的执行情况&#xff0c;生成可视化报告&#xff0c;帮助开发者评估测试用例的有效性。在github上开源的项目&#xff…...

从零开始:使用 Cython + JNI 在 Android 上运行 Python 算法

1. 引言 在 Android 设备上运行 Python 代码通常面临性能、兼容性和封装等挑战。尤其是当你希望在 Android 应用中使用 Python 编写的计算密集型算法时&#xff0c;直接运行 Python 代码可能导致较高的 CPU 占用和较差的性能。为了解决这个问题&#xff0c;我们可以使用 Cytho…...

开源软件许可证冲突的原因和解决方法

1、什么是开源许可证以及许可证冲突产生的问题 开源软件许可证是一种法律文件&#xff0c;它规定了软件用户、分发者和修改者使用、复制、修改和分发开源软件的权利和义务。开源许可证是由软件的版权所有者&#xff08;通常是开发者或开发团队&#xff09;发布的&#xff0c;它…...

stratis,容器podman

一、stratis 1.stratis可以实现动态的在线扩容&#xff0c;lvm虽然也可以实现在线扩容&#xff0c;但是是需要人为的手动扩容。 2.stratis不需要手动格式化&#xff0c;自动会创建文件系统&#xff08;默认是xfs&#xff09; 1. 安装stratis软件包 yum list | grep stratis…...

解决用three.js展示n个叠加的stl模型文件错位的问题

加载stl时可以明显看到下面有一部分模型是错位的。 将stl文件格式转化为glb 使用免费将 STL 转换为 GLB - ImageToStl 模型就没有错位了 代码如下 <template><div ref"threeContainer" class"three-container"></div></template&…...

从零开始实现 C++ TinyWebServer 数据库连接池 SqlConnectPool详解

文章目录 数据库连接池是什么&#xff1f;Web Server 中为什么需要数据库连接池&#xff1f;SqlConnectPool 成员变量实现 Init() 函数实现 ClosePool() 函数SqlConnectRAII 类SqlConnectPool 代码SqlConnectPool 测试 从零开始实现 C TinyWebServer 项目总览 项目源码 数据库连…...

利用ffmpeg库实现音频AAC编解码

AAC‌&#xff08;Advanced Audio Coding&#xff09;是一种音频编码技术&#xff0c;出现于1997年&#xff0c;基于MPEG-2的音频编码技术。AAC具有高效的数据压缩能力和较高的音质&#xff0c;适用于各种音频应用场景。例如&#xff0c;在智能设备中&#xff0c;AAC技术被广泛…...

Vue + CSS实现渐变栅格进度条

进度条作为可视化大屏系统中展示数据状态的关键元素&#xff0c;其视觉效果直接影响用户的使用体验&#xff0c;而传统的进度条往往呈现出固定的样式&#xff0c;缺乏视觉吸引力。在这种场景下&#xff0c;一种基于Vue和CSS实现渐变栅格进度条的方法应运而生&#xff0c;该方法…...

算法模型从入门到起飞系列——背包问题(探索最大价值的掘金之旅)

文章目录 前言一、背包问题溯源&#xff08;动态规划&#xff09;1.1 动态规划的概念1.2 动态规划的基本步骤1.3 动态规划的实际应用 二、背包问题2.1 背包问题衍生2.2 0-1背包2.2.1 0-1背包描述2.2.2 0-1背包图解2.2.3 0-1背包代码刨析 2.3 完全背包2.3.1 完全背包描述2.3.2 完…...

蓝桥杯—迷宫(bfs)

一.题目 分析:最短路径问题&#xff0c;给定一个迷宫&#xff0c;从左上角走到右下角&#xff0c;要求路径最短&#xff0c;并且要求字典序最小&#xff0c;也就是按照D&#xff0c;L&#xff0c;R&#xff0c;U&#xff0c;的搜索顺序去搜索&#xff0c;否则路径不是唯一的&am…...

【Android】安卓 Java下载ZIP文件并解压(笔记)

写在前面的话 在这篇文章中&#xff0c;我们将详细讲解如何在 Android 中通过 Java 下载 ZIP 文件并解压&#xff0c;同时处理下载进度、错误处理以及优化方案。 以下正文 1.权限配置 在 AndroidManifest.xml 中&#xff0c;我们需要添加相应的权限来确保应用能够访问网络和设…...

清晰易懂的 PHP 安装与配置教程

初学者也能看懂的 PHP 安装与配置教程 本教程将手把手教你如何在 Windows 系统上安装 PHP&#xff0c;并配置 Composer&#xff08;PHP 的依赖管理工具&#xff09;的缓存位置&#xff0c;即使你是零基础小白&#xff0c;也能轻松完成&#xff01; 一、准备工作 操作系统&…...