代码随想录算法训练营第三天 | 203.移除链表元素、707.设计链表、206.反转链表

代码随想录算法训练营第三天 | 203.移除链表元素、707.设计链表、206.反转链表

文章目录

  • 代码随想录算法训练营第三天 | 203.移除链表元素、707.设计链表、206.反转链表
    • 1 链表理论基础
      • 1.1 链表的定义
      • 1.2 链表的类型
      • 1.3 链表的存储方式
      • 1.4 链表的操作性能分析
      • 1.5 链表和数组的区别
    • 2 LeetCode 203.移除链表元素
      • 2.1 不带虚拟头节点的删除操作
      • 2.2 带虚拟头节点的删除操作
    • 3 LeetCode 707.设计链表
      • 3.1 Python版本代码
      • 3.2 C++版本代码
    • 4 LeetCode 206.反转链表
      • 4.1 双指针法
      • 4.2 递归法
      • 4.3 头插法

1 链表理论基础

链表是一种常见的数据结构,它在计算机科学中用于存储一系列元素,链表的特点是其元素不必在内存中连续存储,这种结构提供了在序列内部插入和删除元素的高效操作,下面将结合408相关知识介绍一下有关链表的一些基础知识,以及链表与数组的区别。

1.1 链表的定义

链表由一系列称为“节点”的元素组成,每个节点包含两个部分:数据和指向列表中下一个节点的引用(或链接),在最简单的形式中,每个节点包含数据和一个指向列表中下一个节点的指针,即数据域(data)和指针域(next)。

1.2 链表的类型

  • 单向链表:每个节点只有指向下一个节点的链接,其节点类型定义如下(参考王道数据结构单链表定义):

    typedef struct LNode{ElemType data;   //数据域struct LNode *next;  //指针域
    }LNode, *LinkList
    

    利用单链表可以解决顺序表需要大量连续存储单元的缺点,但单链表附加指针域,也存在浪费存储空间的缺点。由于单链表的元素离散地分布在存储空间中,所以单链表是非随机存取的存储结构,即不能直接找到表中某个特定的结点。查找某个特定的结点时,需要从表头开始遍历,依次查找。

    通常用头指针来标识一个单链表,如单链表L,头指针为NULL时表示一个空表。此外,为了操作上的方便,在单链表第一个结点之前附加一个结点,称为头结点。头结点的数据域可以不设任何信息,也可以记录表长等信息。头结点的指针域指向线性表的第一个元素结点,如下图所示(来源于王道数据结构):

    在这里插入图片描述

    头结点和头指针的区分:不管带不带头结点,头指针都始终指向链表的第一个结点,而头结点是带头结点的链表中的第一个结点,结点内通常不存储信息。

    引入头结点后,可以带来两个优点:

    ①由于第一个数据结点的位置被存放在头结点的指针域中,因此在链表的第一个位置上的操作和在表的其他位置上的操作一致,无须进行特殊处理。

    ②无论链表是否为空,其头指针都是指向头结点的非空指针(空表中头结点的指针域为空),因此空表和非空表的处理也就得到了统一。

  • 双向链表:每个节点有两个链接,一个指向前一个节点(即prior指针),另一个指向下一个节点(即next指针),是为了克服单链表的缺点,所以引入了双链表。

    其节点类型定义如下(参考王道数据结构双链表定义):

    typedef struct DNode{ElemType data;   //数据域struct DNode *prior,*next;  //前驱和后继指针
    }LNode, *DLinklist
    

    其表示示意图如下图所示(来源于王道数据结构):

    在这里插入图片描述

    双链表在单链表的结点中增加了一个指向其前驱的prior 指针,因此双链表中的按值查找和按位查找的操作与单链表的相同。但双链表在插入和删除操作的实现上,与单链表有着较大的不同。这是因为“链”变化时也需要对prior 指针做出修改,其关键是保证在修改的过程中不断链。此外,双链表可以很方便地找到其前驱结点,因此,插入、删除操作的时间复杂度仅为O(1)。

  • 循环链表:链表中的最后一个节点指向第一个节点或其他之前的节点,形成一个环,循环单链表和单链表的区别在于,表中最后一个结点的指针不是NULL,而改为指向头结点,从而整个链表形成一个环,如下图所示(来源于王道数据结构):

    在这里插入图片描述

    在循环单链表中,表尾结点*r的next域指向L,故表中没有指针域为NULL的结点,因此循环单链表的判空条件不是头结点的指针是否为空,而是它是否等于头指针。

    循环单链表的插入、删除算法与单链表的几乎一样,所不同的是若操作是在表尾进行,则执行的操作不同,以让单链表继续保持循环的性质。当然,正是因为循环单链表是一个“环”,因此在任何一个位置上的插入和删除操作都是等价的,无须判断是否是表尾。

    在单链表中只能从表头结点开始往后顺序遍历整个链表,而循环单链表可以从表中的任意一个结点开始遍历整个链表。有时对循环单链表不设头指针而仅设尾指针,以使得操作效率更高。其原因是,若设的是头指针,对在表尾插入元素需要O(n)的时间复杂度,而若设的是尾指针r,r->next即为头指针,对在表头或表尾插入元素都只需要O(1)的时间复杂度。

  • 循环双链表:结合了双向链表和循环链表的特点,不同的是在循环双链表中,头节点的prior指针还要指向表尾节点,其表示示意图如下图所示(来源于王道数据结构):

    在这里插入图片描述

    在循环双链表L中,某结点*p为尾结点时,p->next==L;当循环双链表为空表时,其头结点的prior域和next域都等于L。

1.3 链表的存储方式

链表中的节点不需要在内存中连续存储(这一点和数组相反),每个节点只需存储其数据部分和指向列表中下一个(和/或前一个)节点的指针,这种存储方式使得链表在插入和删除操作时能够较少地移动或重新排列其他元素,如下图所示(来源于代码随想录):
在这里插入图片描述

1.4 链表的操作性能分析

  • 插入和删除:链表可以在任何位置高效地插入或删除节点,这些操作的时间复杂度通常是 O(1),但如果需要找到特定位置,则可能需要 O(n) 的时间,其中 n 是链表的长度。
  • 搜索:搜索一个元素在链表中的位置或者搜索链表中的特定元素的效率较低,时间复杂度为 O(n)。
  • 访问:对于链表的随机访问(例如访问第 n 个元素),效率低下,因为需要从头节点开始遍历链表,时间复杂度为 O(n)。

1.5 链表和数组的区别

特性数组链表
存储方式在内存中连续存储元素元素分布在内存中不连续的节点中
访问时间快速随机访问,O(1)需要从头遍历,O(n)
插入和删除操作可能需要移动元素,O(n)在已知位置快速操作,O(1);寻找位置时,O(n)
内存利用率可能造成内存浪费或空间不足动态分配,高效利用内存
内存分配静态或动态内存分配总是使用动态内存分配
实现实现简单,支持多种操作实现复杂,需要额外内存存储指针
应用场景元素数量固定,频繁访问元素数量变化大,频繁插入删除

2 LeetCode 203.移除链表元素

题目链接:https://leetcode.cn/problems/remove-linked-list-elements/description/

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

示例 1:

在这里插入图片描述

输入: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, 104]
  • 1 <= Node.val <= 50
  • 0 <= val <= 50

在移除链表元素时我们需要考虑是否需要给原始链表添加虚拟头节点(也叫哨兵节点),因为如果直接在原始链表上我们需要首先判断是否删除的是head所指的头节点,头节点的删除和其他的节点的删除是不一样的,但是如果我们使用虚拟头节点来进行操作的话,可以统一删除节点的操作,下面我们将两种情况的代码都写出来。

2.1 不带虚拟头节点的删除操作

(1)Python版本代码:

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
class Solution:def removeElements(self, head, val):# 首先移除头部需要删除的节点while(head != None and head.val == val):    # 注意head != Nonehead = head.next# 现在头部节点不需要删除,cur指向第一个不需要删除的节点cur = headwhile(cur != None and cur.next != None):    # 注意cur.next != Noneif cur.next.val == val:    # 如果下一个节点需要删除cur.next = cur.next.next    # 删除下一个节点else:    # 如果下一个节点不需要删除cur = cur.next    # cur指向下一个节点return head

(2)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* removeElements(ListNode* head, int val) {// 首先移除头部需要删除的节点while (head != nullptr && head->val == val) {head = head->next;}// 现在头部节点不需要删除ListNode* cur = head;// 遍历链表while (cur != nullptr && cur->next != nullptr) {if (cur->next->val == val) {// 如果下一个节点需要删除cur->next = cur->next->next;} else {// 如果下一个节点不需要删除cur = cur->next;}}return head;}
};

2.2 带虚拟头节点的删除操作

(1)Python版本代码:

class Solution:def removeElements(self, head, val):dummy = ListNode(0, head)   # 创建虚拟头节点dummy.next = head   # 虚拟头节点指向headcur = dummy    # cur指向虚拟头节点while cur.next:if cur.next.val == val:cur.next = cur.next.nextelse:cur = cur.nextreturn dummy.next

(2)C++版本代码:

class Solution {
public:ListNode* removeElements(ListNode* head, int val) {// 创建一个虚拟头节点ListNode dummy(0);dummy.next = head;// cur 指向虚拟头节点ListNode* cur = &dummy;// 遍历链表while (cur->next != nullptr) {if (cur->next->val == val) {// 删除下一个节点cur->next = cur->next->next;} else {// 移动到下一个节点cur = cur->next;}}return dummy.next;}
};

本题比较简单,主要是介绍两种处理方法,最关键是要理解虚拟头结点的使用技巧,这个对链表题目很重要,我更偏向于使用虚拟头节点来进行操作,因为这样可以统一删除操作,同样添加操作也类似,可以简化代码。

3 LeetCode 707.设计链表

题目链接:https://leetcode.cn/problems/design-linked-list/description/

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

单链表中的节点应该具备两个属性: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

我们后续有关链表的操作都将默认使用虚拟头节点,这样会使代码看起来更加简洁,上面也介绍过了。

这道题目就是考察我们对链表常见的操作的掌握情况,也就是下面的这几种操作:

  • 获取链表第index个节点的数值
  • 在链表的最前面插入一个节点
  • 在链表的最后面插入一个节点
  • 在链表第index个节点前面插入一个节点
  • 删除链表的第index个节点

这道题目掌握牢固了,基本上链表操作也就可以了,其他类型的链表也都是类似的操作。

需要注意的是在链表的插入操作中,我们需要注意插入操作的执行顺序,防止出现断链的情况,这一点在408数据结构中也经常考察,就比如在刚过去的2024年408考试中第一道选择题就考察了链表的操作,需要特别注意。

下面我们直接写出每个函数的代码。

3.1 Python版本代码

class ListNode:def __init__(self, val = 0, next = None):self.val = valself.next = nextclass MyLinkedList:def __init__(self):self.dummy = ListNode()self.size = 0# 获取链表中下标为 `index` 的节点的值。如果下标无效,则返回 `-1` 。def get(self, index: int) -> int:if index < 0 or index >= self.size:     return -1cur = self.dummy.nextwhile index:cur = cur.nextindex -= 1return cur.val# 在链表头部插入一个节点,该节点的值为 `val` 。插入后,新节点将成为链表的第一个节点。def addAtHead(self, val: int) -> None:self.dummy.next = ListNode(val, self.dummy.next)self.size += 1# 将值为 `val` 的节点插入到链表中的第一个值为 `val` 的节点之后。def addAtTail(self, val: int) -> None:cur = self.dummywhile cur.next:cur = cur.nextcur.next = ListNode(val)self.size += 1# 在链表中的第 index 个节点之前,插入值为 `val` 的节点。如果 index 等于链表的长度,则该节点将附加到链表的末尾。def addAtIndex(self, index: int, val: int) -> None:if index < 0 or index > self.size:returncur = self.dummywhile index:cur = cur.nextindex -= 1cur.next = ListNode(val, cur.next)self.size += 1# 删除链表中的第 index 个节点。如果 index 等于链表的长度,则删除链表中的最后一个节点。def deleteAtIndex(self, index: int) -> None:if index < 0 or index >= self.size:returncur = self.dummywhile index:cur = cur.nextindex -= 1cur.next = cur.next.nextself.size -= 1

3.2 C++版本代码

// class ListNode {
// public:
//     int val;
//     ListNode *next;
//     ListNode(int val = 0, ListNode *next = nullptr) : val(val), next(next) {}
// };class MyLinkedList {
public:// 构造函数MyLinkedList() {dummy = new ListNode(0);  // 创建一个虚拟头节点size = 0;}// 析构函数,释放链表占用的内存~MyLinkedList() {ListNode* cur = dummy;while (cur != nullptr) {ListNode* next = cur->next;delete cur;  // 使用 delete 释放内存cur = next;}}// 获取指定索引的节点值int get(int index) {if (index < 0 || index >= size) {return -1;}ListNode* cur = dummy->next;while (index--) {cur = cur->next;}return cur->val;}// 在链表头部添加节点void addAtHead(int val) {dummy->next = new ListNode(val, dummy->next);size++;}// 在链表尾部添加节点void addAtTail(int val) {ListNode* cur = dummy;while (cur->next != nullptr) {cur = cur->next;}cur->next = new ListNode(val);size++;}// 在指定索引添加节点void addAtIndex(int index, int val) {if (index < 0 || index > size) {return;}ListNode* cur = dummy;while (index--) {cur = cur->next;}cur->next = new ListNode(val, cur->next);size++;}// 删除指定索引的节点void deleteAtIndex(int index) {if (index < 0 || index >= size) {return;}ListNode* cur = dummy;while (index--) {cur = cur->next;}ListNode* temp = cur->next;cur->next = cur->next->next;delete temp;  // 删除节点size--;}private:ListNode* dummy;  // 虚拟头节点int size;  // 链表大小
};

对于释放内存操作,需要注意的是在 C++ 中,应当使用 delete 而非 free() 来释放由 new 分配的内存,newdelete 是 C++ 中处理动态内存的标准方式,它们不仅分配和释放内存,还调用对象的构造函数和析构函数,相比之下,malloc()free() 是 C 语言中的函数,它们只处理内存的分配和释放,而不调用构造函数和析构函数。

4 LeetCode 206.反转链表

题目链接:https://leetcode.cn/problems/reverse-linked-list/

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

示例 1:

在这里插入图片描述

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

示例 2:

在这里插入图片描述

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

示例 3:

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

提示:

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

**进阶:**链表可以选用迭代或递归方式完成反转。你能否用两种方法解决这道题?

这道题目有点像有一年的408算法题,我去翻了一下,是2019年的算法题,那道题目需要反转三次,但是这两道题目都是类似的操作,下面我贴出408的2019年算法题:
在这里插入图片描述

下面是2024版王道数据结构中的解法:

在这里插入图片描述

回到这题来,这道题目我们可以首先考虑使用双指针方法来有效的反转链表。

4.1 双指针法

我们使用两个指针:curprecur 指向当前要处理的节点,而 pre 存储 cur 的前一个节点,通过遍历链表,逐个改变节点的指向,直到链表完全反转,需要注意的是反转代码的执行顺序,我们需要设置一个临时指针temp保存当前节点,避免首先反转之后cur的值已经变化然后出现指向错误。在每次迭代中,将 curnext 指针指向 pre,当 curNone 时,pre 将指向新的头节点,完成反转。

(1)Python版本实现代码:

class Solution:def reverseList(self, head):cur = head  # 当前节点pre = None  # 前一个节点while cur:  # 当前节点不为空temp = cur.next  # 临时节点cur.next = pre  # 当前节点指向前一个节点pre = cur     # 前一个节点指向当前节点cur = temp    # 当前节点指向下一个节点return pre  # 返回前一个节点

(2)C++版本实现代码:

class Solution {
public:ListNode* reverseList(ListNode* head) {ListNode* cur = head;  // 当前节点ListNode* pre = nullptr;  // 前一个节点while (cur != nullptr) {ListNode* temp = cur->next;  // 临时节点,保存当前节点的下一个节点cur->next = pre;  // 当前节点指向前一个节点pre = cur;  // 前一个节点指向当前节点cur = temp;  // 当前节点指向下一个节点}return pre;  // 返回前一个节点,即新的头节点}
};

4.2 递归法

我们还可以使用递归的方法简化双指针法的代码,但是不推荐新手首先写递归方法,因为递归方法比较抽象,难以理解,更重要的还是双指针的实现思路,我们先写出双指针的解法之后我们就可以按照双指针的思路对应的写出递归版的代码。

(1)Python版本实现代码:

class Solution:def reverse(self, cur, pre):    if cur == None:return pretemp = cur.nextcur.next = prereturn self.reverse(temp, cur)def reverseList(self, head):return self.reverse(head, None)

(2)C++版本实现代码:

class Solution {
public:ListNode* reverse(ListNode* cur, ListNode* pre) {if (cur == nullptr) {return pre;}ListNode* temp = cur->next;cur->next = pre;return reverse(temp, cur);}ListNode* reverseList(ListNode* head) {return reverse(head, nullptr);}
};

4.3 头插法

另外我们再介绍一种实现思路,那就是利用链表的头插法来解决这道题目,头插法的基本思想是逐个将原链表的节点插入到一个新链表的头部,在本题中也就是逆用头插法,首先创建一个新的虚拟头节点,遍历原链表,每次迭代中,将当前节点从原链表中移除,将该节点插入到新链表的头部,继续遍历,直到原链表为空,最后新链表的头节点即为反转后的链表头节点。

(1)Python版本实现代码:

class Solution:def reverseList(self, head):new_head = None  # 新链表的头节点,初始为 Nonecur = headwhile cur:temp = cur.next  # 保存当前节点的下一个节点cur.next = new_head  # 将当前节点插入到新链表的头部new_head = cur  # 更新新链表的头节点cur = temp  # 移动到原链表的下一个节点return new_head

(2)C++版本实现代码:

class Solution {
public:ListNode* reverseList(ListNode* head) {ListNode* new_head = nullptr;  // 新链表的头节点,初始为 nullptrListNode* cur = head;while (cur != nullptr) {ListNode* temp = cur->next;  // 保存当前节点的下一个节点cur->next = new_head;  // 将当前节点插入到新链表的头部new_head = cur;  // 更新新链表的头节点cur = temp;  // 移动到原链表的下一个节点}return new_head;  // 返回新链表的头节点,即反转后的头节点}
};

在头插法代码中new_head 代表新链表的头节点,最初为 None,在每次迭代中,我们将 cur(当前处理的节点)从原链表中移除,并将其插入到新链表的头部,这个过程一直持续到原链表被完全遍历,最终new_head 将成为反转后链表的头节点。

头插法算是提供了另一种有效的链表反转方法,特别适用于需要创建反转链表的副本的情况。

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

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

相关文章

结队编程 - 华为OD统一考试

OD统一考试 题解: Java / Python / C++ 题目描述 某部门计划通过结队编程来进行项目开发,已知该部门有 N 名员工,每个员工有独一无二的职级,每三个员工形成一个小组进行结队编程,结队分组规则如下: 从部门中选出序号分别为 i、j、k 的3名员工,他们的职级分别为 level[…

JavaScript保留字和预定义的全局变量及函数汇总

保留字也称关键字&#xff0c;每种语言中都有该语言本身规定的一些关键字&#xff0c;这些关键字都是该语言的语法实现基础&#xff0c;JavaScript中规定了一些标识符作为现行版本的关键字或者将来版本中可能会用到的关键字&#xff0c;所以当我们定义标识符时就不能使用这些关…

乱码问题汇总

写在前面 在工作中经常会碰到各种莫名其妙的乱码问题&#xff0c;但通过之前的学习&#xff1a;字符集&字符编码-CSDN博客 &#xff0c;可以知道乱码的根本原因就是使用和数据源编码不一样的编码解码导致。 如&#xff1a;BIG5解码GB2312编码内容&#xff0c;编解码不一致…

【二十】【动态规划】879. 盈利计划、377. 组合总和 Ⅳ、96. 不同的二叉搜索树 ,三道题目深度解析

动态规划 动态规划就像是解决问题的一种策略&#xff0c;它可以帮助我们更高效地找到问题的解决方案。这个策略的核心思想就是将问题分解为一系列的小问题&#xff0c;并将每个小问题的解保存起来。这样&#xff0c;当我们需要解决原始问题的时候&#xff0c;我们就可以直接利…

【UE Niagara学习笔记】07 - 火焰的热变形效果

目录 效果 步骤 一、创建热变形材质 二、添加新的发射器 2.1 设置粒子材质 2.2 设置粒子初始大小 2.3 设置粒子持续生成 三、修改材质 四、设置粒子效果 在上一篇博客&#xff08;【UE Niagara学习笔记】06 - 制作火焰喷射过程中飞舞的火星&#xff09;的基础上继续…

codeql基本使用

0x01 安装codeql 去github下载一个对应版本的codeql捆绑包。 https://github.com/github/codeql-action/releases 然后解压&#xff0c;这里我是解压到桌面 然后用添加到环境变量中 然后在任意位置输入codeql命令&#xff0c;如果能有以下提示就表示安装成功 然后下载vscode…

PCAN数据速率分配

BitrateFD b"f_clock_mhz80, nom_brp8, nom_tseg116, nom_tseg23, nom_sjw1, data_brp4, data_tseg17, data_tseg22, data_sjw1" 在PCANBasic.py提供的example里有几个pcan收发的python程序,配置BitrateFD 后边丝毫看不出配置的Normal Bit Rate与 Data Bit Rate, …

在 Nvidia Docker 容器编译构建显存优化加速组件 xFormers

本篇文章&#xff0c;聊聊如何在新版本 PyTorch 和 CUDA 容器环境中完成 xFormers 的编译构建。 让你的模型应用能够跑的更快。 写在前面 xFormers 是 FaceBook Research &#xff08;Meta&#xff09;开源的使用率非常高的 Transformers 加速选型&#xff0c;当我们使用大模…

7.11、Kali Linux中文版虚拟机安装运行教程

目录 一、资源下载准备工作 二、安装教程 三、kali linux换源 四、apt-get update 报错 一、资源下载准备工作 linux 中文版镜像历史版本下载:http://old.kali.org/kali-images/ 大家可以自行选择版本下载&#xff0c;本人下载的是2021版本 二、安装教程 打开vmvare wokst…

鸿蒙基础开发实战-(ArkTS)像素转换

像素单位转换API的使用 主要功能包括&#xff1a; 展示了不同像素单位的使用。展示了像素单位转换相关API的使用。 像素单位介绍页面 在像素单位介绍页面&#xff0c;介绍了系统像素单位的概念&#xff0c;并在页面中为Text组件的宽度属性设置不同的像素单位&#xff0c;fp…

ATFX汇市:2024年以来十个交易日,日元贬值幅度近3%,居主流货币之首

2024年以来&#xff0c;七大主流货币对美元的贬值幅度如下&#xff1a; ▲ATFX制表 可以看出&#xff0c;对美元贬值幅度最大的就是日元的2.97%&#xff0c;其次是澳元的1.67%&#xff0c;排在第三位的是瑞郎1.34%。七大主流货币当中&#xff0c;只有英镑实现了对美元的升值&a…

关注个人数据保护,肯尼亚发布新指南

近日&#xff0c;肯尼亚数据保护专员办公室&#xff08;ODPC&#xff09;发布了新的指导文件&#xff0c;旨在加强教育、通讯和数字信贷领域的数据保护措施&#xff0c;并提供了一个处理健康数据的通用指南。 这些指导意见是基于《数据保护法》&#xff08;DPA&#xff09;制定…