c# 数据结构 链表篇 有关单链表的一切

news2025/4/17 6:52:41

        本人能力有限,本文仅作学习交流与参考,如有不足还请斧正

目录

0.单链表好处

0.5.单链表分类

1.无虚拟头节点情况

图示:

代码:

头插/尾插

删除

搜索

遍历全部

测试代码:

全部代码

2.有尾指针情况

尾插

全部代码

3.有虚拟头节点情况

全部代码

4.循环单链表

几个特别说明的点

增加时 更新环结构

删除时 删的头节点

非删头节点 注意遍历终止条件

全部代码


 

0.单链表好处

优点说明 / 场景
动态内存分配节点按需创建,无需预先指定固定大小,避免数组的空间浪费或溢出(如数据量不确定时)
高效增删操作插入 / 删除只需修改指针(平均 O(1) 时间复杂度),尤其适合频繁增删场景(如栈、队列)
内存分散存储节点可存储在非连续内存中,适应碎片化内存,利用零散空间(对比数组需连续内存)
灵活动态长度长度可随时增减,无需扩容 / 缩容(如动态缓冲区、链表队列)
实现简单轻量节点结构简单(数据 + 指针),代码易理解,适合入门学习及快速实现基础数据结构
适合链式场景支持链表特有操作(反转、合并、判环等),常用于哈希表拉链法、操作系统进程调度等

0.5.单链表分类

分类维度类型核心特点典型场景 / 优势
头节点无头节点头指针直接指向第一个数据节点,需处理头节点边界条件简单场景,代码稍繁琐
 有头节点头节点为虚拟节点,简化插入 / 删除操作通用场景,代码更简洁
循环非循环尾节点 next 为 null,遍历有终点大多数基础场景
 循环尾节点 next 指向头节点,形成环循环遍历、约瑟夫环等问题
辅助指针无尾指针尾插需遍历链表(\(O(n)\))尾插操作较少的场景
 带尾指针尾插直接通过尾指针操作(\(O(1)\))频繁尾插的场景

        下无特殊说明 皆为非循环单链表 

1.无虚拟头节点情况

                                  请注意 无头节点的意思是没有虚拟头节点

                        而下所说的headNdoe代表的是实际数据第一个节点      

                            单链表 = 实际数据头节点 + (节点 1 + 节点 2 + … + 节点 n)                                            其中,每个节点的定义为:      节点 = 数据 + 指向下一个节点的指针

图示:

注意 因为写c#的时候使用指针需要注意下列问题:

所以指向下一个节点的指针 定义为Node类 也就是Node本身

代码:

class Node { 

    public int value;
    public Node nextNode;

    public Node(int value, Node nextNode) {
        this.value = value;
        this.nextNode = nextNode;
    }
}

头插/尾插

           头插的精髓:每一次插入新node的时候 就把旧headNode作为nextNode,然后改变head的指向即可
        

class LinkeList {

    public Node headNode;
        
    public LinkeList() {
        headNode = null;
    }
    //头插: 新节点的next指向头节点,然后将头节点指向新节点
    public void AddToHead(int value) { 
        //创建一个新节点 并把原来的头节点放到后面去 这就是头插法的精髓
        Node newNode = new Node(value, headNode);
        //将头节点指向新节点
        headNode = newNode;
    }
}

        尾插精髓:遍历到最后一个节点 将该节点NextNode指向新Node

        

public void AddToTail(int value) {
    //创建一个新节点
    Node newNode = new Node(value, null);
    //如果链表为空,则直接将新节点作为头节点
    if (headNode == null)
        headNode = newNode;
    else { 
        //遍历到最后一个节点
        Node currentNode = headNode;
        while (currentNode.nextNode != null)
            { currentNode = currentNode.nextNode; }

        currentNode.nextNode = newNode;
    }
}

删除

        按值删除:单值

        精髓:删除不是让你真删掉,而是将Node的指针置null 这样gc的时候就自动回收了

        找到需要删除的节点的上一个节点,将其nextNode = 要删除节点的下一个Node

        

    //按值删除
    public void RemoveForValue(int value) {
        //如果链表为空,则直接返回
        if (headNode == null)
            return;
        //如果头节点就是要删除的节点,则直接将头节点指向目标的下一个节点
        //相当于断开了原来的头节点 使其无用
        if (headNode.value == value) {
            headNode =headNode.nextNode;
            return;
        }

        //遍历链表 
        Node currentNode = headNode;
        while (currentNode!=null&&currentNode.nextNode != null)
        {
            if (currentNode.nextNode.value != value)
                currentNode = currentNode.nextNode;
            else
                currentNode.nextNode = currentNode.nextNode.nextNode;
        }
    }

        按值删除:删除所有匹配到的重复值

 public void RemoveForValue(int value)
    {
        // 1. 处理头节点的所有重复值(用while循环替代if)
        while (headNode != null && headNode.value == value)
        {
            headNode = headNode.nextNode; // 连续删除头节点中的重复值
        }

        // 2. 遍历删除中间和尾节点的重复值
        Node currentNode = headNode;
        while (currentNode != null)
        {
            // 检查当前节点的下一个节点是否是目标值(避免漏判尾节点)
            while (currentNode.nextNode != null && currentNode.nextNode.value == value)
            {
                currentNode.nextNode = currentNode.nextNode.nextNode; // 删除下一个节点(重复值)
            }
            currentNode = currentNode.nextNode; // 移动到下一个非重复值节点
        }
    }

 

        按节点删除 略 

        这个你知道有这么回事就行了 一般不会用到 因为他在使用的时候需要声明要删除的Node 所以从用户角度来看就不太友好 不建议使用

搜索

         按值遍历

            精髓:没有精髓 遍历按值打印即可

    public bool SerachValue(int value)
    {
        if (headNode == null) { Console.WriteLine("链表为空 无法找到指定值"); return false; }

        Node currentNode = headNode;
        while (currentNode != null&& currentNode.nextNode != null)
        {
            if (currentNode.value == value)
            {
                Console.WriteLine("包含指定值" + value);
                return true;
            }
            else {
                currentNode = currentNode.nextNode;
            }
            
        }
        Console.WriteLine("链表内没有指定值" + value);
        return false;
    }

遍历全部

        精髓:没有精髓 遍历按值打印即可

    public void PrintAllValue() {
        if (headNode == null) return;

        Node currentNode = headNode;
        while (currentNode!= null)
        {
            Console.WriteLine(currentNode.value);
            currentNode = currentNode.nextNode;
        }
    }

测试代码:

LinkeList linke = new LinkeList();
linke.AddToHead(2);
linke.AddToHead(1);
linke.AddToTail(3);
//1 2 3
linke.RemoveForValue(2);
//1 3
Console.WriteLine(linke.SerachValue(2));//false
Console.WriteLine(linke.SerachValue(1));//true

linke.PrintAllValue(); // 1 3

全部代码


using System.Buffers;

LinkeList linke = new LinkeList();
linke.AddToHead(2);
linke.AddToHead(1);
linke.AddToTail(3);
//1 2 3
linke.RemoveForValue(2);
//1 3
Console.WriteLine(linke.SerachValue(2));//false
Console.WriteLine(linke.SerachValue(1));//true

linke.PrintAllValue(); // 1 3


/// <summary>
/// 链表节点应该包含 值 和 指针
/// </summary>
class Node { 

    public int value;
    public Node nextNode;

    public Node(int value, Node newNode) {
        this.value = value;
        this.nextNode = newNode;
    }
}
class LinkeList {

    public Node headNode;
        
    public LinkeList() {
        headNode = null;
    }
    #region Add
    //头插: 新节点的next指向头节点,然后将头节点指向新节点
    public void AddToHead(int value)
    {
        //创建一个新节点 并把原来的头节点放到后面去 这就是头插法的精髓
        Node newNode = new Node(value, headNode);
        //将头节点指向新节点
        headNode = newNode;
    }
    //尾插
    public void AddToTail(int value)
    {
        //创建一个新节点
        Node newNode = new Node(value, null);
        //如果链表为空,则直接将新节点作为头节点
        if (headNode == null)
            headNode = newNode;
        else
        {
            //遍历到最后一个节点
            Node currentNode = headNode;
            while (currentNode.nextNode != null)
            { currentNode = currentNode.nextNode; }

            currentNode.nextNode = newNode;
        }
    }
    #endregion
    #region Remove
    //按值删除
    public void RemoveForValue(int value) {
        //如果链表为空,则直接返回
        if (headNode == null)
            return;
        //如果头节点就是要删除的节点,则直接将头节点指向目标的下一个节点
        //相当于断开了原来的头节点 使其无用
        if (headNode.value == value) {
            headNode =headNode.nextNode;
            return;
        }

        //遍历链表 
        Node currentNode = headNode;
        while (currentNode!=null&&currentNode.nextNode != null)
        {
            if (currentNode.nextNode.value != value)
                currentNode = currentNode.nextNode;
            else
                currentNode.nextNode = currentNode.nextNode.nextNode;
        }
    }

    #endregion

    #region Search
    public bool SerachValue(int value)
    {
        if (headNode == null) { Console.WriteLine("链表为空 无法找到指定值"); return false; }

        Node currentNode = headNode;
        while (currentNode != null&& currentNode.nextNode != null)
        {
            if (currentNode.value == value)
            {
                Console.WriteLine("包含指定值" + value);
                return true;
            }
            else {
                currentNode = currentNode.nextNode;
            }
            
        }
        Console.WriteLine("链表内没有指定值" + value);
        return false;
    }

    #endregion

    #region 遍历打印
    public void PrintAllValue() {
        if (headNode == null) return;

        Node currentNode = headNode;
        while (currentNode!= null)
        {
            Console.WriteLine(currentNode.value);
            currentNode = currentNode.nextNode;
        }
    }

    #endregion
}

2.有尾指针情况

        这个的特别之处在于尾巴辅助的话 尾插不用遍历到最后尾巴

        初始化的时候需要注意一下

尾插

class LinkeList
{
    public Node headNode;
    public Node tailNode;


    public LinkeList()
    {
        headNode = tailNode = null;
    }
}
    // 尾插
    public void AddToTail(int value)
    {
        Node newNode = new Node(value);

        if (headNode == null)
            headNode = tailNode = newNode;

        tailNode.nextNode = newNode;
        tailNode = newNode;
    }

         其他的就没什么了和无虚拟头节点的代码和方法几乎是一样的

全部代码


using System.Diagnostics;

LinkeList linkeList = new LinkeList();
linkeList.AddToHead(2);
linkeList.AddToHead(1);
linkeList.AddToTail(3);
linkeList.RemoveForValue(3);
linkeList.SerachValue(2);
linkeList.SerachValue(3);
linkeList.PrintAllValue();
class Node
{
    public int value;
    public Node nextNode;

    public Node(int value, Node newNode = null)
    {
        this.value = value;
        this.nextNode = newNode;
    }
}

class LinkeList
{
    public Node headNode;
    public Node tailNode;


    public LinkeList()
    {
        headNode = tailNode = null;
    }

    #region Add
    // 头插
    public void AddToHead(int value)
    {
        Node newNode = new Node(value,headNode);
        if (headNode == null) 
            headNode = tailNode = newNode;

        headNode = newNode;
    }

    // 尾插
    public void AddToTail(int value)
    {
        Node newNode = new Node(value);

        if (headNode == null)
            headNode = tailNode = newNode;

        if(tailNode!= null)
            tailNode.nextNode = newNode;
        else
            tailNode = newNode;
    }
    #endregion

    #region Remove
    // 按值删除:双向查找 删除第一个找到的值
    public void RemoveForValue(int value)
    {
        //头空 直接返回
        if (headNode == null)
            return;

        //只有一个头
        if (headNode.value == value)
        {
            if (headNode.nextNode == null)
                headNode = tailNode = null;
                    return;
        }
        Node currentNode = headNode;
        while (currentNode!=null && currentNode.nextNode != null)
        {
            //如果下一个节点的值等于要删除的值
            if (currentNode.nextNode.value == value) {
                //在尾巴上 就更新尾巴
                if (currentNode.nextNode == tailNode)
                {
                    tailNode = currentNode;
                }
                //不在尾巴上 就干掉下一个节点
                currentNode.nextNode = currentNode.nextNode.nextNode;
            }else
            currentNode = currentNode.nextNode;
        }
    }
    #endregion

    #region Search
    public bool SerachValue(int value)
    {

        if (headNode == null)
            return false;

        Node currentNode = headNode;
        while (currentNode != null && currentNode.nextNode != null)
        {
            //如果下一个节点的值等于要删除的值
            if (currentNode.nextNode.value == value)
            {
                Console.WriteLine("找到了目标值"+value);
                return true;
            }
            else
                currentNode = currentNode.nextNode;
        }
        Console.WriteLine("没找到了目标值" + value);
        return false; 
    }
    #endregion

    #region 遍历打印

    public void PrintAllValue()
    {
        Node currentNode = headNode;
        while (currentNode != null)
        {
            Console.WriteLine(currentNode.value);
            currentNode = currentNode.nextNode;
        }
    }

    #endregion
}

3.有虚拟头节点情况

        我认为其没有什么特别的含义 只是省去了头节点为null的判断 我截图对比一下

左无头 右有头

全部代码

using System;
LinkeList linke = new LinkeList();
linke.AddToHead(2);
linke.AddToHead(1);
linke.AddToTail(3);
// 1 2 3
linke.RemoveForValue(2);
// 1 3
Console.WriteLine(linke.SerachValue(2));// false
Console.WriteLine(linke.SerachValue(1));// true

linke.PrintAllValue(); // 1 3
/// <summary>
/// 链表节点应该包含 值 和 指针
/// </summary>
class Node
{
    public int value;
    public Node nextNode;

    public Node(int value, Node newNode = null)
    {
        this.value = value;
        this.nextNode = newNode;
    }
}

class LinkeList
{
    // 虚拟头节点
    private Node dummyHead;
    public LinkeList()
    {
        // 初始化虚拟头节点
        dummyHead = new Node(0);
    }

    #region Add
    // 头插: 新节点的next指向虚拟头节点的下一个节点,然后将虚拟头节点的next指向新节点
    public void AddToHead(int value)
    {
        Node newNode = new Node(value, dummyHead.nextNode);
        dummyHead.nextNode = newNode;
    }

    // 尾插
    public void AddToTail(int value)
    {
        Node newNode = new Node(value);
        Node currentNode = dummyHead;
        while (currentNode.nextNode != null)
        {
            currentNode = currentNode.nextNode;
        }
        currentNode.nextNode = newNode;
    }
    #endregion

    #region Remove
    // 按值删除
    public void RemoveForValue(int value)
    {
        Node currentNode = dummyHead;
        while (currentNode != null && currentNode.nextNode != null)
        {
            if (currentNode.nextNode.value == value)
            {
                currentNode.nextNode = currentNode.nextNode.nextNode;
            }
            else
            {
                currentNode = currentNode.nextNode;
            }
        }
    }
    #endregion

    #region Search
    public bool SerachValue(int value)
    {
        Node currentNode = dummyHead.nextNode;
        while (currentNode != null)
        {
            if (currentNode.value == value)
            {
                Console.WriteLine("包含指定值" + value);
                return true;
            }
            currentNode = currentNode.nextNode;
        }
        Console.WriteLine("链表内没有指定值" + value);
        return false;
    }
    #endregion

    #region 遍历打印
    public void PrintAllValue()
    {
        Node currentNode = dummyHead.nextNode;
        while (currentNode != null)
        {
            Console.WriteLine(currentNode.value);
            currentNode = currentNode.nextNode;
        }
    }
    #endregion
}

4.循环单链表

        我直接用情况2 的代码改的 核心在于:

  1. 尾节点的 nextNode 指向头节点(形成环)
  2. 遍历 / 搜索时通过头节点判断终止条件(避免死循环)
  3. 维护头尾指针的环结构一致性

        你要是问都循环了 还区分头尾节点有必要吗?

        有的兄弟,有的 这样头尾插都是O1

几个特别说明的点

增加时 更新环结构

   // 尾插法:新节点的next指向头节点,原尾节点的next指向新节点,更新尾节点
   public void AddToTail(int value)
   {
       Node newNode = new Node(value, headNode); // 新节点的next指向头节点(形成环)
       if (tailNode == null)
       {
           // 空链表:头尾节点指向新节点,自环
           headNode = tailNode = newNode;
           newNode.nextNode = newNode;
       }
       else
       {
           tailNode.nextNode = newNode; // 原尾节点连接新节点
           tailNode = newNode; // 尾节点更新为新节点
       }
   }

删除时 删的头节点

 public void RemoveForValue(int value)
 {
     if (headNode == null) return;

     // 情况1:删除头节点
     if (headNode.value == value)
     {
         if (headNode == tailNode) // 只有一个节点
         {
             headNode = tailNode = null; // 环断开
         }
         else // 多个节点,头节点后移,尾节点的next指向新头节点
         {
             headNode = headNode.nextNode;
             tailNode.nextNode = headNode; // 尾节点保持环结构
         }
         return;
     }
        .................
    
}

非删头节点 注意遍历终止条件

 while (previous.nextNode != headNode)
 {
     if (previous.nextNode.value == value)
     {
         Node target = previous.nextNode;
         if (target == tailNode)
         {
             tailNode = previous;
             tailNode.nextNode = headNode;
         }
         else
         {
             previous.nextNode = target.nextNode;
         }
     }
     else
     {
         previous = previous.nextNode;
     }
 }

全部代码

class Node
{
    public int value;
    public Node nextNode;

    public Node(int value, Node nextNode = null)
    {
        this.value = value;
        this.nextNode = nextNode;
    }
}

class CircularLinkedList
{
    public Node headNode;
    public Node tailNode;

    public CircularLinkedList()
    {
        headNode = tailNode = null;
    }

    // 头插法
    public void AddToHead(int value)
    {
        Node newNode = new Node(value, headNode);
        if (headNode == null)
        {
            headNode = tailNode = newNode;
            newNode.nextNode = newNode;
        }
        else
        {
            tailNode.nextNode = newNode;
            headNode = newNode;
        }
    }

    // 按值删除节点
    public void RemoveForValue(int value)
    {
        if (headNode == null) return;

        // 处理头节点是要删除的值的情况
        while (headNode != null && headNode.value == value)
        {
            if (headNode == tailNode)
            {
                headNode = tailNode = null;
                return;
            }
            headNode = headNode.nextNode;
            tailNode.nextNode = headNode;
        }

        Node previous = headNode;
        while (previous.nextNode != headNode)
        {
            if (previous.nextNode.value == value)
            {
                Node target = previous.nextNode;
                if (target == tailNode)
                {
                    tailNode = previous;
                    tailNode.nextNode = headNode;
                }
                else
                {
                    previous.nextNode = target.nextNode;
                }
            }
            else
            {
                previous = previous.nextNode;
            }
        }
    }

    // 遍历打印链表
    public void PrintAllValue()
    {
        if (headNode == null) return;
        Node current = headNode;
        do
        {
            Console.WriteLine(current.value);
            current = current.nextNode;
        } while (current != headNode);
    }
}

 

 

 

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

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

相关文章

VS Code连接服务器编写Python文件

1、下载 Visual Studio Code 2、打开扩展&#xff08;ctrl shift x ) 3、搜索 Remote - SSH&#xff0c;安装 4、F1 或者 点金左下角 5、选择&#xff1a;Remote-SSH: Connect to Host……&#xff0c;回车 6、第一次用的时候&#xff0c;VS Code 会提示添加 SSH 主机。输…

Gitea的安装和配置以及应用

Gitea的安装和配置以及应用 一、安装 1、创建数据库和数据库账户&#xff08;pg&#xff09; su – postgres -c "psql" CREATE ROLE gitea WITH LOGIN PASSWORD gitea; CREATE DATABASE giteadb WITH OWNER gitea TEMPLATE template0 ENCODING UTF8 LC_COLLATE …

$_GET变量

$_GET 是一个超级全局变量&#xff0c;在 PHP 中用于收集通过 URL 查询字符串传递的参数。它是一个关联数组&#xff0c;包含了所有通过 HTTP GET 方法发送到当前脚本的变量。 预定义的 $_GET 变量用于收集来自 method"get" 的表单中的值。 从带有 GET 方法的表单发…

TBE(TVM的扩展)

算子 张量 一个张量只有一种数据类型 在内存中只能线性存储&#xff0c;最终形成一个长的一维数组 晟腾AI的数据格式 AIPP是对我们常见的数据格式转化成AI core支持的数据格式 广播机制 TVM TBE的第一种开发方式&#xff1a;DSL TBE的第二种开发方式&#xff1a;TVM TBE的第…

【Function Calling与Tool Calling】深度解析大模型智能中枢的架构革命

目录 一、范式转移&#xff1a;从对话引擎到智能中枢 二、核心技术解析 2.1 Function Calling技术栈 2.2 Tool Calling实现模式 三、企业级应用架构设计 3.1 智能工单系统案例 3.2 性能优化策略 四、安全与治理框架 4.1 权限控制矩阵 4.2 审计追踪设计 五、开发者实…

知识表示方法之六:过程表示法(Procedural Representation)

在人工智能的发展史中&#xff0c;关于知识的表示方法曾存在两种不同的观点。一种观点认为知识主要是陈述性的&#xff0c;其表示方法应着重将其静态特性&#xff0c;即事物的属性以及事物间的关系表示出来&#xff0c;称以这种观点表示知识的方法为陈述式或说明式表示法&#…

sql-labs靶场 less-2

文章目录 sqli-labs靶场less 2 联合注入 sqli-labs靶场 每道题都从以下模板讲解&#xff0c;并且每个步骤都有图片&#xff0c;清晰明了&#xff0c;便于复盘。 sql注入的基本步骤 注入点注入类型 字符型&#xff1a;判断闭合方式 &#xff08;‘、"、’、“”&#xf…

git clone(复制)下载

1、复制 下载地址 2、打开网页&#xff0c;点击 克隆/下载按扭 3、按提示复制命令行到终端 4、VS里打开终端&#xff0c;并粘贴以下命令 5、 下载完毕 6、复制文件夹到你选定的位置 7、用VSCODE打开文件夹&#xff0c;开始你接下来的工作

Android设置adjustResize时无法生效 解决办法

删除Activity类下执行全屏的一行参数。 将图中这段Activity类中执行命令给删除就解决了。 注意关闭后状态栏和导航栏的透明度就无法自动处理了&#xff0c;需要到values和values-night下的themes.xml手动设置状态栏背景颜色。 <item name"android:statusBarColor"…

按键长按代码

这些代码都存放在定时器中断中。中断为100ms中断一次。 数据判断&#xff0c;看的懂就看吧

优选算法第八讲:链表

优选算法第八讲&#xff1a;链表 1.链表常用操作和技巧总结2.两数相加3.两两交换链表中的节点4.重排链表5.合并k个升序链表6.k个一组翻转链表 1.链表常用操作和技巧总结 2.两数相加 3.两两交换链表中的节点 4.重排链表 5.合并k个升序链表 6.k个一组翻转链表

4S店汽车维修保养管理系统 (源码+lw+部署文档+讲解),源码可白嫖!

摘要 二十一世纪我们的社会进入了信息时代&#xff0c;信息管理系统的建立&#xff0c;大大提高了人们信息化水平。传统的管理方式已经与当今4S店汽车维修保养管理系统的业务需求不相适应,也与4S店汽车维修保养管理系统化建设的发展趋势不相适应。本文针对这一需求设计并实现了…

【NLP 面经 8】

目录 一、文本生成任务 模型架构方面 训练数据方面 生成策略方面 二、命名实体识别任务NER 模型架构方面 特征工程方面 训练优化方面 三、情感分析任务 模型架构方面 训练数据方面 超参数调整方面 四、计算余弦相似度并添加符合条件结果 提示&#xff1a; 思路与算法 任由深渊的…

UE5学习笔记 FPS游戏制作43 UI材质

文章目录 实现目标制作UI材质使用UI材质 实现目标 把图片变为灰色 制作UI材质 右键新建一个材质 左侧细节栏&#xff0c;材质域改为用户界面&#xff0c;混合模式改为半透明 此时输出节点应该有两个属性 在内容浏览器里找到要用的图片&#xff0c;然后向上拖动到材质标题…

12、主频和时钟配置实验

一、I.MX6U 时钟系统详解 1、系统时钟来源 开发板的系统时钟来源于两部分: 32.768KHz 和24MHz 的晶振,其中 32.768KHz 晶振是 I.MX6U 的 RTC 时钟源, 24MHz 晶振是 I.MX6U 内核和其它外设的时钟源。 2、7路PLL时钟源 I.MX6U 的外设有很多,不同的外设时钟源不同, NXP 将…

2025 年河北交安安全员考试:巧用行业报告丰富知识储备​

河北交通行业发展迅速&#xff0c;各类行业报告蕴含大量有价值信息。考生可从河北省交通运输行业发展报告、安全专项检查报告等资料入手。在行业发展报告中&#xff0c;了解本省交通建设规模、重点项目规划等内容&#xff0c;这些信息与交安安全员工作紧密相关。比如&#xff0…

​2025快递业绿色包装创新发展论坛(上海)将于9月3日举办

​2025快递业绿色包装创新发展论坛&#xff08;上海&#xff09;将于9月3日举办 为进一步推进快递包装标准化、循环化、减量化、无害化转型&#xff0c;促进邮政快递业绿色低碳发展&#xff0c;在“2025 快递物流新质生产力发展论坛暨 2025 长三角国际快递物流供应链与智能装备…

stm32+esp8266+机智云手机app

现在很多大学嵌入式毕设都要求云端控制&#xff0c;本文章就教一下大家如何使用esp8266去连接机智云的app去进行显示stm32的外设传感器数据啊&#xff0c;控制一些外设啊等。 因为本文章主要教大家如何移植机智云的代码到自己的工程&#xff0c;所以前面的一些准备工作&#x…

【android bluetooth 框架分析 01】【关键线程 1】【关键线程介绍】

1. 为什么学习蓝牙协议栈之前&#xff0c;必须先梳理清楚这几大线程&#xff1f; 为什么 学习协议栈之前 最好是要先梳理清楚 关键线程 bt_stack_manager_threadbt_jni_threadbt_main_threadbt_a2dp_sink_worker_thread 1.1 蓝牙协议栈是典型的“多线程异步系统” 蓝牙协议…

CAS与sychronized优化

文章目录 CAS优化sychronized优化 CAS优化 CAS 优化过程: CAS是一种乐观锁机制&#xff0c;它通过比较内存中的值与预期值是否一致来决定是否更新。如果一致&#xff0c;则更新&#xff1b;如果不一致&#xff0c;则说明有其他线程修改了该值&#xff0c;CAS操作失败&#xff…