深入理解栈与队列——从原理理解到实战应用

目录

一、引言

二、栈(Stack)

2.1 栈的基本概念

2.2 栈的使用

2.3 栈的模拟实现

2.4 栈的实战应用

2.4.1 括号匹配

2.4.2 逆波兰表达式求值

2.4.3 出栈入栈次序匹配

2.4.4 最小栈

三、队列(Queue)

3.1 队列的基本概念

3.2 队列的使用

3.3 队列的模拟实现

3.4 队列的实战应用

3.4.1 设计实现循环队列

四、常见面试题与实战应用

4.1 用队列实现栈

4.2 用栈实现队列

五、总结


一、引言

数据结构是编程的基石,无论是算法设计还是系统开发,都离不开对数据结构的深入理解。栈和队列作为两种最基础的线性数据结构,广泛应用于各种场景中。本文将系统性地介绍栈和队列的概念、实现方式以及实际应用,帮助读者建立完整的知识体系。

二、栈(Stack)

2.1 栈的基本概念

栈是一种遵循先进后出FILO,First In Last Out)原则的线性数据结构,只允许在栈顶进行入栈(压栈)和出栈操作。

拿生活中的一些现象举例子:比如超市里盘子售卖区的一摞一摞盘子,我们想要新增盘子只能放在最顶部,同样地,拿出盘子也只能在最顶部拿。

2.2 栈的使用

在Java内置的Stack类中,提供了以下操作方法:

public void push(int data)入栈/压栈,即增加新元素
public int pop()出栈,即删除栈顶的元素并返回该元素
public int peek()获取栈顶元素并返回,不删除
public boolean empty()判断栈是否为空
public int size()获取栈的大小

具体使用示例如下:

import java.util.Stack;public class StackExample {public static void main(String[] args) {Stack<Integer> stack = new Stack<>();// 压栈操作stack.push(1);stack.push(2);stack.push(3);// 查看栈顶元素System.out.println("栈顶元素: " + stack.peek()); // 输出 3// 出栈操作System.out.println("出栈: " + stack.pop()); // 输出 3System.out.println("出栈: " + stack.pop()); // 输出 2// 判断栈是否为空System.out.println("栈是否为空: " + stack.empty()); // 输出 false// 获取栈的大小System.out.println("栈的大小: " + stack.size());   // 输出 1}
}

2.3 栈的模拟实现

在模拟实现栈时,我们可以选用数组也可以选用链表:用数组实现的栈叫顺序栈;用链表实现的栈叫链式栈。

我们先编写一个 MyStack 类如下:

public class MyStack {// 顺序栈public int[] arr;public int usedSize;           //用于记录栈的元素个数public MyStack(int[] arr) {this.arr = new int[10];    //暂定长度为10}// 链式栈(单向)static class ListNode {public int val;public ListNode next;public ListNode(int val) {this.val = val;}}public ListNode head;public int usedSize;           //用于记录栈的元素个数// 链式栈(双向)static class ListNode {public int val;public ListNode prev;public ListNode next;public ListNode(int val) {this.val = val;}}public ListNode head;public ListNode tail;public int usedSize;           //用于记录栈的元素个数
}

接下来我们来实现 push方法,思路很简单,我们只需要在 usedSize 位置增加新元素就可以了,但是要注意栈满了的时候,需要扩容。

顺序栈实现如下:

public void push(int data) {if (empty()) {this.arr = Arrays.copyOf(this.arr,2*this.arr.length);    //扩容为原长度的2倍}arr[usedSize] = data;usedSize++;
}

单向链式栈实现思路:使用头插法入栈,时间复杂度是O(1)

public void push(int data) {// 头插法入栈ListNode toAdd = new ListNode(data);toAdd.next = head;head = toAdd;usedSize++;
}

双向链式栈实现方式可以头插也可以尾插。

public void push(int data) {// 头插法ListNode toAdd = new ListNode(data);toAdd.next = head;head.prev = toAdd;head = head.prev;usedSize++;
}public void push(int data) {// 尾插法ListNode toAdd = new ListNode(data);tail.next = toAdd;toAdd.prev = tail;tail = tail.next;usedSize++;
}

然后是 pop方法,我们只需要将 usedSize 的个数减一即可。虽然元素在数组中仍然存在,但是不需要担心,等到下次增加元素时,该元素将被覆盖。

顺序栈实现如下:

public int pop() {if (empty()) {throw new EmptyStackException("This stack is empty!");}usedSize--;return this.arr[usedSize];
}

单向链式栈:我们可以使用头删法,每次删除并返回链表的第一个节点

public int pop() {if (empty()) {throw new EmptyStackException("This stack is empty!");}int val = head.val;head = head.next;usedSize--;return val;
}

双向链式栈:与单向链式栈不同的是,我们需要把第二个节点的 prev 置空

public int pop() {if (empty()) {throw new EmptyStackException("This stack is empty!");}int val = head.val;head = head.next;head.prev = null;usedSize--;return val;
}

其中的异常编写如下:

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

peek方法十分简单,只需要返回 usedSize - 1 位置的元素即可,但是注意要判断栈是否为空。

顺序栈实现如下:

public int peek() {if (empty()) {throw new EmptyStackException("This stack is empty!");}return this.arr[usedSize-1];
}

单/双向链式栈:直接返回链表第一个节点的 val 即可

public int peek() {if (empty()) {throw new EmptyStackException("This stack is empty!");}return head.val;
}

empty方法,只需判断 usedSize 是否等于数组的长度即可。

顺序栈实现如下:

public boolean empty() {return usedSize == this.arr.length;
}

单/双向链式栈:判断指向链表第一个节点的引用是否为空即可

public boolean empty() {if (head == null) return true;return false;
}

最简单的是 size方法,只需要返回 usedSize 即可:

public int size() {return this.usedSize;
}

对于单/双链式栈来说,需要遍历整个栈:

public int size() {ListNode cur = head;int count = 0;while (cur != null) {count++;cur = cur.next;}return count;
}

2.4 栈的实战应用

栈相关的面试题:

2.4.1 括号匹配

算法实现思路:

  1. 遍历字符串,如果是左括号,就放入栈中;如果是右括号,就获取栈顶元素并进行匹配
  2. 情况一:当栈为空并且字符串遍历完成,左右括号都匹配成功时,返回true;情况二:当左右括号匹配失败时,返回false;情况三:当字符串遍历完成但是栈不为空时,表示左括号多余,返回false;情况四:当字符串未遍历完成但是栈已经空了,表示右括号多余,返回false。
  3. 注意:当遍历字符串发现字符是右括号时,在匹配前必须先检查栈是否为空:若不为空才进行匹配;若为空则是第四种情况

具体实现如下:

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()) {       // 栈已空,右括号多余return false;}char leftCh = stack.peek();if ((leftCh=='(' && ch==')')|| (leftCh=='[' && ch==']')|| (leftCh=='{' && ch=='}')) {     // 匹配成功stack.pop();} else {               // 匹配失败return false;}}}if (!stack.empty()) {       // 栈不为空但字符串已经遍历完成,左括号多余return false;}return true;
}

2.4.2 逆波兰表达式求值

算法实现思路:

  1. 遍历,遇到数字就放入栈,遇到运算符就取出两个栈顶数字并进行计算(第一个取出的数字作为右操作数,第二个作为左操作数),将计算结果放入栈
  2. 当遍历完成后,栈中所存的元素就是该表达式的最终结果

中缀表达式转后缀表达式(逆波兰表达式)的计算方法:

  1. 先判断中缀表达式的优先级,然后按照优先级依次在算式外部添加括号
  2. 全部添加完成后,从左至右依次将运算符移至括号后面
  3. 例:中缀表达式 a + b * c + ( d * e + f ) * g 
  4. 首先划分优先级:
  5. 单项式( d * e ) ——> 多项式( ( d * e ) + f ) ——> 多项式( ( d * e ) + f ) * g ) ——> 单项式( b * c ) ——> 多项式( a + ( b * c ) ) ——> 多项式( ( a + ( b * c ) ) + ( ( d * e ) + f ) * g ) )
  6. 然后按照优先级先后把运算符全部移到括号后面:
  7. ( ( a + ( b * c ) ) + ( ( ( d * e ) + f ) * g ) ) ——> ( ( a ( b c ) * ) + ( ( ( d e ) * f ) + g ) * ) +
  8. 去掉所有的括号最终结果就是:
  9.  a b c * + d e * f + g * +

具体实现如下:

public int evalRPN(String[] tokens) {Stack<Integer> stack = new Stack<>();for (String s : tokens) {if (s.equals("+")|| s.equals("-")|| s.equals("*")|| s.equals("/")) {    // 是运算符int v2 = stack.pop();   // 右操作符int v1 = stack.pop();   // 左操作符switch (s) {case "+":stack.push(v1+v2);break;case "-":stack.push(v1-v2);break;case "*":stack.push(v1*v2);break;case "/":stack.push(v1/v2);break;}} else {            // 是数字int val = Integer.parseInt(s);  // 转换为整型stack.push(val);}}return stack.pop();
}

2.4.3 出栈入栈次序匹配

算法实现思路:

  1. 遍历pushV数组并入栈,每次入栈一个元素之后拿栈顶元素与popV数组中的 j 位置的元素进行比较是否相等:若一样,就可以出栈;否则,继续遍历pushV数组、入栈
  2. 一旦有一个元素相等,后面的所有元素均相等
  3. 当栈为空或者popV数组和pushV数组遍历完成时,返回true

具体实现如下:

public boolean isPopOrder (int[] pushV, int[] popV) {Stack<Integer> stack = new Stack<>();int j = 0;for (int i = 0; i < pushV.length; i++) {stack.push(pushV[i]);while (j < popV.length&& !stack.isEmpty()&& stack.peek() == popV[j]) {stack.pop();j++;}}return stack.isEmpty();
}

2.4.4 最小栈

算法实现思路:

  1. 使用两个栈,一个普通栈(stack)存放所有的数据,一个最小栈(minStack)用于存放当前 stack 所存元素的最小值
  2. 入栈:当第一个元素放入 stack ,且 minStack 为空时,minStack 也要放。接下来 stack 存放元素后,都要与 minStack 的栈顶元素进行大小比较:若比栈顶元素小,就放入 minStack(注意:若与栈顶元素相等,也要放入);否则不放入
  3. 出栈:当普通栈出一个元素时,判断一下最小栈是否也有相同的元素:若有就出;否则不出

具体实现如下:

public class MinStack {Stack<Integer> stack;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 (minStack.peek() == val) {minStack.pop();}}public int top() {if (stack.empty()) return -1;return stack.peek();}public int getMin() {if (stack.empty()) return -1;return minStack.peek();}
}

三、队列(Queue)

3.1 队列的基本概念

队列是一种遵循先进先出FIFO, First In First Out)原则的线性数据结构。只允许在队尾进行插入操作,在队头进行删除操作。

现实生活中的队列例子:

  • 排队购票:先来的人先得到服务
  • 打印机任务队列:先提交的打印任务先执行

3.2 队列的使用

在Java内置的Queue类中,提供了以下操作方法:

public void offer(int data)入队,即增加新元素
public int poll()出队,即删除队头元素并返回该元素
public int peek()获取队头元素并返回,不删除
public boolean isEmpty()判断队列是否为空
public int size()获取队列的大小

具体使用示例如下:

import java.util.LinkedList;
import java.util.Queue;public class QueueExample {public static void main(String[] args) {Queue<Integer> queue = new LinkedList<>();// 入队操作queue.offer(1);queue.offer(2);queue.offer(3);// 查看队头元素System.out.println("队头元素: " + queue.peek()); // 输出 1// 出队操作System.out.println("出队: " + queue.poll()); // 输出 1System.out.println("出队: " + queue.poll()); // 输出 2// 判断队列是否为空System.out.println("队列是否为空: " + queue.isEmpty()); // 输出 false// 获取队列的元素个数System.out.println("队列的长度: " + queue.size()); // 输出 1}
}

3.3 队列的模拟实现

由于Java的内置队列 Queue 是采用双向链表实现的,我们这里也使用双向链表。各个方法的实现思路与栈的方法的实现思路差不多,此处不再过多阐述。

具体实现如下:

public class MyQueue {static class ListNode {public int val;public ListNode prev;public ListNode next;public ListNode(int val) {this.val = val;}}public ListNode first;public ListNode last;public int usedSize;public void offer(int data) {   // 尾插ListNode toAdd = new ListNode(data);if(first == null) {first = last = toAdd;return;}last.next = toAdd;toAdd.prev = last;last = last.next;usedSize++;}public int poll() {    // 头删if(first == null) {return -1;}int v = first.val;first = first.next;if (first != null) {first.prev = null;}usedSize--;return v;}public int peek() {     // 获取队头的值if(first == null) {return -1;}return first.val;}public int size() {    // 获取队列的大小return usedSize;}
}

3.4 队列的实战应用

3.4.1 设计实现循环队列

我们把一个数组卷起来呈圆形,以这样的结构来实现的队列称为循环队列。

当两个引用 front  和 rear 指向同一个位置(即 front == rear)时,该队列为空。

它的方法如下:

public boolean enQueue(int value)入队,即增加新元素
public boolean deQueue()出队,即删除队头元素并返回该元素
public int Front()获取队头元素并返回
public int Rear()获取队尾元素并返回
public boolean isEmpty()判断队列是否为空
public boolean isFull()判断队列空间是否已满

算法实现思路:

  1. 如何判断队列为空/队列已满?:当 front == rear
  2. 怎么区分为空还是为满?:1.定义 usedSize;2.空出最后一个位置;3.添加一个布尔类型的变量作为标记
  3. 怎么做到循环遍历队列?即怎么让 rear 从下标为7的位置走到下标为0的位置呢?:用公式( rear + 1 ) % array.length判断 rear 的下一个是否 front
  4. 入队列、出队列、返回队头元素、返回队尾元素(需要判断 rear)

具体实现如下:

class MyCircularQueue {    // 这里采用的是第二种方法区分为空还是为满public int front;public int rear;public int[] elem;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 pos = (rear==0) ? elem.length-1 : rear-1;return elem[pos];}public boolean isEmpty() {return rear == front;}public boolean isFull() {return (rear+1)%elem.length == front;}
}

四、常见面试题与实战应用

4.1 用队列实现栈

算法实现思路(使用两个队列):

  1. 模拟的入栈操作:将入栈的元素放入不为空的队列中;
  2. 第一次入栈默认放入qu1中,接下来判断哪个队列不为空就放入哪个队列中:if...else if...else
  3. 模拟的出栈操作:把不为空的队列中的除要出栈的元素之外的(size-1)所有元素放入另一个队列当中,再出队列即可;先判断栈是否为空,再判断哪个队列不为空就出哪个队列
  4. 模拟的获取栈顶元素操作:在出栈操作的基础下(把全部元素都出),加一个变量存储出队列的元素,当最后一个出队列的元素存储后,该值就是模拟的栈顶元素
  5. 两个队列交替工作

具体实现如下:

public class MyStack {Queue<Integer> qu1;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 size = qu1.size();for (int i = 0; i < size-1; i++) {qu2.offer(qu1.poll());}return qu1.poll();} else {int size = qu2.size();for (int i = 0; i < size-1; i++) {qu1.offer(qu2.poll());}return qu2.poll();}}public int top() {if (empty()) {return -1;}if (!qu1.isEmpty()) {int size = qu1.size();int val = 0;for (int i = 0; i < size; i++) {val = qu1.poll();qu2.offer(val);}return val;} else {int size = qu2.size();int val = 0;for (int i = 0; i < size; i++) {val = qu2.poll();qu1.offer(val);}return val;}}public boolean empty() {return qu1.isEmpty() && qu2.isEmpty();}
}

4.2 用栈实现队列

算法实现思路(使用两个栈):

  1. 指定一个栈用来入队列,一个栈用来出队列
  2. 模拟的入队列操作:放入指定的入队列栈中即可
  3. 模拟的出队列操作:先判断出队列栈是否为空?:若为空,将入队列栈中的元素放入出队列栈中,再出栈即可;否则直接出栈
  4. 模拟的获取队头元素操作:在出队列操作下,直接peek出队列栈的栈顶元素即可

具体实现如下:

public class MyQueue {Stack<Integer> st1;Stack<Integer> st2;public MyQueue() {st1 = new Stack<>();st2 = new Stack<>();}public void push(int x) {st1.push(x);}public int pop() {if (empty()) {return -1;}if (st2.isEmpty()) {while (!st1.isEmpty()) {st2.push(st1.pop());}}return st2.pop();}public int peek() {if (empty()) {return -1;}if (st2.isEmpty()) {while (!st1.isEmpty()) {st2.push(st1.pop());}}return st2.peek();}public boolean empty() {return st1.isEmpty() && st2.isEmpty();}
}

五、总结

栈和队列是编程中最基础且重要的数据结构,理解它们的特性和应用场景对每个程序员都至关重要:

  1. 遵循FILO原则,适合用于需要"回溯"的场景,如函数调用、括号匹配、深度优先搜索等

  2. 队列遵循FIFO原则,适合用于需要"排队"的场景,如广度优先搜索、消息队列、任务调度等

  3. 双端队列结合了栈和队列的特性,提供了更灵活的操作方式

  4. 在实际开发中,应根据具体需求选择合适的数据结构,或者组合使用多种数据结构解决问题

掌握这些基础数据结构不仅有助于解决算法问题,也能为理解更复杂的系统设计打下坚实基础。

相信读者朋友看到这里已经能够掌握栈和队列的基本操作了 ~


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/pingmian/97707.shtml
繁体地址,请注明出处:http://hk.pswp.cn/pingmian/97707.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

用html5写王者荣耀之王者坟墓的游戏2deepseek版

我将为您创建一个王者荣耀英雄坟墓游戏的提词器HTML页面。这个工具将帮助游戏主播或玩家在游戏中快速查看英雄技能、连招顺序等信息。设计思路 创建英雄选择界面实现提词器显示区域&#xff0c;可自定义文本内容添加字体大小、滚动速度控制设计符合王者荣耀风格的UI下面是…

轻阅读:一键解决浏览器无法预览Office文档的实用方案

在日常办公中&#xff0c;通过浏览器直接打开Word、Excel或PPT等文档时&#xff0c;常遇到“需下载后用本地软件打开”的困扰&#xff0c;不仅流程繁琐&#xff0c;还面临格式兼容、设备存储不足等问题。轻阅读&#xff08;QingYueDu&#xff09;作为一款轻量级文件在线预览工具…

鸿蒙开发实战项目(六十七):常见组件和容器低代码开发示例(ArkTS)

本文详细代码需订阅下面专栏获取(订阅后私信邮箱+项目名): https://blog.csdn.net/m0_68036862/category_12333038.html 目录 介绍 环境搭建 代码结构解读 创建低代码工程 低代码设计界面布局 实现数据动态渲染 手动创建低代码页面 介绍 本篇Codelab是基于ArkTS语言的…

MySQL学习笔记04-DML-数据的增删改

新增数据--insert样例代码-- DML : 数据操作语言 -- DML : 插入数据 - insert -- 1. 为 emp 表的 username, password, name, gender, phone 字段插入值 insert into emp (username,password,name,gender,phone) values(fei,123456,张飞,1,13888888888);-- 2. 为 emp 表的 所有…

拼多多返利app的服务网格(Service Mesh)实践:Istio在导购系统中的应用

拼多多返利app的服务网格&#xff08;Service Mesh&#xff09;实践&#xff1a;Istio在导购系统中的应用 大家好&#xff0c;我是阿可&#xff0c;微赚淘客系统及省赚客APP创始人&#xff0c;是个冬天不穿秋裤&#xff0c;天冷也要风度的程序猿&#xff01; 在拼多多返利app的…

【RabbitMQ】高级特性:持久性·发送方确认·重试机制·TTL·死信队列·延迟队列·事务·消息分发

RabbitMQ的高级特性还包括我的上篇博客 【RabbitMQ】-----详解RabbitMQ高级特性之消息确认机制-CSDN博客 目录 RabbitMQ高级特性之持久性 持久性 交换机持久化 队列持久化消息持久化 RabbitMQ高级特性之发送方确认机制 发送方确认 添加配置 常量类 声明队列和交换机…

鸿蒙Next ArkWeb网页多媒体开发实战:从基础到高级应用

解锁鸿蒙ArkWeb的强大多媒体能力&#xff0c;让网页视频音频体验媲美原生应用在日常应用开发中&#xff0c;我们经常需要在应用中嵌入网页并展示其中的多媒体内容。鸿蒙HarmonyOS Next的ArkWeb组件提供了强大的网页渲染能力&#xff0c;尤其对网页中的多媒体元素有出色的支持。…

06. Linux进程概念 1

Linux进程概念 冯诺依曼体系 冯诺依曼体系结构&#xff08;Von Neumann Architecture&#xff09;是现代计算机设计的奠基石&#xff0c;由数学家约翰冯诺依曼于1945年提出。这一架构彻底改变了早期计算机“硬件即程序”的设计方式&#xff0c;使得计算机可以灵活地运行不同程序…

HTTP标头全解析:保护你的Web应用!

在网络攻击频发的时代&#xff0c;你的Web应用是否像一座没有城墙的城堡&#xff0c;任由XSS、点击劫持和中间人攻击入侵&#xff1f;HTTP标头&#xff0c;这些看似不起眼的响应头&#xff0c;其实是Web安全的隐形守护者。想象一个电商网站&#xff0c;用户数据被窃取&#xff…

rt-linux下__slab_alloc里的另外一处可能睡眠的逻辑

一、背景 在之前的博客 tasklet上下文内存分配触发might_alloc检查及同步回收调用链 里&#xff0c;我们讲了一处内存分配时会引起睡眠的调用链&#xff08;这个引起睡眠的这个调用链它是在普通linux里也是存在的&#xff09;。这篇博客里&#xff0c;我们讲一处内存分配路径下…

基于STM32F103C8T6的智能环境监测系统:DHT11温湿度检测与OLED显示实现

引言 你是否曾想实时握身边环境的温湿度变化&#xff1f;无论是居家种植需要精准调控环境&#xff0c;还是实验室存放敏感材料需监控条件&#xff0c;亦或是智能座舱场景下的环境感知&#xff0c;智能环境监测系统正成为连接物理世界与数字管理的重要桥梁。而在众多嵌入式开发…

动态规划在子数组/子串问题

目录 一、最大子数组和&#xff08;LeetCode 53&#xff09; 二、环形子数组的最大和&#xff08;LeetCode 918&#xff09; 三、乘积最大子数组&#xff08;LeetCode 152&#xff09; 四、乘积为正数的最长子数组长度&#xff08;LeetCode 1567&#xff09; 五、等差数列…

微信小程序开发笔记(01_小程序基础与配置文件)

ZZHow(ZZHow1024) 参考课程: 【尚硅谷微信小程序开发教程】 [https://www.bilibili.com/video/BV1LF4m1E7kB] 009_文件和目录结构介绍新建页面与调试基础库 一个完整的小程序项目分为两个部分&#xff1a;主体文件、页面文件 主体文件又称全局文件&#xff0c;能够作用于整…

NLP Subword 之 BPE(Byte Pair Encoding) 算法原理

本文将介绍以下内容&#xff1a; 1. BPE 算法核心原理2. BPE 算法流程3. BPE 算法源码实现DemoBPE最早是一种数据压缩算法&#xff0c;由Sennrich等人于2015年引入到NLP领域并很快得到推广。该算法简单有效&#xff0c;因而目前它是最流行的方法。GPT-2和RoBERTa使用的Subword算…

CSS 伪类选择器

伪类选择器&#xff08;pseudo-class selector&#xff09;是一种用于选择HTML元素特定状态或特征的关键字&#xff0c;它允许开发者基于文档树之外的信息&#xff08;如用户交互、元素位置或状态变化&#xff09;来选择元素并应用样式。伪类选择器以冒号(:)开头&#xff0c;附…

Electron 新特性:2025 版本更新解读

引言&#xff1a;Electron 新特性在 2025 版本更新中的解读核心价值与必要性 在 Electron 框架的持续演进中&#xff0c;新特性的引入是推动桌面开发创新的核心动力&#xff0c;特别是 2025 年的版本更新&#xff0c;更是 Electron 项目从成熟生态到前沿技术的跃进之钥。它不仅…

MyBatis从入门到面试:掌握持久层框架的精髓

MyBatis从入门到面试&#xff1a;掌握持久层框架的精髓 前言 在Java企业级应用开发中&#xff0c;持久层框架的选择至关重要。MyBatis作为一款优秀的半自动化ORM框架&#xff0c;以其灵活的SQL定制能力和良好的性能表现&#xff0c;成为了众多开发者的首选。本文将带你从MyBa…

5.Three.js 学习(基础+实践)

Three.js 是 “WebGL 的封装库”&#xff0c;帮你屏蔽了底层的着色器 / 缓冲区细节&#xff0c;专注于 “3D 场景搭建”&#xff0c;开发效率高&#xff0c;是通用 3D 开发的首选。他的核心是 “场景 - 相机 - 渲染器” 的联动逻辑&#xff0c;先掌握基础组件&#xff0c;再学进…

消火栓设备工程量计算 -【图形识别】秒计量

消火栓设备工程量计算 -【图形识别】秒计量 消防系统的消火栓设备水枪、水带和消火栓组成&#xff0c;根据清单定额规则计算消火栓设备工程量。通过CAD快速看图的图形识别框选图纸就能自动数出消火栓数量&#xff0c;省时又准确&#xff0c;是工程人做消防算量的好帮手。 一、…

Docker 与 VSCode 远程容器连接问题深度排查与解决指南

Docker 与 VSCode 远程容器连接问题深度排查与解决指南 引言 Visual Studio Code 的 Remote - Containers 扩展极大地提升了开发体验&#xff0c;它将开发环境容器化&#xff0c;保证了环境的一致性&#xff0c;并允许开发者像在本地一样在容器内进行编码、调试和运行。然而&…