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

news2024/11/16 19:41:11

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

个人主页:我要学编程(ಥ_ಥ)-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();
    }
}

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

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

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

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

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

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

相关文章

【C++初阶路】--- 类和对象(中)

目录 一、this指针1.1 this指针的引出1.2 this指针的特性1.3. C语言和C实现Stack的对比 二、类的6个默认成员函数三、构造函数3.1 概念3.2 特性 一、this指针 1.1 this指针的引出 如下定义一个日期类Date class Date { public://void InitDate(Date* const this, int year …

[Vulnhub] BrainPan BOF缓冲区溢出+Man权限提升

信息收集 Server IP AddressPorts Open192.168.8.105TCP: $ nmap -p- 192.168.8.105 -sC -sV -Pn --min-rate 1000 Starting Nmap 7.92 ( https://nmap.org ) at 2024-06-10 04:20 EDT Nmap scan report for 192.168.8.105 (192.168.8.105) Host is up (0.0045s latency). N…

gtk+2.0设置按钮背景图片

效果 在gtk+2.0中,可以通过gtk_widget_get_style获取按钮的样式,并设置bg_pixmap背景图片的方法。 其中bg_pixmap数组有下面的枚举类型,代表不同情况下的图片类型。这意味着你可以在按钮正常,鼠标覆盖和鼠标按下的时候分别设置不同的图片。 enum GtkStateType typedef e…

【植物大战僵尸杂交版】致敬传奇游戏玩家——一个普通人的六年坚持

目录 缘起 波澜 凌云 缘起 曾​​​​​​佳伟是《植物大战僵尸》的忠实粉丝&#xff0c;这款游戏给了他很多乐趣&#xff0c;也成为了他度过困难时期的精神支柱。他决定制作杂交版&#xff0c;部分原因是出于对原版游戏的热爱和致敬。 六年前&#xff0c;出于对一些pvz续作…

立讯精密:“果链一哥”怎么摆脱依赖症

AI手机创新赋能&#xff0c;隔岸苹果股价走出历史新高&#xff0c;消费电子有望迎来复苏&#xff1f; 这里我们聊聊苹果产业链代工龙头——立讯精密 作为早早入场的代工企业&#xff0c;立讯精密曾经吃足“果链”红利&#xff0c;如今摆在它面前的是增长、毛利、安全等难题。 …

Springboot 整合 Flowable(二):使用 Flowable BPMN visualizer 绘制流程图

&#x1f4c1; Springboot 整合 Flowable&#xff08;一&#xff09;&#xff1a;使用 flowable-UI 绘制流程图-CSDN博客 一、安装 IDEA 插件&#xff1a;Flowable BPMN visualizer 二、绘制流程图 1、创建流程文件 2、选中文件后&#xff0c;右键打开流程图设计界面 以一个简…

使用fetch加载阿里云的在线json 出现403错误

在做地图项目的时候&#xff0c;引用了阿里云的在线JSON地图数据。 问题描述&#xff1a; 但是本地开发使用fetch请求json地址的时候接口却出现了403错误&#xff0c;把地址直接复制到浏览器上却能正常打开。 https://geo.datav.aliyun.com/areas_v3/bound/330000_full.json …

300元拥有一个100%属于自己的博客网站

这里写目录标题 为什么要建个人博客香港独立云主机购买价格研究独立主机购买 1panel安装图床搭建halo博客网站一键安装优先安装mysql数据库安装halo uuwaf南墙防火墙一键安装和配置安装uuwaf创建站点上传SSL证书 halo登录和配置halo开启MFA两步验证主题插件 完结 为什么要建个人…

怎么投资中证全指证券公司指数?

中证全指证券公司指数的代码是399975&#xff0c;有50只成分股&#xff0c;几乎包含了市场上所有主要的证券公司&#xff0c;算是指数基金中投资证券行业的不二选择。 根据天天基金的数据显示&#xff0c;市面上有31只跟踪该指数的基金&#xff0c;规模最大的是南方中证全指证…

【阿里前端面试题】虚拟列表滚动组件开发的原因,以及关键技术点实现

大家好&#xff0c;我是“寻找DX3906”。每天进步一点。日积月累&#xff0c;有朝一日定会厚积薄发&#xff01; 前言&#xff1a; 前面已经和大家分享阿里的了6篇前端面试题&#xff1a; 《【阿里前端面试题】浏览器的加载渲染过程》 《【阿里前端面试题】客户端和服务器交互…

考研数学强化,880+660正确打开方式

1800题基础做完了&#xff1f;做的怎么样&#xff01; 之所以问你做的怎么样&#xff0c;是因为1800题做的好坏&#xff0c;直接决定了你要不要开始做880题和660题。 有的同学1800题做的很好&#xff0c;做完1800题之后开始做880660没毛病 但是有的同学就是纯纯的为了做题而…

Python 基础:文件

目录 一、从文件中读取数据1.1 读取整个文件1.2 逐行读取 二、写入文件2.1 写入空文件2.2 写入多行2.3 附加到文件 遇到看不明白的地方&#xff0c;欢迎在评论中留言呐&#xff0c;一起讨论&#xff0c;一起进步&#xff01; 本文参考&#xff1a;《Python编程&#xff1a;从入…

MySQL版本发布模型

MySQL 8.0 之后使用了新的版本控制和发布模型&#xff0c;分为两个主线&#xff1a;长期支持版&#xff08;LTS&#xff09;以及创新版。这两种版本都包含了缺陷修复和安全修复&#xff0c;都可以用于生产环境。 下图是 MySQL 的版本发布计划&#xff1a; 长期支持版 MySQL…

深度学习项目十六:根据训练好的权重文件推理图片--YOLO系列

文章目录 根据训练好的权重文件推理图片--YOLO系列一、自己构建YOLOv5推理代码1.1 对数据集进行模型训练1.2 对数据集进行模型推理检测1.3 自己编写推理函数1.3.1 针对单张进行推理1.3.2 针对文件夹下的图片进行推理二、自己构建YOLOv8推理代码2.1 对数据集进行模型训练2.2 对数…

HTML星空特效

目录 写在前面 完整代码 代码分析 运行效果 系列文章 写在后面 写在前面 100行代码实现HTML星空特效。 完整代码 全部代码如下。 <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"&g…

R语言统计分析——图形的组合

参考资料&#xff1a;R语言实战【第2版】 在R种使用函数par()或layout()可以容易地组合多幅总括图形。 我们可以在par()函数中使用图形参数mfrowc(nrows,ncols)来创建按行填充的行数为nrows、列数为ncols的图形矩阵。另外&#xff0c;可以使用mfcolc(nrows,ncols)按列填充矩阵。…

SpringBoo+vue3+vite整合讯飞星火3.5通过webscoket实现聊天功能(前端代码)附带展示效果

访问地址&#xff1a; 天梦星服务平台 (tmxkj.top)https://tmxkj.top/#/site 后端文档&#xff1a; SpringBoovue3整合讯飞星火3.5通过webscoket实现聊天功能&#xff08;全网首发&#xff09;附带展示效果_springboot websocket vue3-CSDN博客https://blog.csdn.net/qq_53722…

GBT20041.21金属导管弯曲试验机

一、前言 电动金属导管弯曲试验机依据(GB/T20041.21-2008电缆管理用导管系统刚性导管系统的特殊要求》、《GB/T14823.1电气安装用导管特殊要求金属导管》、《JG/T3050建筑用绝缘电工套管及配件》及《ZBG33008聚氯乙稀塑料波纹电线管》开发的新型产品。适用于金属导管等材料做弯…

【话题】层出不穷的大模型产品,你怎么选?

大家好&#xff0c;我是全栈小5&#xff0c;欢迎阅读小5的系列文章&#xff0c;这是《话题》系列文章 目录 引言元宝体验产品介绍AI作画 文档总结AI超级产品文章推荐 引言 随着近日腾讯元宝APP的正式上线&#xff0c;国内大模型产品又添一员。 关于接连出现的“全能“大模型AI…

判断单链表是否带环且返回节点

今天鄙人为大家带来的是一道简单的逻辑运算题。用用到了一个我们在链表中提及过的方法快慢法。这道题其实没啥考的实际意义。只是我们如果能了解这道题的解决方法的话。对我们后面梳理逻辑会有很大的帮助。 单链表的题目 我们可以看到上面的题目。就是让我们判断是否带环。也许…