[LeetCode] 链表完整版 — 虚拟头结点 | 基本操作 | 双指针法 | 递归

链表

  • 基础知识
  • 虚拟头结点
    • 203# 移除链表元素(可递归)
    • 24# 两两交换链表中的节点(可递归)
  • 链表基本操作
    • 707# 设计链表
      • 单链表
      • 双链表
  • 双指针法
    • 206# 反转链表(可递归)
    • 19# 删除链表的倒数第N个结点
    • 面试题02.07.# 链表相交
    • 142# 环形链表II
  • 补充知识:递归
    • 汉诺塔

基础知识

链表适用于数据量不固定,频繁增删,较少查询的场景

一种通过指针串联在一起的线性结构,分散存储

每一个节点由两部分组成:数据域和指针域(存放指向下一个节点的指针),最后一个节点的指针域指向NULL(空指针)

单链表:

struct ListNode {int val;ListNode *next;ListNode(int x) : val(x), next(NULL) {}
}

双链表:每一个节点有两个指针域,一个指向下一个节点,一个指向上一个节点

循环链表:首尾相连,可以用来解决约瑟夫环问题

虚拟头结点

设置一个虚拟头结点,统一头结点和其他结点的操作,适用于必须要找当前结点的前一个结点的操作

203# 移除链表元素(可递归)

给你一个链表的头节点 head 和一个整数 val ,请你删除链表中所有满足 Node.val == val 的节点,并返回 新的头节点

示例 1:

img
输入:head = [1,2,6,3,4,5,6], val = 6
输出:[1,2,3,4,5]

示例 2:

输入:head = [], val = 1
输出:[]

示例 3:

输入:head = [7,7,7,7], val = 7
输出:[]

提示:

  • 列表中的节点数目在范围 [0, 10^4]
  • 1 <= Node.val <= 50
  • 0 <= val <= 50
// 设置虚拟头结点
// O(n) 0ms; O(1) 19.81MB
/*** 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* removeElements(ListNode* head, int val) {ListNode* dummyHead = new ListNode(0, head);ListNode* ptr = dummyHead;while (ptr->next !=NULL) {if (ptr->next->val == val) {ListNode* tmp = ptr->next;ptr->next = ptr->next->next;delete tmp;} else {ptr = ptr->next;}}head = dummyHead->next;delete dummyHead;return head;}
};

递归法:首先检查头节点的值是否为 val,如果是则移除头节点,答案即为在头节点的后续节点上递归的结果

// 递归法
// O(n) 0ms; O(n) 20.68MB
class Solution {
public:ListNode* removeElements(ListNode* head, int val) {if (head == nullptr) {return head;}if (head->val == val) {ListNode* tmp = head;head = removeElements(head->next, val);delete tmp;} else {head->next = removeElements(head->next, val);}return head;}
};
// 力扣官方递归
class Solution {
public:ListNode* removeElements(ListNode* head, int val) {if (head == nullptr) {return head;}head->next = removeElements(head->next, val);return head->val == val ? head->next : head;}

空间复杂度主要取决于递归调用栈,最多不会超过 n 层

24# 两两交换链表中的节点(可递归)

给你一个链表,两两交换其中相邻的节点,并返回交换后链表的头节点。你必须在不修改节点内部的值的情况下完成本题(即,只能进行节点交换)。

示例 1:

img
输入:head = [1,2,3,4]
输出:[2,1,4,3]

示例 2:

输入:head = []
输出:[]

示例 3:

输入:head = [1]
输出:[1]

提示:

  • 链表中节点的数目在范围 [0, 100]
  • 0 <= Node.val <= 100

使用虚拟头结点,不需要每次交换针对头结点单独处理

// 设置虚拟头结点
// O(n) 0ms; O(1) 11.04MB
/*** 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* swapPairs(ListNode* head) {ListNode *dummyHead = new ListNode(0);dummyHead->next = head;ListNode *cur = dummyHead;while (cur->next && cur->next->next) {ListNode* tmp = cur->next; // 临时起点ListNode* postNode = tmp->next->next;cur->next = cur->next->next;cur->next->next = tmp;tmp->next = postNode;cur = tmp;}ListNode *result = dummyHead->next;delete dummyHead;return result;}
};
// 递归法
// O(n) 0ms; O(n) 10.87MB
class Solution {
public:ListNode* swapPairs(ListNode* head) {if (!head || !head->next) return head;ListNode *swapNode = head->next;head->next = swapPairs(head->next->next);swapNode->next = head;return swapNode;}
};

链表基本操作

707# 设计链表

你可以选择使用单链表或者双链表,设计并实现自己的链表。

单链表中的节点应该具备两个属性:valnextval 是当前节点的值,next 是指向下一个节点的指针/引用。

如果是双向链表,则还需要属性 prev 以指示链表中的上一个节点。假设链表中的所有节点下标从 0 开始。

实现 MyLinkedList 类:

  • MyLinkedList() 初始化 MyLinkedList 对象。
  • int get(int index) 获取链表中下标为 index 的节点的值。如果下标无效,则返回 -1
  • void addAtHead(int val) 将一个值为 val 的节点插入到链表中第一个元素之前。在插入完成后,新节点会成为链表的第一个节点。
  • void addAtTail(int val) 将一个值为 val 的节点追加到链表中作为链表的最后一个元素。
  • void addAtIndex(int index, int val) 将一个值为 val 的节点插入到链表中下标为 index 的节点之前。如果 index 等于链表的长度,那么该节点会被追加到链表的末尾。如果 index 比长度更大,该节点将 不会插入 到链表中。
  • void deleteAtIndex(int index) 如果下标有效,则删除链表中下标为 index 的节点。

示例:

输入
["MyLinkedList", "addAtHead", "addAtTail", "addAtIndex", "get", "deleteAtIndex", "get"]
[[], [1], [3], [1, 2], [1], [1], [1]]
输出
[null, null, null, null, 2, null, 3]解释
MyLinkedList myLinkedList = new MyLinkedList();
myLinkedList.addAtHead(1);
myLinkedList.addAtTail(3);
myLinkedList.addAtIndex(1, 2);    // 链表变为 1->2->3
myLinkedList.get(1);              // 返回 2
myLinkedList.deleteAtIndex(1);    // 现在,链表变为 1->3
myLinkedList.get(1);              // 返回 3

提示:

  • 0 <= index, val <= 1000
  • 请不要使用内置的 LinkedList 库。
  • 调用 getaddAtHeadaddAtTailaddAtIndexdeleteAtIndex 的次数不超过 2000

定义size成员变量,便于处理

单链表

// 单链表
// 5ms; 25.44MB
// 所有函数的单次调用空间复杂度均为 O(1),总体空间复杂度为 O(n)
class MyLinkedList {
private:int _size;ListNode* _dummyHead; // 虚拟头结点public:// O(1)MyLinkedList() {_size = 0;_dummyHead = new ListNode(0);}// O(index)int get(int index) {if (index < 0 || index >= _size) return -1;ListNode* cur = _dummyHead->next;while (index--) {cur = cur->next;}return cur->val;}// O(1)void addAtHead(int val) {ListNode* newNode = new ListNode(val);newNode->next = _dummyHead->next;_dummyHead->next = newNode;_size++;}// O(n)void addAtTail(int val) {ListNode* newNode = new ListNode(val);ListNode* cur = _dummyHead;while (cur->next != nullptr) {cur = cur->next;}cur->next = newNode;_size++;}// O(index)void addAtIndex(int index, int val) {if (index > _size) return;if (index < 0) index = 0;ListNode* newNode = new ListNode(val);ListNode* cur = _dummyHead;while (index--) {cur = cur->next;}newNode->next = cur->next;cur->next = newNode;_size++;}// O(index)void deleteAtIndex(int index) {if (index >= _size || index < 0) return;ListNode* cur = _dummyHead;while (index--) {cur = cur->next;}ListNode* tmp = cur->next;cur->next = tmp->next;delete tmp;// delete命令指示释放了tmp指针原本所指的那部分内存,// 被delete后的指针tmp的值(地址)并非就是NULL,而是随机值。也就是被delete后,// 如果不再加上一句tmp=nullptr,tmp会成为乱指的野指针// 如果之后的程序不小心使用了tmp,会指向难以预想的内存空间tmp = nullptr;_size--;}
};/*** Your MyLinkedList object will be instantiated and called as such:* MyLinkedList* obj = new MyLinkedList();* int param_1 = obj->get(index);* obj->addAtHead(val);* obj->addAtTail(val);* obj->addAtIndex(index,val);* obj->deleteAtIndex(index);*/

双链表

// 双链表
// 0ms; 25.68MB
// 所有函数的单次调用空间复杂度均为 O(1),总体空间复杂度为 O(n)
struct DLinkListNode {int val;DLinkListNode *prev, *next;DLinkListNode(int _val) : val(_val), prev(nullptr), next(nullptr) {}
};class MyLinkedList {
private:int _size;DLinkListNode* _sentinelNode; // 哨兵结点public:// O(1)MyLinkedList() {_size = 0;_sentinelNode = new DLinkListNode(0);_sentinelNode->next = _sentinelNode;_sentinelNode->prev = _sentinelNode;}// O(index)int get(int index) {if (index < 0 || index >= _size) return -1;DLinkListNode* cur = _sentinelNode;if (index < _size >> 1) {for (int i = 0; i < index + 1; i++) {cur = cur->next;}} else {for (int i = 0; i < _size - index; i++) {cur = cur->prev; // 移动到目标节点}}return cur->val;}// O(1)void addAtHead(int val) {DLinkListNode* newNode = new DLinkListNode(val);newNode->prev = _sentinelNode;newNode->next = _sentinelNode->next;_sentinelNode->next->prev = newNode;_sentinelNode->next = newNode;_size++;}// O(1)void addAtTail(int val) {DLinkListNode* newNode = new DLinkListNode(val);DLinkListNode* cur = _sentinelNode->prev;newNode->next = _sentinelNode;newNode->prev = cur;_sentinelNode->prev = newNode;cur->next = newNode;_size++;}// O(index)void addAtIndex(int index, int val) {if (index > _size) return;if (index < 0) index = 0;DLinkListNode* newNode = new DLinkListNode(val);DLinkListNode* cur = _sentinelNode;if (index < _size >> 1) {for (int i = 0; i < index; i++) {cur = cur->next;}} else {for (int i = 0; i < _size - index + 1; i++) {cur = cur->prev;}}newNode->next = cur->next;newNode->prev = cur;cur->next->prev = newNode;cur->next = newNode;_size++;}// O(index)void deleteAtIndex(int index) {if (index >= _size || index < 0) return;DLinkListNode* cur = _sentinelNode;if (index < _size >> 1) {for (int i =0; i < index; i++) {cur = cur->next;}} else {for (int i =0; i < _size - index + 1; i++) {cur = cur->prev;}}DLinkListNode* tmp = cur->next;cur->next = tmp->next;tmp->next->prev = cur;delete tmp;// delete命令指示释放了tmp指针原本所指的那部分内存,// 被delete后的指针tmp的值(地址)并非就是NULL,而是随机值。也就是被delete后,// 如果不再加上一句tmp=nullptr,tmp会成为乱指的野指针// 如果之后的程序不小心使用了tmp,会指向难以预想的内存空间tmp = nullptr;_size--;}
};/*** Your MyLinkedList object will be instantiated and called as such:* MyLinkedList* obj = new MyLinkedList();* int param_1 = obj->get(index);* obj->addAtHead(val);* obj->addAtTail(val);* obj->addAtIndex(index,val);* obj->deleteAtIndex(index);*/

链表插入与删除的目标位置的上一节点相同,而下一节点偏移1,因此为了统一,以上代码均寻找上一节点

双指针法

206# 反转链表(可递归)

给你单链表的头节点 head ,请你反转链表,并返回反转后的链表。

示例 1:

img
输入:head = [1,2,3,4,5]
输出:[5,4,3,2,1]

示例 2:

img
输入:head = [1,2]
输出:[2,1]

示例 3:

输入:head = []
输出:[]

提示:

  • 链表中节点的数目范围是 [0, 5000]
  • -5000 <= Node.val <= 5000

使用双指针(curpre)改变链表的next指针的指向,直接将链表反转 ,而不用重新定义一个新的链表

// 双指针法
// O(n) 0ms; O(1) 13.17MB
/*** 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* reverseList(ListNode* head) {ListNode *pre = nullptr, *cur = head;ListNode *post;while (cur) {post = cur->next; // 保存cur的下一个节点,因为接下来要改变cur->nextcur->next = pre;pre = cur;cur = post;}return pre;}
};

递归法:假设链表的其余部分已经被反转,现在反转它前面的部分(从后往前翻转指针指向)

// 递归法
// O(n) 0ms; O(n) 13.30MB 递归调用了n层栈空间
class Solution {
public:ListNode* reverseList(ListNode* head) {if (!head || !head->next) return head;ListNode *last = reverseList(head->next); // 递归传入下一个节点,多次递归返回值均是最后一个节点head->next->next = head; // 反转// 反转后,head节点为尾节点,next需要指向NULLhead->next = nullptr; // 中间结点指向NULL后,外层递归通过 head->next->next = head 修正return last;}
};

19# 删除链表的倒数第N个结点

给你一个链表,删除链表的倒数第 n 个结点,并且返回链表的头结点。

示例 1:

img
输入:head = [1,2,3,4,5], n = 2
输出:[1,2,3,5]

示例 2:

输入:head = [1], n = 1
输出:[]

示例 3:

输入:head = [1,2], n = 1
输出:[1]

提示:

  • 链表中结点的数目为 sz
  • 1 <= sz <= 30
  • 0 <= Node.val <= 100
  • 1 <= n <= sz

快慢指针fastslow,如果要删除倒数第n个结点,让fast移动n步,然后让fastslow同时移动,直到fast指向链表末尾,删掉slow所指向的结点

使用虚拟头结点便于处理删除头结点的情况

// 快慢指针法 & 设置虚拟头结点
// O(n) 0ms; O(1) 14.68MB
/*** 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* removeNthFromEnd(ListNode* head, int n) {ListNode *dummyHead = new ListNode(0);dummyHead->next = head;ListNode *slow = dummyHead, *fast = dummyHead;for (int i = 0; i < n + 1; i++) { // slow应指向删除节点的上一个节点,故为n+1fast = fast->next;}while (fast) {slow = slow->next;fast = fast->next;}ListNode *tmp = slow->next;slow->next = tmp->next;delete tmp;return dummyHead->next; // 头结点可能会被删除,因此不能 return head}
};

面试题02.07.# 链表相交

给你两个单链表的头节点 headAheadB ,请你找出并返回两个单链表相交的起始节点。如果两个链表没有交点,返回 null

图示两个链表在节点 c1 开始相交**:**

题目数据 保证 整个链式结构中不存在环。

注意,函数返回结果后,链表必须 保持其原始结构

示例 1:

输入:intersectVal = 8, listA = [4,1,8,4,5], listB = [5,0,1,8,4,5], skipA = 2, skipB = 3
输出:Intersected at '8'
解释:相交节点的值为 8 (注意,如果两个链表相交则不能为 0)。
从各自的表头开始算起,链表 A 为 [4,1,8,4,5],链表 B 为 [5,0,1,8,4,5]。
在 A 中,相交节点前有 2 个节点;在 B 中,相交节点前有 3 个节点。

示例 2:

输入:intersectVal = 2, listA = [0,9,1,2,4], listB = [3,2,4], skipA = 3, skipB = 1
输出:Intersected at '2'
解释:相交节点的值为 2 (注意,如果两个链表相交则不能为 0)。
从各自的表头开始算起,链表 A 为 [0,9,1,2,4],链表 B 为 [3,2,4]。
在 A 中,相交节点前有 3 个节点;在 B 中,相交节点前有 1 个节点。

示例 3:

输入:intersectVal = 0, listA = [2,6,4], listB = [1,5], skipA = 3, skipB = 2
输出:null
解释:从各自的表头开始算起,链表 A 为 [2,6,4],链表 B 为 [1,5]。
由于这两个链表不相交,所以 intersectVal 必须为 0,而 skipA 和 skipB 可以是任意值。
这两个链表不相交,因此返回 null 。

提示:

  • listA 中节点数目为 m
  • listB 中节点数目为 n
  • 0 <= m, n <= 3 * 104
  • 1 <= Node.val <= 105
  • 0 <= skipA <= m
  • 0 <= skipB <= n
  • 如果 listAlistB 没有交点,intersectVal0
  • 如果 listAlistB 有交点,intersectVal == listA[skipA + 1] == listB[skipB + 1]

注意:交点不是数值相等,而是指针相等

假设链表headA的不相交部分有a个节点,链表headB的不相交部分有b个节点,两个链表相交的部分有c个节点,即a+c=mb+c=n

那么a+c+b = b+c+a

因此解决方法是双指针同步遍历两个链表,为空时更换所遍历的链表,直至指向同一个结点或都为空

// 双指针法
// O(m+n) 34ms; O(1) 18.18MB
/*** Definition for singly-linked list.* struct ListNode {*     int val;*     ListNode *next;*     ListNode(int x) : val(x), next(NULL) {}* };*/
class Solution {
public:ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {if (!headA || !headB) return nullptr;ListNode *curA = headA, *curB = headB;while (curA != curB) {curA = curA ? curA->next : headB;curB = curB ? curB->next : headA;}return curA;}
};

142# 环形链表II

给定一个链表的头节点 head ,返回链表开始入环的第一个节点。 如果链表无环,则返回 null

如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。如果 pos-1,则在该链表中没有环。注意:pos 不作为参数进行传递,仅仅是为了标识链表的实际情况。

不允许修改 链表。

示例 1:

img
输入:head = [3,2,0,-4], pos = 1
输出:返回索引为 1 的链表节点
解释:链表中有一个环,其尾部连接到第二个节点。

示例 2:

输入:head = [1,2], pos = 0
输出:返回索引为 0 的链表节点
解释:链表中有一个环,其尾部连接到第一个节点。

示例 3:

输入:head = [1], pos = -1
输出:返回 null
解释:链表中没有环。

提示:

  • 链表中节点的数目范围在范围 [0, 104]
  • -105 <= Node.val <= 105
  • pos 的值为 -1 或者链表中的一个有效索引

快慢指针法,从头结点出发,fast每次移动两个节点,slow每次移动一个节点,如果fastslow相同 ,则链表有环

(若链表有环,那么fastslow一定能重合,原因:该快慢指针的相对移动速度是1,它们在环内的相对距离一定能够减小为0)

设链表中环外部分的长度为a(所求),相遇点与环起点的距离为b,那么 a+b+n(b+c) = 2(a+b),即a=c+(n−1)(b+c)

则从头结点和相遇点同时出发一个指针,那么两个指针相遇点即为环形入口的结点(从头结点出发的指针走了a,从相遇点出发的指针走了c+(n−1)(b+c)即绕环若干圈后相遇)

// 快慢指针法
// O(n) 3ms; O(1) 11.18MB
/*** Definition for singly-linked list.* struct ListNode {*     int val;*     ListNode *next;*     ListNode(int x) : val(x), next(NULL) {}* };*/
class Solution {
public:ListNode *detectCycle(ListNode *head) {ListNode *slow = head, *fast = head;while (fast && fast->next) {slow = slow->next;fast = fast->next->next;if (slow == fast) break;}if (!fast || !fast->next) return nullptr;slow = head;while (slow != fast) {slow = slow->next;fast = fast->next;}return slow;}
};

时间复杂度:快慢指针相遇前,slow指针走过的距离不会超过链表的总长度;相遇后,两个指针走的次数也不会超过链表长度,因此总体次数不超过2n

slow指针走过的距离不会超过链表的总长度:slow进环后与fast的最大距离d小于环长C,且2b = b+d < b+C,因此slow走的距离b < Cfastslow的相对移动速度是1,因此fast不可能跳过slow,故2b = b+d而非2b = b+d+kC

补充知识:递归

推荐教学视频:快速掌握递归

  • 确定问题,即函数参数
  • 解决基准问题:可以直接计算结果并返回的条件
  • 拆解问题:寻找规模更小的子问题

汉诺塔

void hanoi(int n, char F, char A, char T) {if (n = 1) {printf("move %d from %c to %c\n", n, F, T);return;}hanoi(n-1, F, T, A);printf("move %d from %c to %c\n", n, F, T);hanoi(n-1, A, F, T);
}

本文参考了 LeetCode官方题解 及 代码随想录

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

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

相关文章

OODA循环在网络安全运营平台建设中的应用

OODA循环最早用于信息战领域&#xff0c;在空对空武装冲突敌对双方互相较量时&#xff0c;看谁能更快更好地完成“观察—调整—决策—行动”的循环程序。 双方都从观察开始&#xff0c;观察自己、观察环境和敌人。基于观察&#xff0c;获取相关的外部信息&#xff0c;根据感知…

【人工智能】:搭建本地AI服务——Ollama、LobeChat和Go语言的全方位实践指南

前言 随着自然语言处理&#xff08;NLP&#xff09;技术的快速发展&#xff0c;越来越多的企业和个人开发者寻求在本地环境中运行大型语言模型&#xff08;LLM&#xff09;&#xff0c;以确保数据隐私和提高响应速度。Ollama 作为一个强大的本地运行框架&#xff0c;支持多种先…

HarmonyOS NEXT应用开发边学边玩系列:从零实现一影视APP (四、最近上映电影滚动展示及加载更多的实现)

在HarmonyOS NEXT开发环境中&#xff0c;可以使用多种组件和库来构建丰富且交互友好的应用。本文将展示如何使用HarmonyOS NEXT框架和nutpi/axios库&#xff0c;从零开始实现一个简单的影视APP的首页&#xff0c;主要关注最近上映电影的滚动展示及加载更多功能的实现。 开源项目…

Linux 音视频入门到实战专栏(视频篇)视频编解码 MPP

文章目录 一、MPP 介绍二、获取和编译RKMPP库三、视频解码四、视频编码 沉淀、分享、成长&#xff0c;让自己和他人都能有所收获&#xff01;&#x1f604; &#x1f4e2;本篇将介绍如何调用alsa api来进行音频数据的播放和录制。 一、MPP 介绍 瑞芯微提供的媒体处理软件平台…

LabVIEW 蔬菜精密播种监测系统

在当前蔬菜播种工作中&#xff0c;存在着诸多问题。一方面&#xff0c;播种精度难以达到现代农业的高标准要求&#xff0c;导致种子分布不均&#xff0c;影响作物的生长发育和最终产量&#xff1b;另一方面&#xff0c;对于小粒径种子&#xff0c;传统的监测手段难以实现有效监…

飞牛 使用docker部署Watchtower 自动更新 Docker 容器

Watchtower是一款开源的Docker容器管理工具&#xff0c;其主要功能在于自动更新运行中的Docker容器 Watchtower 支持以下功能&#xff1a; 自动拉取镜像并更新容器。 配置邮件通知。 定时执行容器更新任务。 compose搭建Watchtower 1、新建文件夹 先在任意位置创建一个 w…

网络功能虚拟化(NFV):网络设备也能虚拟成产品

随着信息技术的迅猛发展&#xff0c;网络服务的需求也在不断变化。为了应对这一挑战&#xff0c;网络功能虚拟化&#xff08;NFV&#xff09;作为一项创新技术应运而生。它不仅改变了传统网络服务的部署方式&#xff0c;还为电信行业带来了前所未有的灵活性、效率和成本效益。 …

如何将本地 Node.js 服务部署到宝塔面板:完整的部署指南

文章简介&#xff1a; 将本地开发的 Node.js 项目部署到线上服务器是开发者常见的工作流程之一。在这篇文章中&#xff0c;我将详细介绍如何将本地的 Node.js 服务通过宝塔面板&#xff08;BT 面板&#xff09;上线。宝塔面板是一个强大的服务器管理工具&#xff0c;具有简洁的…

Word2Vec中的CBOW模型训练原理详细解析

Word2Vec中的CBOW模型训练原理详细解析 1. CBOW模型概述 CBOW模型的训练目标是利用一个单词周围的上下文单词来预测该单词本身。具体来说&#xff0c;给定当前单词的上下文单词&#xff0c;通过训练神经网络来最大化当前单词出现在这些上下文单词中的概率。 2. 模型结构 CB…

第22篇 基于ARM A9处理器用汇编语言实现中断<四>

Q&#xff1a;怎样编写ARM A9处理器汇编语言代码配置使用按键和定时器中断&#xff1f; A&#xff1a;本次实验同样为中断模式和监督模式都设置ARM A9堆栈指针&#xff0c;并使能中断&#xff0c;此外在主程序中调用子程序CONFIG_HPS_TIMER和CONFIG_KEYS分别对HPS Timer 0&…

后盾人JS -- 好用的 JavaScript Symbol 类型

Symbol使用场景介绍 举个例子&#xff0c;当leader让你去机房取某个电脑的时候&#xff0c;机房那么多电脑&#xff0c;你怎么知道取哪个 所以这个时候symbol的作用就显现出来了&#xff08;上面有什么贴纸的&#xff0c;什么型号的电脑&#xff09; 声明定义Symbol的几种方…

反转字符串中的单词 II:Swift 实现与详解

网罗开发 &#xff08;小红书、快手、视频号同名&#xff09; 大家好&#xff0c;我是 展菲&#xff0c;目前在上市企业从事人工智能项目研发管理工作&#xff0c;平时热衷于分享各种编程领域的软硬技能知识以及前沿技术&#xff0c;包括iOS、前端、Harmony OS、Java、Python等…

JVM相关面试题

一、JVM是什么: Java Virtual Machine,Java的运行环境(java二进制字节码的运行环境)&#xff1b;一次编写、到处运行&#xff1b;自动管理内存&#xff0c;提供垃圾回收机制 JVM的组成部分、运行流程: 二、JVM的组成: 1.程序计数器: 程序计数器是线程私有的&#xff0c;内部…

【蜂巢——方向,数学】

题目 代码 #include <bits/stdc.h> using namespace std; using ll long long; int dx[6] {-1, -1, 0, 1, 1, 0}; int dy[6] {0, 1, 1, 0, -1, -1}; void cal(int d, int p, int q, int& x, int& y) {x p * dx[d];y p * dy[d];d (d 2) % 6;x q * dx[d];…

使用 Thermal Desktop 进行航天器热分析

介绍 将航天器保持在运行温度下的轨道上是一个具有挑战性的问题。航天器需要处理太空非常寒冷的背景温度&#xff0c;同时还要管理来自内部组件、地球反照率和太阳辐射的高热负荷。航天器在轨道上可以进行的各种轨道机动使解决这个问题变得更加复杂。 Thermal Desktop 是一款…

【ESP32】ESP-IDF开发 | WiFi开发 | AP模式 + 基站连接例程

1. 简介 前面一篇讲了WiFi的基站模式&#xff0c;演示了怎么编程连接AP&#xff0c;所以这一篇讲一讲AP模式&#xff0c;ESP32作AP&#xff0c;让其他的设备连接自己。 1.1 DHCP 这里需要补充一个知识点——DHCP服务器。当基站连接一个AP时&#xff0c;会被分配一个IP&#xf…

nss刷题3

[SWPUCTF 2022 新生赛]webdog1__start level1&#xff1a; 打开环境后什么也&#xff0c;没有&#xff0c;查看源码&#xff0c;看到第一关是MD5值&#xff0c;要get传参web&#xff0c;然后web的值的MD5和它原来值相等&#xff0c;0e开头的字符在php中都是0&#xff0c;传入…

如何使用C#与SQL Server数据库进行交互

一.创建数据库 用VS 创建数据库的步骤&#xff1a; 1.打开vs&#xff0c;创建一个新项目&#xff0c;分别在搜素框中选择C#、Windows、桌面&#xff0c;然后选择Windows窗体应用(.NET Framework) 2.打开“视图-服务器资源管理器”&#xff0c;右键单击“数据连接”&#xff0…

用户中心项目教程(二)---umi3的使用出现的错误

目录 1.情况的说明 2.遇到的问题 1&#xff09;第一个问题-关于npx的使用 2&#xff09;第二个问题--unsupport问题 3&#xff09;第三个收获--nodejs安装问题 4&#xff09;第四个收获---nvm下载问题 5&#xff09;第五个问题--尚未解决的问题 3.个人总结 1.情况的说明…

讲一下ZooKeeper的持久化机制?

大家好&#xff0c;我是锋哥。今天分享关于【讲一下ZooKeeper的持久化机制&#xff1f;】面试题。希望对大家有帮助&#xff1b; 讲一下ZooKeeper的持久化机制&#xff1f; 1000道 互联网大厂Java工程师 精选面试题-Java资源分享网 ZooKeeper 是一个开源的分布式协调服务&…