链表算法题目

1.两数相加

两个非空链表,分别表示两个整数,只不过是反着存储的,即先存储低位在存储高位。要求计算这两个链表所表示数的和,然后再以相同的表示方式将结果表示出来。如示例一:两个数分别是342和465,和为807,但是我们要逆序存储,所以结果链表就是708.

解法:模拟

我们只需要用两个指针同时遍历两个链表,取出相加,相加的值刚好作为新链表的头。再遍历下一个节点的时候,只需要将结果尾插到新链表后即可。

但因为每一个节点只能存储个位数,所以我们在储存时要对相加的结果进行取模,而且两数相加还有可能遇到进位的情况,所以我们还要保存进位,因为进位是在下一位相加时才有用,所以要注意取模和求进位的先后顺序。

我们会遇到那些位数不同的数字相加,要注意循环的结束条件,只有一个为空时,我们仍需要继续遍历,空的那个只需要让其默认是0即可,这样也不会引起问题。当然,当计算到最后一位时,仍有可能进位,但是两个链表都空了,此时直接返回的话,就会出错。所以我们也要保证进位不为0

时间复杂度:O(max(n1,n2)),只需要遍历一遍链表,n1,n2分别是两个链表的长度

空间复杂度:O(1),我们申请了一个哨兵位的头节点,但是再最后释放掉了

// C++/*** Definition for singly-linked list.* struct ListNode {*     int val;*     ListNode *next;*     ListNode() : val(0), next(nullptr) {}*     ListNode(int x) : val(x), next(nullptr) {}*     ListNode(int x, ListNode *next) : val(x), next(next) {}* };*/
class Solution 
{
public:ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) {ListNode* newhead = new ListNode;// 哨兵头节点ListNode* tail = newhead; // 尾节点// 遍历链表指针ListNode* cur1 = l1;ListNode* cur2 = l2;int carry = 0;// 如果两个链表还有一个没有走完还得继续走// 如果链表都走完了,但是要记得检查进位,判断是否还有剩余进位while(cur1 || cur2 || carry){// 获取节点的值,如果节点为空则默认该节点的值为0,不影响加的结果carry += cur1 ? cur1->val : 0;carry += cur2 ? cur2->val : 0;// carry的个位就是该节点要存储的,不要忘了保存进位ListNode* newnode = new ListNode(carry % 10);carry /= 10;// 尾插tail->next = newnode;tail = newnode;// 移动cur1/2到下一个节点cur1 = cur1 ? cur1->next : cur1 = nullptr;cur2 = cur2 ? cur2->next : cur2 = nullptr;}// newhead是new出来的,避免内存泄露,在返回之前将其delete掉tail = newhead->next;delete newhead;return tail;}
};

2.链表相加2

这道题与上面很像,这不过这里的链表是按照高位到地位,返回的结果也是按照高位到地位。 

解法

先将两个链表逆置,然后再利用上一题的方法进行相加并得出逆序的结果,最后再将和链表逆置就可以得出答案。

时间复杂度:O(n)

空间复杂度:O(1)

class Solution 
{
public:ListNode* addInList(ListNode* head1, ListNode* head2) {if((head1 == nullptr && head2 == nullptr) || (head1 == nullptr && head2)) return head2;if(head1 && head2 == nullptr) return head1;// 1.先将原链表进行逆置 // 头插法ListNode* newhead1 = new ListNode(0);ListNode* newhead2 = new ListNode(0);while(head1 || head2){if(head1){ListNode* next = head1->next;head1->next = newhead1->next;newhead1->next = head1;head1 = next;}if(head2){ListNode* next = head2->next;head2->next = newhead2->next;newhead2->next = head2;head2 = next;}}// 2.同时遍历两个逆置链表,进行加法运算ListNode* ret = new ListNode(0);ListNode* cur1 = newhead1->next;ListNode* cur2 = newhead2->next;int carry = 0;while(cur1 || cur2 || carry){carry += cur1?cur1->val:0;carry += cur2?cur2->val:0;ListNode* newNode = new ListNode(carry%10);carry /= 10;// 头插法,将和链表逆置回来newNode->next = ret->next;ret->next = newNode;cur1 = cur1?cur1->next:nullptr;cur2 = cur2?cur2->next:nullptr;}cur1 = ret->next;delete ret;delete newhead1;delete newhead2;return cur1;}
};

3.反转链表

 解法1:头插法

我们定义一个哨兵头节点,然后遍历原链表,将原链表的节点都头插到哨兵头节点的后面。

时间复杂度:O(n)

空间复杂度:O(1) 

ListNode* _ReverseList(ListNode* head)
{// 头插法反转链表if (!head || !head->next) return head;ListNode* ret = new ListNode(0);while (head){ListNode* next = head->next;head->next = ret->next;ret->next = head;head = next;}head = ret->next;delete ret;return head;
}

 解法2:双指针:

借助两个指针cur1和cur2,在原链表上修改指针的指向来实现逆置。cur2指向头节点,cur1指向空,然后用cur2遍历原链表,遍历的同时,让cur2的next指向cur1,实现逆置,然后让cur1走到cur2的位置,然后cur2走到下一个位置。在这个过程中需要用一个next指针提前记录cur2的next位置。

时间复杂度:O(n)

空间复杂度:O(1) 

ListNode* __ReverseList(ListNode* head)
{// 双指针反转链表if (!head || !head->next) return head;ListNode* cur1 = nullptr;ListNode* cur2 = head;while (cur2){ListNode* next = cur2->next;cur2->next = cur1;cur1 = cur2;cur2 = next;}return cur1;
}

 解法3:递归

首先借助递归找到尾节点,以该节点为头节点开始返回。返回的过程中,将该层的head的next的next指针指向自己即head的前一个节点的next指针指向自己,此时就达到了后一个指向前一个,接着将head的next指针指向空,避免成环。接着继续递归返回。

下面是链表递归到尾节点第一次返回的时候:

等到下一层时,head就到了2节点,此时再让前一个节点的next指向自己,在将自己的next置为空,就将2号节点连接到了ans链表中。 

 时间复杂度:O(n)

空间复杂度:O(n),递归会消耗栈空间,递归所需的栈空间就是链表的长度

ListNode* ReverseList(ListNode* head)
{// 递归反转链表if (!head || !head->next) return head;ListNode* ans = ReverseList(head->next);head->next->next = head;head->next = nullptr;return ans;
}

4.两两交换链表中的节点

 解法1:哨兵头节点+快慢指针

利用快慢双指针遍历原链表,让fast指针先尾插到哨兵头节点后,接着尾插slow,重复该过程。在遍历的过程中,如果原链表是奇数个,最后一次slow不为空,fast为空,退出循环,直接将slow尾插即可;如果原链表是偶数个,最后一个slow和fast都不为空,尾插结束后,slow走到空,循环结束。但记得最后让待返回的链表的尾节点指向空,否则答案可能会错误!

时间复杂度:O(n),只遍历了一遍链表

空间复杂度:O(1)

 // 借助哨兵头节点+快慢指针ListNode* swapPairs(ListNode* head) {// 链表为空/链表只有一个节点,直接返回if(head == nullptr || head->next == nullptr) return head;// 创建虚拟头节点ListNode* virtualHead = new ListNode;ListNode* tail = virtualHead;// 定义快慢指针ListNode* slow = head;ListNode* fast = slow->next;while(fast && slow){ListNode* _fast = fast;ListNode* _slow = slow;slow = fast->next;if(slow)fast = slow->next;tail->next = _fast;tail = _fast;tail->next = _slow;tail = _slow;} if(slow){tail->next = slow;tail = slow;}tail->next = nullptr;tail = virtualHead->next;delete virtualHead;return tail;}

 解法2:哨兵头节点+原地修改链表指向

首先将哨兵头节点连接到原链表头部,接着定义4个指针prev,cur,next,nnext,从哨兵开始依次指向一个节点。接下来只需要根据图示修改指针指向即可:

prev->next = next; next->next = cur;cur->next = nnext;

修改一次之后,cur直接指向第3个节点——nnext,其他的三个指针分别修改好,继续修改指向。 在修改next和nnext指针时要注意空指针解引用的问题。

结束条件:当cur遍历到空时,说明已经没有节点需要交换了;当next指向空时,说明只剩cur一个节点了,此时也不需要交换。

时间复杂度:O(n),每个节点都被访问一次

空间复杂度:O(1)

 ListNode* swapPairs(ListNode* head) {// 链表为空/链表只有一个节点,直接返回if(head == nullptr || head->next == nullptr) return head;// 创建虚拟头节点ListNode* virtualHead = new ListNode;virtualHead->next = head;ListNode* prev = virtualHead;ListNode* cur = head;ListNode* next = cur->next;ListNode* nnext = next->next;while(cur && next){prev->next = next;next->next = cur;cur->next = nnext;prev = cur;cur = nnext;if(cur) next = cur->next;if(next) nnext = next->next;}cur = virtualHead->next;delete virtualHead;return cur;}

 解法3:递归

我们将节点两个两个分组,并且我们确保在交换前两个节点时,后面的链表已经交换完成了,并且返回了一个新的头节点tmp。我们用新头节点newhead指向旧头节点oldhead,然后用oldhead指向后面已经完成交换的链表,将两个部分连接起来,其实newhead就是我们完成交换之后的头节点。

简单来说,对于原链表来说,每两个一组的节点,第一个节点是新链表的第二个节点,第二个节点是新链表的头节点。 

时间复杂度:O(n),每一个节点都被访问了

空间复杂度:O(n),递归所需要的栈空间,每两个节点需要一个函数栈帧

// 递归
ListNode* swapPairs(ListNode* head)
{if (head == nullptr || head->next == nullptr) return head;ListNode* newhead = head->next;ListNode* tmp = swapPairs(newhead->next); // 下层传上来的交换后的头节点newhead->next = head;head->next = tmp;return newhead;
}

5.重排链表

该题的意思其实是按照第一个倒数第一个,第二个倒数第二个...的方式将链表重新排列后返回。 

解法:

我们观察规律可以得出,重排后的链表其实是可以由两个链表合并得来的。而且这两个链表就是从原链表中间分开,前半部分和后半部分的逆置。

所以我们的解决策略就是先找出原链表的中心节点,接着将后半部分逆置,然后借助哨兵头节点,将这样个链表进行合并,合并时先尾插前半部分,再尾插后半部分。

1.对于寻找链表的中间节点,我们可以采取快慢双指针的方式,fast一次走两步,slow一次走一步,当fast走到空/尾时,slow的位置就是中间节点的位置。但是这种方法对于奇数链表来说,slow会停在中间位置,对于偶数链表,slow会停在中间靠右的节点。 

2.接下来的逆置我们既可以直接逆置slow及以后的部分,也可以逆置slow后面的部分。两者都是可以的。下面只给出偶数节点的例子,对于奇数节点个数也是成立的。

虽然两种方式都可以,但是这里建议将slow之后的链表进行逆置。因为这样可以将原链表断开,形成两个独立的链表,合并时更好处理。如果逆置slow及之后的话,无法将前后链表断开。 

3. 采取逆置slow之后,我们会得到两个独立的链表,接下来只需要合并即可。注意合并顺序:先前再后。

时间复杂度:O(n),虽然三次遍历链表,但整体上依旧是O(n)

空间复杂度:O(1) 

void reorderList(ListNode* head)
{// 当链表为空/一个节点/两个节点,都不需要发生变换,直接返回if (head == nullptr || head->next == nullptr || head->next->next == nullptr)return;// 1.找到链表的中间节点,采用快慢双指针// 奇数个slow会落在中间节点// 偶数个slow会落在中间靠右的节点ListNode* fast = head;ListNode* slow = head;while (fast && fast->next){fast = fast->next->next;slow = slow->next;}// 2.逆序后半部分链表——头插法// 这里采取逆序mid后面的部分,不包括mid节点ListNode* newhead = new ListNode;ListNode* mid = slow->next; // 先记录下需要逆序的部分slow->next = nullptr; // 将前半部分和需要逆序的部分断开while (mid){ListNode* next = mid->next;mid->next = newhead->next;newhead->next = mid;mid = next;}// 3.合并两个链表-head和newheadListNode* ret = new ListNode;ListNode* tail = ret;ListNode* cur1 = head, * cur2 = newhead->next;while (cur1){tail->next = cur1;tail = cur1;cur1 = cur1->next;if (cur2){tail->next = cur2;tail = cur2;cur2 = cur2->next;}}head = ret->next;delete newhead;delete ret;
}

6.合并K个升序链表

lists的每一个元素都是一个链表,我们需要将这样链表以升序的格式全部合并最终返回。

 解法1:两两合并

虽然有多个链表,但是我们可以两个两个合并,用合并的结果在于下一个合并,重复该过程,直到将所有的链表都合并了。

时间复杂度:O(nk^2),我们假设平均每个链表的节点数是n,有k个链表,第一个链表需要合并k-1次,第二个链表合并k-2次,最后一个链表合并1次。所以加起来就是n*k^2次。

空间复杂度:O(1)

// 1.两两合并
ListNode* _mergeKLists(vector<ListNode*>& lists)
{if (lists.size() == 0) return nullptr;else if (lists.size() == 1) return lists[0];ListNode* ret = lists[0];for (int i = 1; i < lists.size(); ++i){ListNode* tmp = new ListNode;ListNode* tail = tmp;ListNode* cur = lists[i];// 合并两个有序链表while (ret && cur){if (ret && cur && ret->val <= cur->val){tail->next = ret;tail = ret;ret = ret->next;}else if(ret && cur && ret->val > cur->val){tail->next = cur;tail = cur;cur = cur->next;}}while (ret){tail->next = ret;tail = ret;ret = ret->next;}while (cur){tail->next = cur;tail = cur;cur = cur->next;}// 此时cur和ret的和在tmp中,将tmp的值赋给ret// cur与第一次的和继续合并ret = tmp->next;delete tmp;}return ret;
}

 解法2:优先级队列——小根堆

我们可以定义k个指针指向每个链表的头,让这k个节点进入小堆,此时堆顶数据就是最小的,我们将他尾插到ret链表,接着删除堆顶数据,将堆顶元素所在链表的下一个节点push到堆中。重复该过程。

时间复杂度:O(nlogk),每一个节点插入到堆中是logk,因为要进行向下调整

空间复杂度:O(k),优先级队列中同时最多包含K个节点

// 2.借助priority_queue
ListNode* __mergeKLists(vector<ListNode*>& lists)
{// 借助小根堆,先将vector中的所有头节点放入堆中,此时堆顶节点的val值就是最小的。// 取出堆顶节点尾插到哨兵位头节点后,接着从堆中删除该节点,将该节点的next插入到堆中。// 堆不为空,持续该过程。// 再将节点插入到堆中时要判断该节点是否为空size_t n = lists.size();if (n == 0) return nullptr;else if (n == 1) return lists[0];ListNode* ret = new ListNode;ListNode* tail = ret;priority_queue < ListNode*, vector<ListNode*>, decltype([](ListNode* l1, ListNode* l2) {return l1->val > l2->val; }) > heap;for (auto l : lists)if (l) heap.emplace(l);while (!heap.empty()){ListNode* min = heap.top();heap.pop();tail->next = min;tail = min;if (min->next)heap.emplace(min->next);}tail = ret->next;delete ret;return tail;
}

解法三:分治——归并策略

我们可以采取归并排序的策略,将所有的链表分为两部分,先合并左边,在合并右边,最后将这两个合并好的链表再一合并即可。

时间复杂度:O(nklogk),划分的过程中高度是logk,一共有nk个节点

空间复杂度:O(logk),递归的深度是logk,消耗了logk的栈空间

// 3. 分治——归并策略ListNode* mergeTwoList(ListNode* cur1, ListNode* cur2){if(!(cur1) || !(cur2)) return !cur1 ? cur2 : cur1;ListNode ret(0);ListNode* tail = &ret;while(cur1 && cur2){if(cur1->val <= cur2->val){tail->next = cur1;tail = cur1;cur1 = cur1->next;}else{tail->next = cur2;tail = cur2;cur2 = cur2->next;}}tail->next = cur1?cur1:cur2;return ret.next;}ListNode* merge(vector<ListNode*>& lists, int left, int right){if(left>=right) return left == right ? lists[left] : nullptr;int mid = (right-left)/2+left;// [left, mid] [mid+1, right]ListNode* cur1 = merge(lists, left, mid); // 接收左边合并后的结果ListNode* cur2 = merge(lists, mid+1, right); // 接收右边合并后的结果return mergeTwoList(cur1, cur2); // 合并两个有序数组}ListNode* mergeKLists(vector<ListNode*>& lists){  return merge(lists,0,lists.size()-1);}

7.K个一组反转链表

给一个节点,在一个数k,将链表每k个为一组,进行反转,如果最后不足k个,就直接保留。

解法:模拟

1.我们可以首先求出需要反转多少次,n个节点,k个一组,那么就有n/k组,每组都要反转一次,一共反转n/k次。

2.头插法反转链表,需要注意,每进行一次反转要标记第一个头插到链表的节点,下一次反转将节点都插在标记节点的后面,而不是哨兵头结点的后面。

时间复杂度:O(n)

空间复杂度:O(1)

ListNode* reverseKGroup(ListNode* head, int k)
{if (!head || k == 1) return head;// 1.先算出需要进行几次逆序操作int n = 0;ListNode* cur = head;while (cur){n += 1;cur = cur->next;}n /= k;// 2.n次逆序ListNode* newhead = new ListNode;ListNode* tail = newhead;cur = head;while (n--){ListNode* tmp = tail;for (int i = 0; i < k; ++i){if (i == 0) tail = cur; // 标记每次反转的第一个节点ListNode* next = cur->next;cur->next = tmp->next;tmp->next = cur;cur = next;}}if (cur) tail->next = cur;tail = newhead->next;delete newhead;return tail;
}

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

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

相关文章

深入解析 BitBake 日志机制:任务调度、日志记录与调试方法

1. 引言&#xff1a;为什么 BitBake 的日志机制至关重要&#xff1f; BitBake 是 Yocto 项目的核心构建工具&#xff0c;用于解析配方、管理任务依赖&#xff0c;并执行编译和打包任务。在 BitBake 构建过程中&#xff0c;日志记录机制不仅用于跟踪任务执行情况&#xff0c;还…

C++--迭代器(iterator)介绍---主要介绍vector和string中的迭代器

目录 一、迭代器&#xff08;iterator&#xff09;的定义 二、迭代器的类别 三、使用迭代器 3.1 迭代器运算符 3.2 迭代器的简单应用&#xff1a;使用迭代器将string对象的第一个字母改为大写 3.3 将迭代器从一个元素移动到另外一个元素 3.4 迭代器运算 3.5 迭代器的复…

基于multisim的自动干手器设计与仿真

1 设计的任务与要求 设计一个输出 5V 的直流稳压电源。用开关的闭合模拟手挡住光线的功能。用灯的亮灭模拟烘干吹风功能。 2 方案论证与选择 2.1 自动干手器的系统方案 本设计由5V直流电源、红外发射电路、红外接收电路、灯模拟电路构成。 1. 5V直流电源系统 这一部分是整…

【算法学习之路】7.链表算法

链表算法 前言一.原地逆置思路一&#xff1a;头插法思路二&#xff1a;双指针法思路3&#xff1a;递归 例题&#xff1a;1.头插法2.双指针法3&#xff0c;递归 二.双指针快慢指针&#xff1a;一个指针快一个指针慢例题1例题2 前言 我会将一些常用的算法以及对应的题单给写完&am…

分布式锁—7.Curator的分布式锁

大纲 1.Curator的可重入锁的源码 2.Curator的非可重入锁的源码 3.Curator的可重入读写锁的源码 4.Curator的MultiLock源码 5.Curator的Semaphore源码 1.Curator的可重入锁的源码 (1)InterProcessMutex获取分布式锁 (2)InterProcessMutex的初始化 (3)InterProcessMutex.…

IPD(集成产品开发)简介

参考&#xff1a;IPD咨询_研发管理咨询_IPD集成产品开发-百思特管理咨询集团 一、什么是IPD IPD到底是什么&#xff1f;一套体系&#xff1f;一些流程&#xff1f;还是一种模式&#xff1f; 华为在整个企业内部改革中最重要的两个项目一个是ISC(集成供应链)&#xff0c;另外…

解决Jenkins默认终止Shell产生服务进程的问题

1、Windows环境 Jenkins进行Build steps的使用Execute Windows batch command启动微服务&#xff08;Jar包&#xff09;&#xff0c;Jenkins会默认终止Shell产生的服务进程&#xff0c;而在命令行能够正常运行的服务进程。 1.1 使用命令行启动服务是正常 使用命令行执行 正常…

C 语言数据结构(三):栈和队列

目录 1. 栈 1.1 栈的概念及结构 1.2 栈的实现 2. 队列 2.1 队列的概念及结构 2.2 队列的实现 &#x1f4ac; &#xff1a;如果你在阅读过程中有任何疑问或想要进一步探讨的内容&#xff0c;欢迎在评论区畅所欲言&#xff01;我们一起学习、共同成长~&#xff01; &#x…

多模态分子预训练模型 - SPMM 评测

SPMM 是 Structure-Property Multi-Modal foundation model 的简称&#xff0c;一种多模态分子性质-结构双向预训练模型。作者是韩国科学技术研究院人工智能研究生院&#xff0c;大田&#xff0c;韩国的 Jong Chul Ye&#xff0c;于 2024 年 3 月 14 日 发表在 nature communic…

算法.习题篇

算法 — 地大复试 模拟 while循环和MOD循环计数 1.约瑟夫问题 http://bailian.openjudge.cn/practice/3254 using namespace std;bool isNoPeople(vector<bool> c)//判断当前数组是否一个小孩都没有了 {bool nopeople true;for (bool ival : c){if ( ival true)nop…

C++第十节:map和set的介绍与使用

【本节要点】 1.关联式容器2.键值对3.map介绍与使用4.set介绍与使用5.multimap与multisedd的介绍与使用 一、关联式容器&#xff1a;数据管理的核心利器 关联式容器是STL中用于高效存储和检索键值对&#xff08;key-value pair&#xff09;的数据结构&#xff0c;其底层基于红黑…

Java 大视界 -- Java 大数据在智能家居能源管理与节能优化中的应用(120)

&#x1f496;亲爱的朋友们&#xff0c;热烈欢迎来到 青云交的博客&#xff01;能与诸位在此相逢&#xff0c;我倍感荣幸。在这飞速更迭的时代&#xff0c;我们都渴望一方心灵净土&#xff0c;而 我的博客 正是这样温暖的所在。这里为你呈上趣味与实用兼具的知识&#xff0c;也…

DeepSeek-R1本地化部署(Mac)

一、下载 Ollama 本地化部署需要用到 Ollama&#xff0c;它能支持很多大模型。官方网站&#xff1a;https://ollama.com/ 点击 Download 即可&#xff0c;支持macOS,Linux 和 Windows&#xff1b;我下载的是 mac 版本&#xff0c;要求macOS 11 Big Sur or later&#xff0c;Ol…

手写简易Tomcat核心实现:深入理解Servlet容器原理

目录 一、Tomcat概况 1. tomcat全局图 2.项目结构概览 二、实现步骤详解 2.1 基础工具包&#xff08;com.qcby.util&#xff09; 2.1.1 ResponseUtil&#xff1a;HTTP响应生成工具 2.1.2 SearchClassUtil&#xff1a;类扫描工具 2.1.3 WebServlet&#xff1a;自定义注解…

OpenHarmony子系统开发编译构建指导

OpenHarmony子系统开发编译构建指导 概述 OpenHarmony编译子系统是以GN和Ninja构建为基座&#xff0c;对构建和配置粒度进行部件化抽象、对内建模块进行功能增强、对业务模块进行功能扩展的系统&#xff0c;该系统提供以下基本功能&#xff1a; 以部件为最小粒度拼装产品和独…

红日靶场(一)——个人笔记

说明&#xff1a; 红日靶场官网 http://vulnstack.qiyuanxuetang.net/vuln/detail/2/ 靶场默认密码 hongrisec2019 预留空间 【攻击机Kail_2023.3】70G 【靶场win7】32G 【靶场Win2K3 】11G 【靶场winserver08】23G 环境搭建 修改VMnet1、VMnet2网卡的地址 将VMnet1作为内…

网络基础(一)【网络发展/认识协议/网络 VS 系统/以太网通信原理/重谈协议/网络中的地址管理】

网络基础&#xff08;一&#xff09; 1. 网络的发展2. 认识协议3. 网络 VS 系统4. 以太网通信原理5. 重谈协议6. 网络中的地址管理 1. 网络的发展 最开始时&#xff0c;计算机之间相互独立。 但是为了协作完成一些任务&#xff0c;就产生了计算机之间相互通讯的需求&#xff0c…

【ESP-IDF】组件

前言 对于要封装自己的库&#xff0c;在ESP-IDF中&#xff0c;可以采用构建组件的方式导入&#xff0c;而不是单纯在文件夹下导入.h和.c文件&#xff0c;不然一旦要导入的文件过多&#xff0c;它们背后的依赖可能就会相互交叉&#xff0c;不在方便移除和复用。本文就分别讲述&a…

AR配置静态IP双链路负载分担示例

AR配置静态IP双链路负载分担示例 适用于大部分企业网络出口 业务需求&#xff1a; 运营商1分配的接口IP为100.100.1.2&#xff0c;子网掩码为255.255.255.252&#xff0c;网关IP为100.100.1.1。 运营商2分配的接口IP为200.200.1.2&#xff0c;子网掩码为255.255.255.248&am…

视觉 Yolov11 环境配置(GPU版)

虚拟环境 首先用 anaconda 创建虚拟环境 根据自己需求创建一个虚拟环境 一个环境名叫 yolov11-py-3-8 就创建好了&#xff0c;后续的 yolov11 就会以这个环境去做深度学习&#xff08;这里不建议把环境的 py 版本设置到最新&#xff0c;设置个 3.8 或者 3.10 完全够用了 &am…