数据结构之探索“队列”的奥秘

找往期文章包括但不限于本期文章中不懂的知识点:

个人主页:我要学编程(ಥ_ಥ)-CSDN博客

所属专栏:数据结构(Java版)

目录

队列有关概念

队列的使用 

队列模拟实现  

循环队列的模拟实现

 622. 设计循环队列 

双端队列

栈与队列相互转换

232. 用栈实现队列

225. 用队列实现栈 


队列有关概念

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

入队列:进行插入操作的一端称为队尾(Tail/Rear) ;

出队列:进行删除操作的一端称为队头 (Head/Front)。

队列和我们在日常生活中买东西排队或者在食堂打饭的场景是一样的。如下图:

队列的使用 

上面这张图中,Queue 便是队列,可以看到其底层是 LinkedList ,也就是双向链表实现的。

队列的常用方法
方法功能
boolean offer(E e)往队尾添加元素
E poll()获取并删除队头元素
peek()获取队头元素
int size()获取队列中有效元素个数
boolean isEmpty()检测队列是否为空
public class Test {
    public static void main(String[] args) {
        Queue<Integer> queue = new LinkedList<>();
        // 判断队列是否为空
        System.out.println(queue.isEmpty());
        // 往队尾插入元素
        queue.offer(1);
        queue.offer(2);
        queue.offer(3);
        queue.offer(4);
        queue.offer(5);
        // 获得并删除对头元素
        System.out.println(queue.poll());
        // 获取对头元素
        System.out.println(queue.peek());
        // 判断队列是否为空
        System.out.println(queue.isEmpty());
        int x = queue.size();
        System.out.println(x);
        // 循环遍历队列中的元素并删除
        for (int i = 0; i < x; i++) {
            System.out.print(queue.poll()+" ");
        }
        System.out.println();
        // 注意这个poll()方法并不会因为队列为空而抛出空指针异常
        System.out.println(queue.poll());
    }
}

因为上面代码中队列是用 LinkedList 实现的,那么我们就应该去其源码下,查看 poll() 方法

队列模拟实现  

// 用链表模拟实现队列
public class MyQueue {
    static class ListNode {
        int val;
        ListNode prev;
        ListNode next;

        public ListNode(int val) {
            this.val = val;
        }
    }

    public ListNode head;
    public ListNode last;
    public int size; // 减少时间复杂度去遍历链表

    // 往队尾添加元素
    public boolean offer(int val) {
        // 创建一个新的节点
        ListNode newNode = new ListNode(val);
        // 队列为空
        if (head == null) {
            head = newNode;
            last = newNode;
        } else {
            // 就是在链表中尾插元素
            last.next = newNode;
            newNode.prev = last;
            last = last.next;
        }
        size++;
        return true;
    }

    // 删除并获取对头元素
    public Integer poll() {
        // 链表为空就返回null
        if (head == null) {
            return null;
        } else {
            int x = head.val;
            size--;
            // 链表中只有一个元素就直接删除
            if (head.next == null) {
                head = null;
                last = null;
                return x;
            } else {
                // 链表中有多个节点
                head = head.next;
                return x;
            }
        }
    }

    // 获取对头元素
    public Integer peek() {
        // 模范源码的写法
        return head == null ? null : head.val;
    }

    // 获取节点个数
    public int size() {
        return size;
    }

    // 检测链表是否为空
    public boolean isEmpty() {
        return head == null;
    }
}

上面是用链表模拟实现,队列用链表实现时,就可以把队列看成是一个链表,我们就可以用实现链表的方式来实现队列。又因为链表是用节点组成,因此,我们就要创建节点,来操作链表。

接下来,我们就可以尝试用数组来实现队列。

上面这个数组看起来虽然可以实现队列,但是我们如果去写队列的方法时,根本无法实现。因为删除对头元素时,last到底要不要 -1 呢?如果 -1,那么last 就会指向5位置的下标,不符合要求;如果不 -1,那么对头元素该怎么办呢?有小伙伴可能会说,直接把元素从后往前覆盖,然后再把 last -1,不就完事了吗? 但又有一个问题:移动数组元素所付出的时间是非常大的,因为队列中出队的元素永远是数组的首元素,在移动数组时,全部的元素都得移动,这就会浪费很多的时间。因此上面这种方法是不可行的,就得用一种全新的数组来解决:循环数组。用循环数组实现的队列也叫作循环队列。

循环队列的模拟实现

循环队列有几个要解决的难题:

1、怎么判断这个队列是空还是有元素?

可能有小伙伴会说:看head 与 last 两者的下标是否相等,如果相等,就说明队列为空;如果不相等就说明队列不为空。但是很巧不巧:当队列满了的时候,这个head 与 last 指向的位置还是一样的啊。有三种解决方案:1、使用usedSize 来记录元素的个数。当head 与 last 相遇时,看看usedSize 是否为0,不为0,就说明的确是满了;否则,就没满。2、浪费一个空间来阻止其相遇。既然当两者相遇时,不知道到底是空还是满,那我们就阻止其相遇即可,当last 的下一个位置是head时,就说明队列已经满了,则不往这个位置存放元素。3、使用标记的方式。就是通过维护一个额外的变量,来判断这个队列是否满了。例如:定义一个isEmpty的变量,初始化为true,只要执行了入队操作,就把其变为false,如果在之后的入队操作中,遇到head == last ,并且isEmpty 为false,那么就说明满了。其实这个就是排除了刚开始 head == last的情况。

2、当这个last 或者是 head 为7 时,怎么把其变为 0呢?也就是说怎么把这个下标给正常化?

大佬们给出的方法是 last = (last+1) % Queue.size();   

极端情况:0 = (7+1) % 8           正常情况:1 = (0+1) % 8 、  6 = (5+1) % 8

两种难题都已经解决了,就可以开始着手写循环队列了。

 622. 设计循环队列 

设计你的循环队列实现。 循环队列是一种线性数据结构,其操作表现基于 FIFO(先进先出)原则并且队尾被连接在队首之后以形成一个循环。它也被称为“环形缓冲器”。

循环队列的一个好处是我们可以利用这个队列之前用过的空间。在一个普通队列里,一旦一个队列满了,我们就不能插入下一个元素,即使在队列前面仍有空间。但是使用循环队列,我们能使用这些空间去存储新的值。

你的实现应该支持如下操作:

  • MyCircularQueue(k): 构造器,设置队列长度为 k 。
  • Front: 从队首获取元素。如果队列为空,返回 -1 。
  • Rear: 获取队尾元素。如果队列为空,返回 -1 。
  • enQueue(value): 向循环队列插入一个元素。如果成功插入则返回真。
  • deQueue(): 从循环队列中删除一个元素。如果成功删除则返回真。
  • isEmpty(): 检查循环队列是否为空。
  • isFull(): 检查循环队列是否已满。

示例:

MyCircularQueue circularQueue = new MyCircularQueue(3); // 设置长度为 3
circularQueue.enQueue(1);  // 返回 true
circularQueue.enQueue(2);  // 返回 true
circularQueue.enQueue(3);  // 返回 true
circularQueue.enQueue(4);  // 返回 false,队列已满
circularQueue.Rear();  // 返回 3
circularQueue.isFull();  // 返回 true
circularQueue.deQueue();  // 返回 true
circularQueue.enQueue(4);  // 返回 true
circularQueue.Rear();  // 返回 4

提示:

  • 所有的值都在 0 至 1000 的范围内;
  • 操作数将在 1 至 1000 的范围内;
  • 请不要使用内置的队列库。

思路:其实把上面的难题解决之后,循环队列也就容易实现了。

class MyCircularQueue {
    // 用数组来实现循环队列
    public int[] elem; // 循环数组
    public int usedSize; // 记录数组的有效元素的个数
    public int head; // 记录对头的位置
    public int last; // 记录队尾的位置
    public int lastValue; // 获取队尾的值

    public MyCircularQueue(int k) {
        this.elem = new int[k];
    }
    
    public boolean enQueue(int value) {
        // 如果满了,就插入失败
        if (isFull()) {
            return false;
        }
        // 往数组中插入元素
        elem[last] = value;
        lastValue  = elem[last];
        last = (last+1) % elem.length;
        // 注意:不能简单的++了
        usedSize++;
        return true;
    }
    
    public boolean deQueue() {
        if (isEmpty()) {
            return false;
        }
        // 删除对头元素
        // head 往后走就行,其余不用管
        head = (head+1) % elem.length;
        usedSize--;
        return true;
    }
    
    public int Front() {
        if (isEmpty()) {
            return -1;
        } else {
            return elem[head];
        }
    }
    
    public int Rear() {
        if (isEmpty()) {
            return -1;
        } else {
            return lastValue;
        }
    }
    
    public boolean isEmpty() {
        return usedSize == 0;
    }
    
    public boolean isFull() {
        return usedSize == elem.length;
    }
}

注意:因为这里 last 的位置会在插入元素之后发生改变, 因此,我们得把改变前的位置存起来或者把改变前的队尾值存起来,以便后面的 Rear() 方法。

这里我用的是usedSize 记录位置看是否满,也可以用其他的方法。下面是用浪费一个空间的方法:(为了更好的观察,就只给出了改动代码)

class MyCircularQueue {

    public MyCircularQueue(int k) {
        // 既然浪费了一个空间,那么我们就偷偷地多申请一个空间
        this.elem = new int[k+1];
    }
    
    
    public boolean isEmpty() {
        // 两者只有在队列为空时,才能相遇
        return head == last;
    }
    
    public boolean isFull() {
        // 如果 last+1 == head,说明此时队列已经满了
        return (last+1) % elem.length == head;
    }
}

双端队列

前面我们学习的队列都是队尾进,对头出。现在我们来学习一种全新的队列:双端队列。

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

从上图也可以看出Deque 拓展了 Queue 的功能,并且 ArrayList 与 LinkedList 都是实现了该接口的,也就说明既有 线性的双端队列,也有链式的双端队列。

其他的方法与我们在上面的方法差不多,都是这样的,这里就不过多的赘述了。

上面是ArrayDeque 的offer() 方法。

注意:Java 8 和 Java 17 在针对ArrayDeque的无参构造方法上设计的有些不一样。

上面这个就是在浪费一个空间作为判断循环队列是否已满的情况。和我们前面的处理是一样。 

由于双端队列可以在一端进,一端出,这也就表明其可以作为栈来使用了。

栈与队列相互转换

232. 用栈实现队列

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

实现 MyQueue 类:

  • void push(int x) 将元素 x 推到队列的末尾
  • int pop() 从队列的开头移除并返回元素
  • int peek() 返回队列开头的元素
  • boolean empty() 如果队列为空,返回 true ;否则,返回 false

说明:

  • 你 只能 使用标准的栈操作 —— 也就是只有 push to toppeek/pop from topsize, 和 is empty 操作是合法的。
  • 你所使用的语言也许不支持栈。你可以使用 list 或者 deque(双端队列)来模拟一个栈,只要是标准的栈操作即可。

示例 1:

输入:
["MyQueue", "push", "push", "peek", "pop", "empty"]
[[], [1], [2], [], [], []]
输出:
[null, null, null, 1, 1, false]

解释:
MyQueue myQueue = new MyQueue();
myQueue.push(1); // queue is: [1]
myQueue.push(2); // queue is: [1, 2] (leftmost is front of the queue)
myQueue.peek(); // return 1
myQueue.pop(); // return 1, queue is [2]
myQueue.empty(); // return false

提示:

  • 1 <= x <= 9
  • 最多调用 100 次 pushpoppeek 和 empty
  • 假设所有操作都是有效的 (例如,一个空的队列不会调用 pop 或者 peek 操作)

思路:先取一个栈让其作为第一次存放元素的栈, 如果进行peek 或者 pop 操作,就把有元素的栈中所有元素出栈到另一个空栈中即可,empty 就是看两个栈是否都为空。

class MyQueue {
    public Stack<Integer> stack1;
    public Stack<Integer> stack2;

    public MyQueue() {
        stack1 = new Stack<>();
        stack2 = new Stack<>();
    }
    
    public void push(int x) {
        // 如果两个栈都为空,随便选取一个作为存放元素的栈
        if (empty()) {
            stack1.push(x);
        } else if (!stack1.empty()) {
            // 如果栈1不为空,存放到栈1中,反之则存放到栈2中
            stack1.push(x);
        } else {
            stack2.push(x);
        }
    }
    
    public int pop() {
        // 把有元素的栈中所有元素存放到另一个栈中
        if (!stack1.empty()) {
            int size = stack1.size();
            for (int i = 0; i < size; i++) {
                stack2.push(stack1.pop());
            }
            // 再颠倒过来
            int x = stack2.pop();
            size = stack2.size();
            for (int i = 0; i < size; i++) {
                stack1.push(stack2.pop());
            }
            return x;
        } else {
            int size = stack2.size();
            for (int i = 0; i < size; i++) {
                stack1.push(stack2.pop());
            }
            // 再颠倒过来
            int x = stack2.pop();
            size = stack2.size();
            for (int i = 0; i < size; i++) {
                stack1.push(stack2.pop());
            }
            return x;
        }
    }
    
    public int peek() {
        // 把有元素的栈中所有元素存放到另一个栈中
        if (!stack1.empty()) {
            int size = stack1.size();
            for (int i = 0; i < size; i++) {
                stack2.push(stack1.pop());
            }
            // 再颠倒过来
            // 这个peek()方法的顺序没有关系
            int x = stack2.peek();
            size = stack2.size();
            for (int i = 0; i < size; i++) {
                stack1.push(stack2.pop());
            }
            return x;
        } else {
            int size = stack2.size();
            for (int i = 0; i < size; i++) {
                stack1.push(stack2.pop());
            }
            // 再颠倒过来
            int x = stack2.peek();
            size = stack2.size();
            for (int i = 0; i < size; i++) {
                stack1.push(stack2.pop());
            }
            return x;
        }
    }
    
    public boolean empty() {
        // 当两个栈同时为空时,才为空
        return stack1.empty() && stack2.empty();
    }
}

 上面这个代码有些过于繁琐了,可以简化为下面这样:

class MyQueue {
    public Stack<Integer> stack1;
    public Stack<Integer> stack2;

    public MyQueue() {
        stack1 = new Stack<>();
        stack2 = new Stack<>();
    }
    
    public void push(int x) {
        // 指定在一个中存放
        stack1.push(x);
    }
    
    public int pop() {
        while (stack2.empty()) {
            while (!stack1.empty()) {
                stack2.push(stack1.pop());
            }
        }
        return stack2.pop();
    }
    
    public int peek() {
        while (stack2.empty()) {
            while (!stack1.empty()) {
                stack2.push(stack1.pop());
            }
        }
        return stack2.peek();
    }
    
    public boolean empty() {
        // 当两个栈同时为空时,才为空
        return stack1.empty() && stack2.empty();
    }
}

这个代码就是把一个栈专门用来存放元素,另一个栈专门用来出元素。

225. 用队列实现栈 

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

实现 MyStack 类:

  • void push(int x) 将元素 x 压入栈顶。
  • int pop() 移除并返回栈顶元素。
  • int top() 返回栈顶元素。
  • boolean empty() 如果栈是空的,返回 true ;否则,返回 false 。

注意:

  • 你只能使用队列的标准操作 —— 也就是 push to backpeek/pop from frontsize 和 is empty 这些操作。
  • 你所使用的语言也许不支持队列。 你可以使用 list (列表)或者 deque(双端队列)来模拟一个队列 , 只要是标准的队列操作即可。

示例:

输入:
["MyStack", "push", "push", "top", "pop", "empty"]
[[], [1], [2], [], [], []]
输出:
[null, null, null, 2, 2, false]

解释:
MyStack myStack = new MyStack();
myStack.push(1);
myStack.push(2);
myStack.top(); // 返回 2
myStack.pop(); // 返回 2
myStack.empty(); // 返回 False

提示:

  • 1 <= x <= 9
  • 最多调用100 次 pushpoptop 和 empty
  • 每次调用 pop 和 top 都保证栈不为空

思路:一个队列存放元素,在pop时,把有元素的队列的前n-1个元素给到新队列,再根据需要处理最后一个元素即可。 

class MyStack {
    public Queue<Integer> queue1;
    public Queue<Integer> queue2;

    public MyStack() {
        queue1 = new LinkedList<>();
        queue2 = new LinkedList<>();
    }
    
    public void push(int x) {
        // 如果两个都为空,则任选一个
        if (!empty()) {
            queue1.offer(x);
        } else if (!queue1.isEmpty()) {
            queue1.offer(x);
        } else {
            queue2.offer(x);
        }
    }
    
    public int pop() {
        // 把不为空的队列的前n-1个元素出队
        if (!queue1.isEmpty()) {
            int size = queue1.size();
            for (int i = 0; i < size-1; i++) {
                queue2.offer(queue1.poll());
            }
            // 将原队列的队尾元素出队
            return queue1.poll();
        } else {
            int size = queue2.size();
            for (int i = 0; i < size-1; i++) {
                queue1.offer(queue2.poll());
            }
            return queue2.poll();
        }
    }
    
    public int top() {
        // 把不为空的队列的前n-1个元素出队
        if (!queue1.isEmpty()) {
            int size = queue1.size();
            for (int i = 0; i < size-1; i++) {
                queue2.offer(queue1.poll());
            }
            // 得到原队列的队尾元素,并插入新队列
            int x = queue1.peek();
            queue2.offer(queue1.poll());
            return x;
        } else {
            int size = queue2.size();
            for (int i = 0; i < size-1; i++) {
                queue1.offer(queue2.poll());
            }
            int x = queue2.peek();
            queue1.offer(queue2.poll());
            return x;
        }
    }
    
    public boolean empty() {
        // 两个都为空,则返回空
        return queue1.isEmpty() && queue2.isEmpty();
    }
}

当然本题可以使用双端队列,只不过题目要求要用两个队列,而且双端队列来实现栈比用两个队列更为简单,因为只需要维护一个队列即可。 

双端队列 思路:创建一个双端队列,正常入队元素,但出队是从队尾出。

综上:栈与队列之间的相互转换还是不难的,只要掌握了栈与队列的特点,再根据各自的特点去实现即可。 

好啦!本期 数据结构之探索“队列”的奥秘 的学习之旅就到此结束了,我们下一期再一起学习吧!

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

仓库管理系统的设计

管理员账户功能包括&#xff1a;系统首页&#xff0c;个人中心&#xff0c;管理员管理&#xff0c;公告管理&#xff0c;物资管理&#xff0c;基础数据管理&#xff0c;用户管理 用户账户功能包括&#xff1a;系统首页&#xff0c;个人中心&#xff0c;公告管理&#xff0c;物…

【python】PyCharm如何设置字体大小和背景

目录 效果展示 字体大小 背景设置 效果展示 字体大小 再左上角找到四条杠的图标 找到File 一般字体大小为22最合适&#xff0c;行间距为默认 背景设置 还是再字体设置的页面搜索 background 小编的其他文章详见&#xff0c;欢迎来支持 东洛的克莱斯韦克-CSDN博客 【机器…

failed to create network xxxx: Error response from daemon

问题描述&#xff1a; 启动项目时&#xff0c;docker内部网络冲突。 解决方案&#xff1a; 1.删除所有docker容器&#xff08;强制删除一个或多个容器&#xff0c;即使它们正在运行&#xff09; docker rm -f $(docker ps -aq) 2.验证docker容器是否删除成功 docker ps --…

“论数据访问层设计技术及其应用”写作框架,系统架构设计师

论文真题 在信息系统的开发与建设中&#xff0c;分层设计是一种常见的架构设计方法&#xff0c;区分层次的目的是为了实现“高内聚低耦合”的思想。分层设计能有效简化系统复杂性&#xff0c;使设计结构清晰&#xff0c;便于提高复用能力和产品维护能力。一种常见的层次划分模…

Google Adsense----Wordpress插入谷歌广告

1.搭建个人博客,绑定谷歌search consol,注册adsense 详细可以参考这个视频b站视频 2.将个人博客网站关联到Adsense 在adsense里新加网站,输入你的博客网址,双击网站 将这段代码复制到header.php的里面 在wordpress仪表盘的外观-主题文件编辑器,找到header.php将代码复制,…

C++跨平台socket编程

C跨平台socket编程 一、概述1.1 TCP协议1.1 TCP 的主要特性1.2 TCP报文格式 UDP报文格式IP协议使用windows编辑工具直接编辑Linux上代码 二、系统socket库1.windows上加载socket库2.创建socket2.1 windows下2.2 linux下 3.网络字节序4.bind端口5.listen监听并设置最大连接数6.a…

【Python机器学习实战】 | Lasso回归和弹性网回归详细分析研究

&#x1f3a9; 欢迎来到技术探索的奇幻世界&#x1f468;‍&#x1f4bb; &#x1f4dc; 个人主页&#xff1a;一伦明悦-CSDN博客 ✍&#x1f3fb; 作者简介&#xff1a; C软件开发、Python机器学习爱好者 &#x1f5e3;️ 互动与支持&#xff1a;&#x1f4ac;评论 &…

【windows|004】BIOS 介绍及不同品牌电脑和服务器进入BIOS设置的方法

&#x1f341;博主简介&#xff1a; &#x1f3c5;云计算领域优质创作者 &#x1f3c5;2022年CSDN新星计划python赛道第一名 &#x1f3c5;2022年CSDN原力计划优质作者 ​ &#x1f3c5;阿里云ACE认证高级工程师 ​ &#x1f3c5;阿里云开发者社区专家博主 &#x1f48a;交流社…

【BEV】BEVFormer总结

本文分享BEV感知方案中&#xff0c;具有代表性的方法&#xff1a;BEVFormer。 它基于Deformable Attention&#xff0c;实现了一种融合多视角相机空间特征和时序特征的端到端框架&#xff0c;适用于多种自动驾驶感知任务。 主要由3个关键模块组成&#xff1a; BEV Queries Q&am…

【qt5生成软件-can卡-上位机-无法加载ControlCAN.dll错误代码(0xc0150002)等相关问题-WIN11系统-尝试解决】

【qt5生成软件-无法加载ControlCAN.dll&错误代码0xc0150002&#xff1a;-等相关问题-WIN11系统-尝试解决-总结整理】 1.前言2.环境说明3.问题说明4.尝试方法总结&#xff08;1&#xff09;更新支持包c库&#xff08;2&#xff09;更新USB相关驱动&#xff08;3&#xff09;…

安装pytorch环境

安装&#xff1a;Anaconda3 通过命令行查显卡nvidia-smi 打开Anacanda prompt 新建 conda create -n pytorch python3.6 在Previous PyTorch Versions | PyTorch选择1.70&#xff0c;安装成功&#xff0c;但torch.cuda.is_available 返回false conda install pytorch1.7.0…

【golang学习之旅】使用VScode安装配置Go开发环境

1. 下载并安装Go1.1 下载地址1.2 选择版本并下载1.3 安装目录1.4 验证是否安装成功 2. 配置环境变量2.1 配置步骤2.2 GO部分环境变量说明 3. 下载或更新 Vscode3.1 下载地址3.2 安装步骤 4. 为Go开发配置VScode 1. 下载并安装Go 1.1 下载地址 https://studygolang.com/dl 1.…

【单片机】三极管的电路符号及图片识别

一&#xff1a;三极管的电路符号 二&#xff1a;三极管的分类 a;按频率分&#xff1a;高频管和低频管 b;按功率分&#xff1a;小功率管&#xff0c;中功率管和的功率管 c;按机构分&#xff1a;PNP管和NPN管 d;按材质分&#xff1a;硅管和锗管 e;按功能分&#xff1a;开关管和放…

硬盘分区无法访问:深度解析与解决之道

一、硬盘分区无法访问的现象描述 在日常使用电脑的过程中&#xff0c;有时会遇到硬盘分区无法访问的情况。这通常表现为双击分区时系统提示“无法访问”、“磁盘未格式化”或“需要格式化”等错误消息&#xff0c;导致分区内的文件无法读取或操作。这种情况可能会给用户带来极…

构建高效、便捷的家校沟通桥梁

在现代教育中&#xff0c;家校之间的有效沟通和协作是确保学生全面发展的关键。搭贝家校管理应用通过一系列强大而便捷的功能&#xff0c;帮助学校和家长实现无缝对接&#xff0c;提供全面的管理和服务。以下是搭贝家校管理应用的主要功能和优势。 &#x1f3eb; 主要功能模…

发布自己的c#包到nuget

1)创建自己的nuget账号 NuGet Gallery | Home 2)在Rider中-->项目文件夹右键-->properties 注意&#xff1a;必须勾选生成nuget包 3)编译后&#xff0c;将生成一个包 4)点击上传包 5)将之前的nuget包拖拽过来&#xff0c;点击上传即可&#xff0c;如果有不对的比如&a…

STM32开发环境搭建

新建工程 1.双击桌面的快捷方式打开STM32CubeIDE&#xff0c;需要选择一下工作空间&#xff0c;保存路径可以根据实际选择其他路径&#xff08;不要带中文&#xff09;。 点击File->New->STM32 Project. 搜索并选择芯片&#xff0c;我这里以STM32F103RCT6为例&#xff0…

python学习笔记-08

面向对象基础(OOP)-上 1. 面向对象概述 面向过程&#xff1a;根据业务逻辑从上到下写代码 函数式&#xff1a;将某功能代码封装到函数中&#xff0c;日后便无需重复编写&#xff0c;仅调用函数即可 面向对象(object oriented programming)&#xff1a;将数据与函数绑定到一起…

防爆气象站:化工厂区气象环境监测的最佳选择

在化工厂区&#xff0c;气象环境监测至关重要&#xff0c;它直接关系到生产安全、环境保护以及员工健康。防爆气象站作为专业的监测设备&#xff0c;凭借其独特的防爆性能和精准的数据监测能力&#xff0c;成为化工厂区气象环境监测的最佳选择。 防爆气象站采用先进的防爆技术…