【数据结构】(6) LinkedList 链表

一、什么是链表

1、链表与顺序表对比

不同点LinkedListArrayList
物理存储上不连续连续
随机访问效率O(N)O(1)
插入、删除效率O(1)O(N)

3、链表的分类

        链表根据结构分类,可分为单向/双向无头结点/有头节点非循环/循环链表,这三组每组各取一个就构成一种结构链表。其中,单向、不带头、非循环链表学习的重点双向、不带头、非循环链表在实际开发中常用。

        以单向、不带头、非循环链表为例:

        双向、不带头、非循环链表:

        单向、带头、非循环链表(好处在于,不用特别处理头节点,比如增删时):

        单向、不带头、循环链表:

二、单向、不带头、非循环链表的实现

1、IList 接口

        定义 IList 接口,声明线性表需要实现的方法:

package listinterface;

public interface IList {
    //头插法
    void addFirst(int data);
    //尾插法
    void addLast(int data);
    //任意位置插入,第一个数据节点为0号下标
    void addIndex(int index,int data);
    //查找是否包含关键字key是否在单链表当中
    boolean contains(int key);
    //删除第一次出现关键字为key的节点
    void remove(int key);
    //删除所有值为key的节点
    void removeAllKey(int key);
    //得到单链表的长度
    int size();
    void clear();
    void display();
}

2、结点内部类

        结点仅由链表内部使用,且每个结点的产生不依赖于某个链表,可定义为链表的私有静态内部类:

public class MySingleList implements IList {
    // 结点内部类
    private static class Node {
        int data;
        Node next;

        public Node(int data) {
            this.data = data;
        }
    }
    // 头结点
    private Node head;
    // 链表长度
    private int size;

    ............
}

3、打印链表

    @Override
    public void display() {
        Node cur = head;
        while (cur != null) {
            System.out.print(cur.data + " ");
            cur = cur.next;
        }
        System.out.println(" size: " + size);
    }

4、清空链表、获取链表长度

    @Override
    public int size() {
        return size;
    }

    @Override
    public void clear() {
        head = null;
        size = 0;
    }

        head 为空,没有引用指向 node1,自动回收 node1;node1 回收,没有引用指向 node2,自动回收 node2……故 clear 只需 head 设为 null 即可。

5、头插法

    @Override
    public void addFirst(int data) {
        Node newNode = new Node(data);
        newNode.next = head;
        head = newNode;
//        if (head != null) {
//            newNode.next = head;
//        }
//        head = newNode;
        size++;
    }

6、尾插法

    @Override
    public void addLast(int data) {
        Node newNode = new Node(data);
        if (head == null) {
            head = newNode;
            size++;
            return;
        }
        Node cur = head;
        while (cur.next != null) { // 找到尾节点
            cur = cur.next;
        }
        cur.next = newNode;
        size++;
    }

7、任意位置插入

受查异常,父类方法没抛出异常,子类方法不能抛出异常;父类抛出了,子类不做要求。非受查异常,不做要求。)为了不改动 IList,且让 addIndex 抛出异常,以便在 main 处对异常做处理,链表下标越界异常选择继承非受查异常 RuntimeExcption。

package myexception;

public class ListIndexOutOfBoundsException extends RuntimeException {
    public ListIndexOutOfBoundsException(String message) {
        super(message);
    }
}
    private void checkIndexOutOfBounds(int index) throws ListIndexOutOfBoundsException {
        if (index < 0 || index > size) {
            throw new ListIndexOutOfBoundsException("插入位置不合法。" + "Index: " + index + ", Size: " + size);
        }
    }

    private Node findIndex(int index) {
        Node cur = head;
        int cnt = 0;
        while (cnt != index) {
            cur = cur.next;
            cnt++;
        }
        return cur;
    }

    @Override
    public void addIndex(int index, int data) throws ListIndexOutOfBoundsException {
        // 先检查索引是否越界
        checkIndexOutOfBounds(index);
        if (index == 0) { // 在 0 处插入,没有前驱节点
            addFirst(data);
            return;
        }
        if (index == size) { // 在末尾插入
            addLast(data);
            return;
        }
        Node newNode = new Node(data);
        // 找到插入位置前的节点
        Node pre = findIndex(index - 1);
        // 插入新节点
        newNode.next = pre.next;
        pre.next = newNode;
        size++;
    }

8、删除第一次出现关键字为key的节点

    private Node findKey(int key) {
        Node cur = head;
        while (cur.next != null) {
            if (cur.next.data == key) {
                return cur;
            }
            cur = cur.next;
        }
        return null;
    }

    @Override
    public void remove(int key) {
        if (head == null) { // 空链表
            return;
        }
        if (head.data == key) { // 头节点就是要删除的节点
            head = head.next;
            size--;
            return;
        }
        Node pre = findKey(key);
        if (pre != null) { // 找到了要删除的节点
            pre.next = pre.next.next;
            size--;
        } else { // 要删除的节点不存在
            System.out.println("要删除的节点不存在。");
        }
    }

9、删除所有关键字为 key 的节点

    @Override
    public void removeAllKey(int key) {
        if (head == null) { // 空链表
            return;
        }
        Node pre = head;
        Node cur = head.next;
        while (cur != null) {
            if(cur.data == key) {
                pre.next = cur.next;
                // cur = cur.next;
            }
            else {
                pre = cur;
                // cur = cur.next;
            }
            cur = cur.next;
        }
        if (head.data == key) {
            head = head.next;
            size--;
        }
    }

三、链表面试题练习

1、反转链表

206. 反转链表 - 力扣(LeetCode)

思路:从头开始,边遍历边反转,最后尾结点为 head。

class Solution {
    public ListNode reverseList(ListNode head) {
        // 特殊处理 空链表、只有一个结点的链表
        if(head == null || head.next == null) { // 两者顺序不能反,因为 null 没有next
            return head;
        }
        ListNode cur = head.next;
        // 头结点,其 next 为 null 
        head.next = null;
        // 边反转边遍历,直到反转完尾结点
        while(cur != null) {
            ListNode curN = cur.next; 
            cur.next = head;
            head = cur;
            cur = curN;
        }
        return head;
    }
}

2、链表的中间结点

876. 链表的中间结点 - 力扣(LeetCode)

思路

1、计算链表长度的一半,再走一半长。时间复杂度 O(N) + O(N/2)。

2、设置 slow 和 fast 从 head 开始走,slow 每次走 1 步,fast 每次走 2 步。那么 fast 走完全程(fast = null 或者 fast.next = null),必是 slow 的两倍长度,故 slow 就是中间节点的位置。时间复杂度 O(N/2) ,更优。

class Solution {
    public ListNode middleNode(ListNode head) { // 根据题目,链表非空
        // 从头节点开始
        ListNode slow = head;
        ListNode fast = head;
        // fast 走完整个链表
        while(fast != null && fast.next != null) {
            // slow 每次走 1 步,fast 每次走 2 步
            slow = slow.next;
            fast = fast.next.next;
        }
        // slow 就是中间结点
        return slow;
    }
}

3、返回倒数第 k 个结点

面试题 02.02. 返回倒数第 k 个节点 - 力扣(LeetCode)

思路:slow 从 head 开始,fast 比 slow 先多走 k-1 步,然后 slow、fast 每次只走一步,直到 fast 走到尾结点。

class Solution {
    public int kthToLast(ListNode head, int k) {
        // 根据题目,k 保证有效,因此不用判断 k<=0 和 k > len 的情况
        ListNode slow = head;
        ListNode fast = head;
        // 当 fast 先走 k-1 步
        int cnt = k-1;
        // k保证有效,fast 不会走过头
        while(cnt != 0) { 
            fast = fast.next;
            cnt--;
        }
        // 让 fast 走到尾结点,slow 就是倒数第 k 个结点
        while(fast.next != null) {
            slow = slow.next;
            fast = fast.next;
        }
        return slow.val;
    }
}

 4、合并两个有序列表

21. 合并两个有序链表 - 力扣(LeetCode)

思路:分别同时遍历两个链表,更小的插入新链表,插入的链表插入后更新为 next,直到 l1 或者 l2 遍历完了。没遍历完的那个链表,把剩的接在新链表尾。为了不单独处理头指针,新链表带头节点。

class Solution {
    public ListNode mergeTwoLists(ListNode list1, ListNode list2) {
        ListNode head = new ListNode(); // 创建头节点
        ListNode cur = head;
        ListNode cur1 = list1;
        ListNode cur2 = list2;
        // 其中一个遍历完就退出
        while(cur1 != null && cur2 != null) {
            if(cur1.val < cur2.val) {
                cur.next = cur1;
                // cur = cur.next;
                cur1 = cur1.next;
            }
            else {
                cur.next = cur2;
                // cur = cur.next;
                cur2 = cur2.next;
            }
            cur = cur.next;
        }
        // 没遍历完的,接在后面
        if(cur1 != null) {
            cur.next = cur1;
        }
        if(cur2 != null) {
            cur.next = cur2;
        }
        return head.next; // 不带头节点
    }
}

5、链表分割

链表分割_牛客题霸_牛客网

思路:先创建两个链表,依次遍历原链表,小于和大于等于 x 的分开尾插入两个链表,再合并。

public class Partition {
    public ListNode partition(ListNode pHead, int x) {
        // 创建两个头节点
        ListNode head1 = new ListNode(-1); 
        ListNode head2 = new ListNode(-1);
        // 遍历原链表,每个结点与 x 比大小,分类放在两个新链表中
        ListNode cur1 = head1;
        ListNode cur2 = head2; 
        while(pHead != null) {
            if(pHead.val < x) {
                cur1.next = pHead;
                cur1 = cur1.next;
            }
            else {
                cur2.next = pHead;
                cur2 = cur2.next;
            }
            pHead = pHead.next;
        }
        // 现在 cur1、cur2 分别指向两个链表的尾结点。
        cur1.next = head2.next; // 链表1的尾接上链表2的头
        cur2.next = null; // 链表2的尾接上 null
        // 如果最后链表1为空,cur1就是head1,没问题
        // 如果最后链表2为空,cur2就是head2,没问题
        return head1.next;
    }
}

6、链表的回文结构

链表的回文结构_牛客题霸_牛客网

思路

1、求尾结点,头、尾同时前进并比较,到 head = tail(奇数长度) 或者 head.next = tail (偶数长度)为止。tail 往前走行不通,因为是单链表。

2、把原链表的后一半反转,再将反转链表与原链表对比,直到反转链表遍历完。时间复杂度O(N/2)+O(N/2)+O(N/2)=O(N)。找到一半处、将一半反转、对比一半。

public class PalindromeList {

    private ListNode findHalf(ListNode head) {
        ListNode slow = head;
        ListNode fast = head;
        while(fast != null && fast.next != null) {
            slow = slow.next;
            fast = fast.next.next;
        }
        return slow;
    }

    private ListNode reverse(ListNode head) {
        if(head == null || head.next == null) {
            return head;
        }
        ListNode cur = head.next;
        head.next = null;
        while(cur != null) {
            ListNode curN = cur.next;
            cur.next = head;
            head = cur;
            cur = curN;
        }
        return head;
    }

    public boolean chkPalindrome(ListNode A) {
        // 找到中间结点
        ListNode halfNode = findHalf(A);
        // 将后一半反转
        ListNode B = reverse(halfNode);
        // 将后半段与前半段对比,后半段遍历完退出
        while(B != null) {
            if(A.val != B.val) {
                return false;
            }
            A = A.next;
            B = B.next;
        }
        return true;
    }
}

7、相交链表

160. 相交链表 - 力扣(LeetCode)

思路:可能分叉的地方,前边或后边。但是对于链表不可能后边分叉,因为如果后边分叉了,当你遍历一条单链时,遍历到后边分叉的地方,就不知道继续走哪条路了,这样就不是单链表了。因此只有可能如上图的Y型,或者在一条直线上。

        如果同时各自出发,每次走一步,直到有一方到达尾结点,那么短的链表总是先到达,并且它们的最终距离是两链表的长度差。长度差来自分叉的部分,而不是相交的部分。那么如果让长的先走长度差步,再同时走,它们碰面的地方将是相交处。

public class Solution {

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

    public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
        // 求两条链的长度
        int size1 = size(headA);
        int size2 = size(headB);
        // 让长的作为 headA,求两链表长度差(正),
        int len = size1 - size2;
        if(size1 < size2) {
            len = size2 - size1;
            ListNode tmp = headA;
            headA = headB;
            headB = tmp;
        }
        // 让长的先走 len 步
        while(len != 0) {
            headA = headA.next;
            len--;
        }
        // 再同时走,相等的地方就是相交结点
        while(headA != headB) {
            headA = headA.next;
            headB = headB.next;
        }
        return headA;
    }
}

8、环形链表

141. 环形链表 - 力扣(LeetCode)

思路:慢指针和快指针同时走,如果有环,那么快、慢指针总会在环里相遇(快指针多跑 k 圈,然后追上慢指针);如果没环,快指针先走完全程结束。我们设慢指针每次走1步,快指针每次走2步。

        为什么快指针不能是 3、4、……、n 步?如果是3步,存在以下情况,无论走多久都不会相遇:

        快指针如果走 4 步,存在以下情况,无论走多久都不会相遇:

        以此类推……

public class Solution {
    public boolean hasCycle(ListNode head) {
        // 设置快、慢指针
        ListNode slow = head;
        ListNode fast = head;
        // slow 每次走1步,fast 每次走2步
        // 直到 fast = null(偶数个) 或者 fast.next = null(奇数个) 返回 flase
        // 或者 slow = fast 返回 true
        while(fast != null && fast.next != null) {
            slow = slow.next;
            fast = fast.next.next;
            if(slow == fast) {
                return true;
            }
        }
        return false;
    }
}

9、环形链表Ⅱ

142. 环形链表 II - 力扣(LeetCode)

思路

        若一指针 head1 从相遇点开始,一指针 head2 从头指针开始,同时走,每次走一步。当 head1 走 X 步到达入口;同时,head2 从相遇点开始走了 (k-1) 圈回到相遇点,再走 N 步到入口。即两指针相遇处,就是入口结点。

public class Solution {

    private ListNode getMeetNode(ListNode head) {
        ListNode slow = head;
        ListNode fast = head;
        while(fast != null && fast.next != null) {
            slow = slow.next;
            fast = fast.next.next;
            if(slow == fast) {
                return slow;
            }
        }
        return null;
    }

    public ListNode detectCycle(ListNode head) {
        // 获得 slow 与 fast 的相遇结点
        ListNode meetNode = getMeetNode(head);
        // 链表无环,返回 null
        if(meetNode == null) {
            return null;
        }
        // 分别从头指针,相遇结点开始走,两指针相遇处就是入口
        while(head != meetNode) {
            head = head.next;
            meetNode = meetNode.next;
        }
        return head;
    }
}

四、双向、不带头、非循环链表的实现

1、Node 内部类和属性

public class MyLinkedList  implements IList {
    private static class Node {
        int val;
        Node next; // 后继指针
        Node prev; // 前驱指针

        public Node(int val) {
            this.val = val;
        }
    }
    private Node head; // 头指针
    private Node tail; // 尾指针
    private int size; // 链表大小
    
    ............
}

2、清空链表

        与单向不同直接 head = null,双向需要遍历结点并释放 node,原因如下:head 为空,还有 node2 指向 node1,所以手动置 node2 的 pre 为空,node1释放;还有 node3 指向 node2,手动置 node3 的 pre 未空……

    @Override
    public void clear() {
        Node cur = head;
        while (cur!= null) {
            Node curN = cur.next;
            cur.prev = null;
            cur.next = null;
            cur = curN;
        }
        head = null;
        tail = null;
        size = 0;
    }

3、头插法

    @Override
    public void addFirst(int data) {
        Node newNode = new Node(data);
        if (head == null) {
            head = newNode;
            tail = newNode;
        } else {
            newNode.next = head;
            head.prev = newNode;
            head = newNode;
        }
        size++;
    }

4、尾插法

    @Override
    public void addLast(int data) {
        Node newNode = new Node(data);
        if (head == null) {
            head = newNode;
            tail = newNode;
        } else {
            tail.next = newNode;
            newNode.prev = tail;
            tail = newNode;
        }
        size++;
    }

5、任意位置插入

    @Override
    public void addIndex(int index, int data) {
        // 先检查索引是否越界
        checkIndexOutOfBounds(index);
        if (index == 0) { // 在 0 处插入,没有前驱节点
            addFirst(data);
            return;
        }
        if (index == size) { // 在末尾插入
            addLast(data);
            return;
        }
        Node newNode = new Node(data);
        // 找到插入位置
        Node cur = findIndex(index);
        // 插入新节点
        newNode.next = cur;
        newNode.prev = cur.prev;
        cur.prev.next = newNode;
        cur.prev = newNode;
        size++;
    }

6、删除第一次出现的 key

    private Node findKey(int key) {
        Node cur = head;
        while (cur != null) {
            if (cur.val == key) {
                return cur;
            }
            cur = cur.next;
        }
        return null;
    }

    @Override
    public void remove(int key) {
        Node deleteNode = findKey(key); // 找到待删除节点,如果不存在,返回 null
        if (deleteNode == null) { // 包含空链表的情况
            System.out.println("要删除的节点不存在。");
            return;
        }
        if (deleteNode == head) { // 待删除节点是头节点,包含了链表只有一个结点的情况
            head = deleteNode.next;
            if (head == null) { // 链表只有一个节点
                tail = null;
            } 
            else {
                head.prev = null;
            }
        } else if (deleteNode == tail) { // 待删除节点是尾节点
            tail = deleteNode.prev;
            tail.next = null;
        } else { // 待删除节点是中间节点
            deleteNode.prev.next = deleteNode.next;
            deleteNode.next.prev = deleteNode.prev;
        }
        size--;
    }

7、删除所有 key

    @Override
    public void removeAllKey(int key) {
        Node cur = head;
        while (cur != null) {
            if (cur.val == key) {
                if (cur == head) { // 待删除节点是头节点
                    head = cur.next;
                    if (head == null) { // 链表中只有一个节点
                        tail = null;
                    }
                    else {
                        head.prev = null;
                    }
                } else if (cur == tail) { // 待删除节点是尾节点
                    tail = cur.prev;
                    tail.next = null;
                } else { // 待删除节点是中间节点
                    cur.prev.next = cur.next;
                    cur.next.prev = cur.prev;
                }
                size--;
//                cur = cur.next; // 跳过已删除节点,继续遍历
            } /*else {
                cur = cur.next;
            }*/
            cur = cur.next;
        }
    }

五、LinkedList 的使用

        集合类中,LinkedList 的底层是双向链表

1、常用方法的使用

2、迭代器

        Iterator<E> 是集合类通用的迭代器,线性表专用的迭代器 ListIterator<E> 功能更强,可以反向迭代:

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

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

相关文章

Mac电脑上好用的压缩软件

在Mac电脑上&#xff0c;有许多优秀的压缩软件可供选择&#xff0c;这些软件不仅支持多种压缩格式&#xff0c;还提供了便捷的操作体验和强大的功能。以下是几款被广泛推荐的压缩软件&#xff1a; BetterZip 功能特点&#xff1a;BetterZip 是一款功能强大的压缩和解压缩工具&a…

VUE 集成企微机器人通知

message-robot 便于线上异常问题及时发现处理&#xff0c;项目中集成企微机器人通知&#xff0c;及时接收问题并处理 企微机器人通知工具类 export class MessageRobotUtil {constructor() {}/*** 发送 markdown 消息* param robotKey 机器人 ID* param title 消息标题* param…

通信易懂唠唠SOME/IP——SOME/IP-SD服务发现阶段和应答行为

一 SOME/IP-SD服务发现阶划分 服务发现应该包含3个阶段 1.1 Initial Wait Phase初始等待阶段 初始等待阶段的作用 初始等待阶段是服务发现过程中的一个阶段。在这个阶段&#xff0c;服务发现模块等待服务实例的相关条件满足&#xff0c;以便继续后续的发现和注册过程。 对…

Day 30 卡玛笔记

这是基于代码随想录的每日打卡 93. 复原 IP 地址 有效 IP 地址 正好由四个整数&#xff08;每个整数位于 0 到 255 之间组成&#xff0c;且不能含有前导 0&#xff09;&#xff0c;整数之间用 . 分隔。 例如&#xff1a;"0.1.2.201" 和 "192.168.1.1" …

【中间件】 Kafka

1.先导知识&#xff1a; 消息队列MQ(Message Queue): 将需要传输的数据临时(设置有效期)存放在队列中,进行存取消息消息队列中间件&#xff1a; 用来存储消息的中间件(组件) 2.消息队列的应用场景 异步处理 为什么要使用消息队列&#xff1f; 比较耗时的操作放在其他系统中…

给排水 笔记

给水管&#xff08;上水管&#xff09; 概述 专用于上水系统的管道。 单位 基本单位解读 项概述符号表示备注公称直径&#xff08;DN&#xff09;指管道的平均外直径。是行业描述的标准&#xff0c;是参考值&#xff0c;并非指任何直径。-理论外直径。公称外径&#xff08…

React 设计模式:实用指南

React 提供了众多出色的特性以及丰富的设计模式&#xff0c;用于简化开发流程。开发者能够借助 React 组件设计模式&#xff0c;降低开发时间以及编码的工作量。此外&#xff0c;这些模式让 React 开发者能够构建出成果更显著、性能更优越的各类应用程序。 本文将会为您介绍五…

C++11详解(三) -- 可变参数模版和lambda

文章目录 1.可变模版参数1.1 基本语法及其原理1.2 包扩展1.3 empalce系列接口1.3.1 push_back和emplace_back1.3.2 emplace_back在list中的使用&#xff08;模拟实现&#xff09; 2. lambda2.1 lambda表达式语法2.2 lambda的捕捉列表2.3 lambda的原理 1.可变模版参数 1.1 基本…

【数据结构】_时间复杂度相关OJ(力扣版)

目录 1. 示例1&#xff1a;消失的数字 思路1&#xff1a;等差求和 思路2&#xff1a;异或运算 思路3&#xff1a;排序&#xff0b;二分查找 2. 示例2&#xff1a;轮转数组 思路1&#xff1a;逐次轮转 思路2&#xff1a;三段逆置&#xff08;经典解法&#xff09; 思路3…

OSPF基础(2):数据包详解

OSPF数据包(可抓包) OSPF报文直接封装在IP报文中&#xff0c;协议号89 头部数据包内容&#xff1a; 版本(Version):对于OSPFv2&#xff0c;该字段值恒为2(使用在IPV4中)&#xff1b;对于OSPFv3&#xff0c;该字段值恒为3(使用在IPV6中)。类型(Message Type):该OSPF报文的类型。…

第二篇:前端VSCode常用快捷键-以及常用技巧

继续书接上一回&#xff0c; 我们讲解了常用的vscode 插件。 vscode 常用的插件地址&#xff1a; 前端VSCode常用插件-CSDN博客 本篇文章&#xff0c;主要介绍vscode常用的快捷键&#xff0c;可以提高我们的开发效率。 一、VSCode常用的快捷键 注意&#xff0c;其实这个快捷…

【LeetCode】152、乘积最大子数组

【LeetCode】152、乘积最大子数组 文章目录 一、dp1.1 dp1.2 简化代码 二、多语言解法 一、dp 1.1 dp 从前向后遍历, 当遍历到 nums[i] 时, 有如下三种情况 能得到最大值: 只使用 nums[i], 例如 [0.1, 0.3, 0.2, 100] 则 [100] 是最大值使用 max(nums[0…i-1]) * nums[i], 例…

vue生命周期及其作用

vue生命周期及其作用 1. 生命周期总览 2. beforeCreate 我们在new Vue()时&#xff0c;初始化一个Vue空的实例对象&#xff0c;此时对象身上只有默认的声明周期函数和事件&#xff0c;此时data,methods都未被初始化 3. created 此时&#xff0c;已经完成数据观测&#xff0…

什么是三层交换技术?与二层有什么区别?

什么是三层交换技术&#xff1f;让你的网络飞起来&#xff01; 一. 什么是三层交换技术&#xff1f;二. 工作原理三. 优点四. 应用场景五. 总结 前言 点个免费的赞和关注&#xff0c;有错误的地方请指出&#xff0c;看个人主页有惊喜。 作者&#xff1a;神的孩子都在歌唱 大家好…

e2studio开发RA2E1(5)----GPIO输入检测

e2studio开发RA2E1.5--GPIO输入检测 概述视频教学样品申请硬件准备参考程序源码下载新建工程工程模板保存工程路径芯片配置工程模板选择时钟设置GPIO口配置按键口配置按键口&Led配置R_IOPORT_PortRead()函数原型R_IOPORT_PinRead()函数原型代码 概述 本篇文章主要介绍如何…

【LLM】为何DeepSeek 弃用MST却采用Rejection采样

文章目录 拒绝采样 Rejection sampling&#x1f3af;马尔可夫搜索树 &#x1f333;RFT和SFT1. RFT和SFT的区别2. 如何将RFT用于数学推理任务&#xff1f; Reference 在提升大语言模型&#xff08;LLM&#xff09;推理能力时&#xff0c;拒绝采样&#xff08;Rejection Sampling…

股指入门:股指期货是什么意思?在哪里可以做股指期货交易?

股指期货是一种以股票指数为标的物的期货合约&#xff0c;也可以称为股票指数期货或期指。 股指期货是什么意思&#xff1f; 股指期货是一种金融衍生品&#xff0c;其标的资产是股票市场上的股指&#xff0c;例如标普500指数、道琼斯工业平均指数、上证50指数等。 股指期货允…

前端构建工具大比拼:Vite、Webpack、Parcel、esbuild 等热门工具使用分析

前端构建工具大比拼&#xff1a;Vite、Webpack、Parcel、esbuild 等热门工具使用分析 随着前端技术的不断发展&#xff0c;构建工具成为了每个前端项目的核心部分。通过合适的构建工具&#xff0c;我们能够优化开发效率、提升构建速度&#xff0c;并最终实现更加高效和灵活的开…

安装和使用 Ollama(实验环境windows)

下载安装 下载 https://ollama.com/download/windows 安装 Windows 安装 如果直接双击 OllamaSetup.exe 安装&#xff0c;默认会安装到 C 盘&#xff0c;如果需要指定安装目录&#xff0c;需要通过命令行指定安装地址&#xff0c;如下&#xff1a; # 切换到安装目录 C:\Use…

node.js使用mysql2对接数据库

一、引言 在现代Web开发中&#xff0c;Node.js作为一种高效、轻量级的JavaScript运行时环境&#xff0c;已经广泛应用于后端服务的开发中。而MySQL&#xff0c;作为一个广泛使用的关系型数据库管理系统&#xff08;RDBMS&#xff09;&#xff0c;提供了强大的数据存储和查询功能…