当前位置: 首页 > news >正文

数据结构C语言练习(单双链表)

本篇练习题(单链表):

1.力扣 203. 移除链表元素

2.力扣 206. 反转链表

3.力扣 876. 链表的中间结点

4.力扣 21. 合并两个有序链表

5. 牛客 链表分割算法详解

6.牛客 链表回文结构判断

7. 力扣 160. 相交链表

8. 力扣 141 环形链表

9. 力扣 142 环形链表 II

10. 力扣 138. 随机链表的复制

1.力扣 203. 移除链表元素:构建新链表解法详解

一、题目概述

给定一个链表的头节点 head 和一个整数 val,需要删除链表中所有满足 Node.val == val 的节点,并返回新的头节点。例如,输入链表 [1,2,6,3,4,5,6]val=6,输出 [1,2,3,4,5]

二、代码实现

/*** Definition for singly-linked list.* struct ListNode {*     int val;*     struct ListNode *next;* };*/
struct ListNode* removeElements(struct ListNode* head, int val) {struct ListNode* pcur, *newhead, *newtail;pcur = head;newhead = newtail = NULL;while (pcur) {if (pcur->val != val) {if (newhead == NULL) {newhead = newtail = pcur;} else {newtail->next = pcur;newtail = newtail->next;}}pcur = pcur->next;}if (newtail) {newtail->next = NULL;}return newhead;
}

三、代码逐行详解

1. 初始化指针

struct ListNode* pcur, *newhead, *newtail;
pcur = head;          // 用于遍历原链表
newhead = newtail = NULL;  // 新链表头、尾,初始为空
  • pcur:遍历原链表的指针,从头部 head 开始。
  • newhead 和 newtail:构建新链表的指针,初始时新链表无节点,均为 NULL

2. 遍历原链表,构建新链表

while (pcur) {if (pcur->val != val) {  // 仅处理值不为 val 的节点if (newhead == NULL) {  // 新链表为空,初始化头和尾newhead = newtail = pcur;} else {  // 新链表已有节点,连接到尾部newtail->next = pcur;newtail = newtail->next;}}pcur = pcur->next;  // 原链表指针后移
}
  • 循环逻辑:遍历原链表每个节点。
  • 条件判断:若节点值不等于 val,则加入新链表:
    • 新链表为空时,当前节点既是头 newhead 也是尾 newtail
    • 新链表非空时,将当前节点接到 newtail 之后,更新 newtail 到新位置。
  • 无论是否处理当前节点,pcur 都会后移,确保遍历完整原链表。

3. 处理新链表尾节点

if (newtail) {newtail->next = NULL;  // 断开与原链表的连接,保证新链表结构正确
}
return newhead;  // 返回新链表头
  • 若新链表存在(newtail 非空),将尾节点的 next 置为 NULL,避免残留原链表节点。
  • 最后返回新链表头 newhead,即删除目标节点后的结果。

四、核心思想与复杂度分析

核心思想

通过构建新链表的方式,仅保留值不为 val 的节点。这种方法简化了删除操作,无需处理原链表头节点删除的特殊情况,逻辑更清晰。

复杂度分析

  • 时间复杂度:(O(N)),仅遍历原链表一次,n 为链表节点数。
  • 空间复杂度:(O(1)),仅使用常数级额外指针,未创建新数据结构。

五、总结

本文通过构建新链表的方法解决了 “移除链表元素” 问题。该思路通过遍历原链表,有选择性地构建新链表,避免了复杂的节点删除操作。理解这种解题方式,有助于加深对链表操作的理解,在处理类似链表修改问题时提供新思路。

2.力扣 206. 反转链表:迭代法代码详解与实现

一、引言

链表反转是链表操作中的经典问题。在力扣 206 题中,给定单链表的头节点 head,需要反转链表并返回反转后的头节点。本文将详细讲解迭代法实现链表反转的代码逻辑,帮助大家理解这一经典算法。


二、迭代法核心思路

迭代法反转链表的核心是通过三个指针:n1(记录当前节点的前驱)、n2(当前处理的节点)、n3(暂存当前节点的后继),在遍历链表时逐步修改节点的 next 指针,使其指向前驱节点,最终完成链表反转。


三、代码逐行详解

/*** Definition for singly-linked list.* struct ListNode {*     int val;*     struct ListNode *next;* };*/
typedef struct ListNode ListNode;
struct ListNode* reverseList(struct ListNode* head) {// 处理空链表情况if (head == NULL) {return head;}ListNode* n1, *n2, *n3;n1 = NULL;       // n1 指向当前节点的前驱,初始无前驱n2 = head;       // n2 指向当前处理的节点(从原链表头开始)n3 = n2->next;   // 暂存当前节点的后继,防止断链while (n2) {     // 遍历链表,直到处理完所有节点// 反转指针:当前节点 n2 的 next 指向其前驱 n1n2->next = n1;// 指针后移n1 = n2;     // n1 变为当前节点n2 = n3;     // n2 处理下一个节点if (n3) {    // 若后继节点存在,n3 后移n3 = n3->next;}}return n1;       // 最终 n1 指向反转后的头节点
}

代码分段解析

  1. 空链表处理

    if (head == NULL) {return head;
    }
    

    若输入链表为空,直接返回 head,无需处理。

  2. 指针初始化

    ListNode* n1, *n2, *n3;
    n1 = NULL;
    n2 = head;
    n3 = n2->next;
    
    • n1 记录当前节点的前驱,初始为 NULL(头节点无前驱)。
    • n2 指向当前处理的节点(从原链表头开始)。
    • n3 暂存 n2 的后继节点,避免修改 n2->next 时链表断裂。
  3. 迭代反转

    while (n2) {n2->next = n1;n1 = n2;n2 = n3;if (n3) {n3 = n3->next;}
    }
    
    • 反转指针n2->next = n1 将当前节点的指针指向其前驱,完成局部反转。
    • 指针后移
      • n1 = n2n1 后移,变为当前节点。
      • n2 = n3n2 后移,处理下一个节点。
      • n3 = n3->next:若 n3 存在,继续暂存后续节点,确保链表遍历不断链。
  4. 返回结果

    return n1;
    

    循环结束后,n1 指向原链表的最后一个节点(即反转后的头节点),直接返回。


四、算法复杂度分析

  • 时间复杂度:(O(N)),需遍历链表一次,处理每个节点。
  • 空间复杂度:(O(1)),仅使用常数级别的额外指针变量,未占用额外线性空间。

五、总结

迭代法反转链表通过三个指针的配合,在遍历链表过程中完成指针反转,逻辑清晰且效率高。理解这一方法不仅能解决力扣 206 题,还能为后续复杂链表操作(如 K 个一组反转链表)打下基础。掌握指针操作的核心思想,就能灵活应对各类链表问题。

3. 力扣 876. 链表的中间结点:快慢指针解法深度解析

引言

在链表问题中,“找中间节点” 是一个经典题型。力扣 876 题要求我们找到链表的中间节点,若有两个中间节点,返回第二个。本文将通过快慢指针法高效解决该问题,深入解析代码逻辑与算法思想。


一、题目分析

题目描述:给定单链表的头结点 head,返回链表的中间节点。若节点数为偶数,返回第二个中间节点。
示例

  • 输入 [1,2,3,4,5],输出 3(奇数节点,中间一个)。
  • 输入 [1,2,3,4,5,6],输出 4(偶数节点,返回第二个中间节点)。

二、算法思路:快慢指针法

核心思想

定义两个指针:

  • 慢指针 slow:每次移动 1 步。
  • 快指针 fast:每次移动 2 步。
    当快指针无法继续移动(即 fast 或 fast->next 为空)时,慢指针恰好指向中间节点。

数学逻辑

  • 链表长度为奇数:快指针走完链表时,慢指针正好在正中间。
  • 链表长度为偶数:快指针超出链表范围时,慢指针落在第二个中间节点。

三、代码详细解析

/*** Definition for singly-linked list.* struct ListNode {*     int val;*     struct ListNode *next;* };*/
typedef struct ListNode ListNode;struct ListNode* middleNode(struct ListNode* head) {ListNode* slow = head; // 慢指针初始化,指向头节点ListNode* fast = head; // 快指针初始化,指向头节点// 循环条件:快指针能继续移动两步while (fast && fast->next) {slow = slow->next;    // 慢指针每次走 1 步fast = fast->next->next; // 快指针每次走 2 步}return slow; // 循环结束,slow 指向中间节点
}

代码逐行解释

  1. typedef struct ListNode ListNode;:简化结构体名称,方便后续代码书写。
  2. 初始化 slow 和 fast 均指向头节点 head
  3. while (fast && fast->next):确保快指针能移动两步,避免越界。
  4. 循环内,slow 走 1 步,fast 走 2 步。
  5. 循环结束后,slow 指向目标中间节点,直接返回。

四、示例演示

示例 1:输入 [1,2,3,4,5]

  • 初始:slow 和 fast 指向 1
  • 第一次循环:slow 到 2fast 到 3
  • 第二次循环:slow 到 3fast 到 5
  • 第三次检查:fast->next 为空,退出循环,返回 slow(值为 3)。

示例 2:输入 [1,2,3,4,5,6]

  • 初始:slow 和 fast 指向 1
  • 第一次循环:slow 到 2fast 到 3
  • 第二次循环:slow 到 3fast 到 5
  • 第三次循环:slow 到 4fast 尝试移动到 7(越界,退出循环)。
  • 返回 slow(值为 4)。

五、算法复杂度分析

  • 时间复杂度:O (N)。快慢指针遍历链表一次,仅需一次线性扫描。
  • 空间复杂度:O (1)。仅使用常数级额外空间(两个指针)。

总结

快慢指针法是解决链表中间节点问题的经典方案,通过控制指针移动速度的差异,高效定位目标节点。该方法不仅适用于本题,还广泛应用于链表环检测、倒数第 k 个节点等问题。掌握这一思想,能大幅提升链表类题目的解题效率。

4. 力扣 21. 合并两个有序链表:代码详解与思路分析

一、引言

在链表操作中,“合并两个有序链表” 是一道经典题目。它不仅考察对链表结构的理解,还考验逻辑处理能力。本文将以 LeetCode 21 题为例,详细讲解解题思路,并对代码逐行分析,帮助大家彻底掌握这一问题。

二、题目分析

题目要求:将两个升序链表合并为一个新的升序链表并返回。新链表通过拼接给定的两个链表的所有节点组成。 例如: 输入:l1 = [1,2,4]l2 = [1,3,4] 输出:[1,1,2,3,4,4]

三、解题思路

1. 虚拟头节点的妙用

创建一个虚拟头节点 nodehead,它作为新链表的 “临时起点”。这样做的好处是:避免处理空链表时的复杂边界条件,让代码逻辑更统一。

2. 遍历比较节点

同时遍历两个链表,比较当前节点值:

  • 若 l1 当前节点值更小,将其接入新链表;
  • 若 l2 当前节点值更小,将其接入新链表。 通过一个尾指针 nodetail 始终跟踪新链表的末尾,方便追加节点。

3. 处理剩余节点

遍历结束后,若其中一个链表还有剩余节点(因链表长度可能不同),直接将剩余部分接到新链表末尾(输入链表本身有序,无需再比较)。

四、代码逐行详解

#include <stdlib.h>// 链表节点定义
typedef struct ListNode {int val;struct ListNode *next;
} ListNode;struct ListNode* mergeTwoLists(struct ListNode* list1, struct ListNode* list2) {// 处理特殊情况:若其中一个链表为空,直接返回另一个if (list1 == NULL) {return list2;}if (list2 == NULL) {return list1;}// 创建虚拟头节点和尾指针ListNode* nodehead = (ListNode*)malloc(sizeof(ListNode));ListNode* nodetail = nodehead;nodehead->next = NULL;ListNode* l1 = list1;ListNode* l2 = list2;// 遍历合并while (l1 && l2) {if (l1->val > l2->val) {// l2 当前节点更小,接入新链表nodetail->next = l2;nodetail = nodetail->next;l2 = l2->next;} else {// l1 当前节点更小,接入新链表nodetail->next = l1;nodetail = nodetail->next;l1 = l1->next;}}// 处理剩余节点if (l1) {nodetail->next = l1;}if (l2) {nodetail->next = l2;}// 提取真实头节点,释放虚拟头节点ListNode* result = nodehead->next;free(nodehead);nodehead = NULL;return result;
}

代码解释:

  1. 特殊情况处理: 直接判断 list1 或 list2 为空的情况,返回另一个链表。

  2. 虚拟头节点初始化nodehead 作为虚拟头,nodetail 始终指向新链表末尾,初始时都指向 nodehead

  3. 遍历合并while 循环中,比较 l1 和 l2 当前节点值,将较小节点接入新链表,尾指针 nodetail 后移。

  4. 处理剩余节点: 循环结束后,若 l1 或 l2 有剩余,直接接到 nodetail 后。

  5. 释放内存与返回: 提取真实头节点 result,释放虚拟头节点 nodehead,避免内存泄漏。

五、完整代码测试

// 测试代码(可补充完整测试逻辑)
int main() {// 构建测试用例链表(此处省略具体构建过程)return 0;
}

六、总结

  • 时间复杂度:\(O(m + n)\),其中 m 和 n 分别为两个链表的长度,需遍历每个节点一次。
  • 空间复杂度:\(O(1)\),除虚拟头节点外,未使用额外与输入规模相关的空间。 通过虚拟头节点简化操作,结合遍历比较和剩余节点处理,我们高效解决了合并有序链表问题。这一思路在链表操作中非常通用,值得深入理解和记忆。

5. 牛客 链表分割算法详解: 风格实现与解析

一、问题描述

给定一个链表的头指针 ListNode* pHead 和一个整数值 x,需将所有小于 x 的节点排在其余节点之前,同时保持原有数据顺序,最后返回重排后的链表头指针。

二、代码实现

struct ListNode {int val;ListNode* next;ListNode(int x) : val(x), next(NULL) {}
};class Partition {
public:ListNode* partition(ListNode* pHead, int x) {// 初始化两个哑节点(哨兵节点)ListNode* lessHead = new ListNode(-1);  // 存储小于x的节点ListNode* lessTail = lessHead;ListNode* greaterHead = new ListNode(-1);  // 存储大于等于x的节点ListNode* greaterTail = greaterHead;ListNode* pCur = pHead;while (pCur) {if (pCur->val < x) {// 连接到less链表lessTail->next = pCur;lessTail = lessTail->next;} else {// 连接到greater链表greaterTail->next = pCur;greaterTail = greaterTail->next;}pCur = pCur->next;}// 处理greater链表的末尾,避免循环引用greaterTail->next = NULL;// 合并less和greater链表lessTail->next = greaterHead->next;// 释放哑节点内存ListNode* ret = lessHead->next;delete lessHead;delete greaterHead;return ret;}
};

三、代码详细解析

1. 链表节点定义

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

定义链表节点结构,val 存储节点值,next 指向下一节点,构造函数初始化节点值并默认 next 为 NULL

2. 初始化哑节点

ListNode* lessHead = new ListNode(-1);  
ListNode* lessTail = lessHead;
ListNode* greaterHead = new ListNode(-1);  
ListNode* greaterTail = greaterHead;

创建两个哑节点 lessHead 和 greaterHead,作为临时链表的头。lessTail 和 greaterTail 跟踪各自链表的尾部,便于后续节点追加。

3. 遍历原链表并分割

ListNode* pCur = pHead;
while (pCur) {if (pCur->val < x) {lessTail->next = pCur;lessTail = lessTail->next;} else {greaterTail->next = pCur;greaterTail = greaterTail->next;}pCur = pCur->next;
}

遍历原链表:

  • 若节点值小于 x,追加到 less 链表(lessTail 后);
  • 否则,追加到 greater 链表(greaterTail 后);
  • 移动 pCur 继续遍历。

4. 合并链表与内存释放

greaterTail->next = NULL;  // 断开greater链表尾
lessTail->next = greaterHead->next;  // 合并less和greaterListNode* ret = lessHead->next;
delete lessHead;
delete greaterHead;
return ret;
  • 处理 greater 链表尾,避免悬空引用;
  • 合并 less 与 greater 链表;
  • 释放哑节点内存,返回合并后链表的头节点。

四、算法分析

  • 时间复杂度:O (n),仅一次遍历链表。
  • 空间复杂度:O (1),使用常数级额外空间(哑节点)。
  • 稳定性:保持节点相对顺序,因按遍历顺序分配到两个链表。

五、总结

通过哑节点简化链表操作,将原链表拆分为两个子链表后合并,高效实现链表分割。此方法逻辑清晰,内存管理规范,是链表操作的经典思路。掌握哑节点的使用,能更便捷地处理链表头节点相关问题。

6.牛客 链表回文结构判断:经典解法详解与实现

引言

在链表算法问题中,判断链表是否为回文结构是一道经典题目。回文结构意味着链表正读和反读的节点值序列一致,如 1->2->2->1。本文将介绍一种高效解法:利用快慢指针找中间节点,反转后半部分链表,最后对比前后部分,实现时间复杂度 O (n)、空间复杂度 O (1) 的最优解。


一、解题思路分析

1. 找中间节点(快慢指针法)

  • 原理:快指针每次走两步,慢指针每次走一步。当快指针到达链表末尾时,慢指针恰好位于中间位置。若链表长度为偶数,慢指针指向后半部分第一个节点。
  • 作用:将链表拆分为前后两部分,为后续反转后半部分做准备。

2. 反转后半部分链表

  • 原理:通过迭代法,调整节点的 next 指针方向,实现链表反转。
  • 作用:反转后,后半部分链表的节点顺序与前半部分对称,便于直接对比。

3. 前后对比验证

  • 原理:同时遍历原前半部分链表和反转后的后半部分链表,逐节点比较值是否相等。
  • 作用:若所有对应节点值一致,链表为回文结构;否则不是。

二、代码实现与逐函数解析

1. 定义链表节点结构

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

2. middleNode:找中间节点

ListNode* middleNode(ListNode* head) {  ListNode* slow, *fast;  slow = fast = head;  while (fast && fast->next) {  slow = slow->next;       // 慢指针一步  fast = fast->next->next; // 快指针两步  }  return slow; // 返回中间节点  
}  
  • 逻辑:快慢指针从头部出发,快指针移动到末尾时,慢指针指向中间。例如,链表 1->2->3->4,最终 slow 指向 3

3. reverseList:反转链表

ListNode* reverseList(ListNode* head) {  if (head == NULL) return head;  ListNode* n1 = NULL, *n2 = head, *n3 = head->next;  while (n2) {  n2->next = n1;   // 反转指针方向  n1 = n2;         // 指针后移  n2 = n3;  if (n3) n3 = n3->next; // 避免空指针  }  return n1; // 反转后新头节点  
}  
  • 逻辑:通过 n1(前驱)、n2(当前)、n3(后继)三个指针,逐步调整节点指向。例如,输入 2->1,反转后返回 1->2

4. chkPalindrome:判断回文

bool chkPalindrome(ListNode* A) {  ListNode* mid = middleNode(A);       // 找中间节点  ListNode* right = reverseList(mid);  // 反转后半部分  ListNode* left = A;                  // 前半部分起点  while (right) {  if (left->val != right->val) return false; // 对比失败,非回文  left = left->next;  right = right->next;  }  return true; // 全部匹配,是回文  
}  
  • 逻辑:先拆分、反转链表,再同时遍历前后两部分。若节点值不一致,直接返回 false;遍历结束无冲突,返回 true

三、代码优势与复杂度分析

  • 时间复杂度:O (n)。找中间节点、反转链表、对比操作均遍历链表一次,总时间与链表长度成正比。
  • 空间复杂度:O (1)。仅使用有限个指针变量,未占用额外数据结构空间。

四、总结

本文通过 “快慢指针 + 反转链表” 的经典思路,实现了链表回文结构的高效判断。这种方法融合了链表操作的基础技巧,是解决链表问题的常用范式。理解并掌握该解法,对提升链表算法能力有很大帮助,也可迁移到其他链表相关问题(如链表中点查找、链表反转变形题)的解决中。

7. 力扣 160. 相交链表:函数实现深度解析

引言

在链表算法中,寻找两个单链表的相交节点是经典问题。本文围绕 getIntersectionNode 函数,详细解析每一行代码的实现逻辑,助读者深入理解解法核心思想。

函数整体框架

struct ListNode *getIntersectionNode(struct ListNode *headA, struct ListNode *headB) {  // 初始化指针与长度变量  ListNode* pa = headA;  ListNode* pb = headB;  int sizeA = 0, sizeB = 0;  // 计算链表长度  while (pa) { ... }  while (pb) { ... }  // 处理长度差,对齐链表  int gap = abs(sizeA - sizeB);  ListNode* shortList = headA;  ListNode* longList = headB;  if (sizeA > sizeB) { ... }  while (gap--) { ... }  // 同步遍历找相交节点  while (shortList) { ... }  return NULL;  
}  

函数分为初始化、计算链表长度、处理长度差对齐链表、同步遍历查找相交节点四部分,以下逐一解析。

1. 初始化指针与长度变量

ListNode* pa = headA;  
ListNode* pb = headB;  
int sizeA = 0, sizeB = 0;  
  • 作用:定义 papb 遍历链表 headAheadBsizeAsizeB 记录链表长度,为后续计算与操作奠基。
  • 说明:指针初始化指向链表头节点,长度变量置 0,便于后续累加。

2. 计算链表长度

while (pa) {  sizeA++;  pa = pa->next;  
}  
while (pb) {  sizeB++;  pb = pb->next;  
}  
  • 计算链表 A 长度
    • while (pa) 循环:pa 非空时,sizeA 自增 1(统计节点数),pa 后移。pa 为 NULL 时,完成遍历,sizeA 存储链表 A 长度。
  • 计算链表 B 长度
    • 逻辑同链表 A,通过 while (pb) 统计 sizeB,直至 pb 为 NULL
  • 意义:获取两链表长度,为后续长度差计算与链表对齐提供数据。

3. 处理长度差,对齐链表

int gap = abs(sizeA - sizeB);  
ListNode* shortList = headA;  
ListNode* longList = headB;  
if (sizeA > sizeB) {  longList = headA;  shortList = headB;  
}  
while (gap--) {  longList = longList->next;  
}  
  • 计算长度差
    • gap = abs(sizeA - sizeB):用 abs 取长度差绝对值,确保 gap 非负。
  • 确定长短链表指针
    • if (sizeA > sizeB) 判断链表长短。若 A 更长,longList 指向 headAshortList 指向 headB;否则保持初始赋值。
  • 长链表指针移动
    • while (gap--) 循环:长链表指针 longList 后移 gap 步。如 A 比 B 长 3 步,longList 移动 3 步后,两链表剩余部分长度一致,为找相交节点做准备。

4. 同步遍历找相交节点

while (shortList) {  if (longList == shortList) {  return longList;  }  shortList = shortList->next;  longList = longList->next;  
}  
return NULL;  
  • 遍历查找
    • while (shortList) 循环:shortList 非空时,检查 longList 与 shortList 是否指向同一节点(地址相同)。若相同,找到相交节点,直接返回。
    • 未找到则 shortListlongList 同时后移,继续遍历。
  • 返回结果
    • 循环结束未找到相交节点,说明两链表不相交,返回 NULL

总结

getIntersectionNode 函数通过计算链表长度、处理长度差对齐链表、同步遍历三步,高效解决相交链表问题。每一步目标明确,时间复杂度 O (M + N),空间复杂度 O (1),是简洁高效的解法。理解函数实现细节,不仅掌握本题解法,更为解决其他链表问题提供思路。

8. 力扣 141 环形链表问题详解:快慢指针法判断链表是否有环

一、引言

在链表相关的算法问题中,“判断链表是否有环” 是经典题目。本文将通过 C 语言实现,结合快慢指针算法,详细解析每一行代码的逻辑,帮助读者深入理解解题思路。


二、算法思路

快慢指针法: 定义两个指针,慢指针 slow 每次移动一步,快指针 fast 每次移动两步。若链表有环,快指针最终会在环内追上慢指针(相遇);若无环,快指针会先到达链表末尾(NULL)。


三、代码实现(C 语言)

#include <stdbool.h>// 链表节点定义
struct ListNode {int val;struct ListNode *next;
};// 类型重命名,简化代码书写
typedef struct ListNode ListNode;bool hasCycle(ListNode *head) {ListNode *slow, *fast;slow = fast = head; // 初始时,快慢指针都指向头节点while (fast && fast->next) { // 快指针未到末尾时循环slow = slow->next;       // 慢指针每次移动一步fast = fast->next->next; // 快指针每次移动两步if (fast == slow) {      // 快慢指针相遇,说明有环return true;}}return false; // 循环结束未相遇,说明无环
}

四、代码逐行详解

1. 链表节点定义

struct ListNode {int val;struct ListNode *next;
};
typedef struct ListNode ListNode;
  • struct ListNode 定义链表节点,包含存储值的 val 和指向下一节点的 next 指针。
  • typedef 重命名类型,后续代码中可用 ListNode 代替 struct ListNode,简化书写。

2. 函数参数与初始化

bool hasCycle(ListNode *head) {ListNode *slow, *fast;slow = fast = head;
  • 函数 hasCycle 接收链表头节点 head,返回布尔值(true 有环,false 无环)。
  • 初始化快慢指针,均指向头节点 head,从同一位置开始移动。

3. 循环条件

while (fast && fast->next) {
  • fast && fast->next 确保快指针未到达链表末尾。若快指针为空或快指针的下一个节点为空,说明链表无环,退出循环。

4. 指针移动

slow = slow->next;
fast = fast->next->next;
  • slow = slow->next;:慢指针每次沿 next 移动一步。
  • fast = fast->next->next;:快指针每次移动两步,加速遍历。

5. 相遇判断

if (fast == slow) {return true;
}
  • 若快慢指针相遇(fast == slow),说明链表存在环,立即返回 true

6. 最终返回结果

return false;
  • 循环结束后未触发相遇条件,说明链表无环,返回 false

五、总结

快慢指针法通过不同的移动速度,高效判断链表是否有环,时间复杂度为 \(O(n)\),空间复杂度为 \(O(1)\)。理解这一思路后,不仅能解决本题,还能延伸到其他链表问题(如找环入口)。掌握指针操作和逻辑判断,是解决链表问题的关键。

9. 力扣 142 环形链表 II 问题详解:代码逐行解析与算法原理深度剖析

引言

在链表相关的算法问题中,“环形链表 II” 是一个经典题目。给定链表头节点 head,需要判断链表是否有环,若有环则找到环的入口节点。本文将通过详细的代码解析和算法原理分析,带大家深入理解这一问题的解决思路。


链表节点结构体定义

struct ListNode {int val;struct ListNode *next;
};
typedef struct ListNode ListNode;
  • 结构体作用:定义单链表的节点结构。每个节点包含两个成员,int val 用于存储节点的值,struct ListNode *next 是指向下一个节点的指针,若 next 为 NULL,则表示当前节点是链表的尾节点。
  • typedef 用途:通过 typedef 为 struct ListNode 定义别名 ListNode。这样在后续代码中,就可以直接使用 ListNode 代替 struct ListNode,简化代码书写,让代码更简洁易读。

detectCycle 函数逐行解析

1. 函数初始化

struct ListNode* detectCycle(struct ListNode *head) {ListNode* slow = head;  // 慢指针,每次移动 1 步ListNode* fast = head;  // 快指针,每次移动 2 步
  • 初始化快慢指针:定义两个指针 slow(慢指针)和 fast(快指针),它们都从链表的头节点 head 出发。慢指针每次移动一个节点,快指针每次移动两个节点。这是利用快慢指针的速度差来判断链表是否有环的经典初始化方式。若链表有环,快指针最终会追上慢指针(即相遇);若链表无环,快指针会先到达链表末尾(NULL)。

2. 判断链表是否有环

    while (fast && fast->next) {slow = slow->next;          // 慢指针移动 1 步fast = fast->next->next;    // 快指针移动 2 步if (fast == slow) {        // 快慢指针相遇,说明有环
  • 循环条件while (fast && fast->next) 确保快指针在移动时,fast 和 fast->next 都有效,避免指针越界。
  • 指针移动逻辑
    • slow = slow->next;:慢指针每次向前移动一个节点。
    • fast = fast->next->next;:快指针每次向前移动两个节点。
  • 环存在的判断:当 fast == slow 时,快慢指针相遇,说明链表中存在环,此时进入寻找环入口节点的逻辑。

3. 寻找环入口节点

            ListNode* pcur = head;  // 新指针 pcur 从链表头部出发while (slow != pcur) {  // 当 slow 和 pcur 未相遇时pcur = pcur->next;  // pcur 每次移动 1 步slow = slow->next;  // slow 继续移动 1 步}return pcur;  // 相遇点即为环入口
  • 数学原理支撑:设链表头到环入口的距离为 a,环入口到快慢指针相遇点的距离为 b,环的周长为 c。相遇时,慢指针走过的距离是 a + b,快指针走过的距离是 a + b + n*cn 为快指针绕环的圈数)。由于快指针速度是慢指针的 2 倍,可得 2(a + b) = a + b + n*c,化简后得到 a = n*c - b。这表明,从链表头(pcur)和相遇点(slow)同时出发,每次移动 1 步,最终会在环入口相遇。
  • 代码实现逻辑
    • 定义新指针 pcur,使其从链表头部 head 出发。
    • 通过 while (slow != pcur) 循环,让 pcur 和 slow 每次各移动一个节点,直到两者相遇。此时,pcur 指向的节点就是环的入口节点,直接返回该节点。

4. 处理无环情况

    }return NULL;  // 若循环结束未相遇,说明链表无环,返回 NULL
}
  • 逻辑说明:如果 while (fast && fast->next) 循环正常结束(即快指针移动到链表末尾,fast 或 fast->next 为 NULL),说明链表中不存在环,此时直接返回 NULL

算法总结

  • 时间复杂度:整个算法中,快慢指针判断环的过程和寻找环入口的过程,时间复杂度均为 \(O(n)\),整体时间复杂度为 \(O(n)\)。
  • 空间复杂度:仅使用了有限的指针变量,空间复杂度为 \(O(1)\),没有额外的空间消耗。
  • 算法核心:利用快慢指针判断链表是否有环,再通过数学推导找到环入口节点。这种方法巧妙地避免了使用哈希表等额外数据结构,高效解决了问题。

10. 力扣 138. 随机链表的复制:函数级详解与完整流程剖析

一、问题概述

复制带有随机指针 random 的链表时,需确保新链表的 next 和 random 指针均指向新链表节点。本文通过 “三步法” 实现,逐函数解析代码逻辑。


二、代码实现与函数详解

1. 节点创建函数:buyNode

Node* buyNode(int x) {Node* newnode = (Node*)malloc(sizeof(Node));newnode->val = x;newnode->next = newnode->random = NULL;return newnode;
}
  • 功能:动态分配内存创建节点,初始化 val,并将 nextrandom 置空。
  • 细节:使用 malloc 分配内存,避免内存泄漏;初始化指针,防止野指针。
  • 作用:为复制链表提供节点创建工具,后续复制的节点均由此生成。

2. 插入复制节点:AddNode

void AddNode(Node* head) {Node* pcur = head;while (pcur) {Node* newnode = buyNode(pcur->val);Node* next = pcur->next;newnode->next = next;pcur->next = newnode;pcur = next;}
}
  • 执行流程
    1. 遍历原链表,对每个节点 pcur
      • 创建值相同的复制节点 newnode
      • 保存原节点的后续节点 next
      • 将复制节点插入原节点后,形成 原节点->复制节点->原后续节点 结构。
    2. 遍历完成后,链表变为原节点与复制节点交替排列。
  • 核心作用:构建原节点与复制节点的相邻关系,为处理 random 指针奠定结构基础。

3. 设置随机指针:setRandom

void setRandom(Node* head) {Node* pcur = head;while (pcur) {Node* copy = pcur->next;if (pcur->random) {copy->random = pcur->random->next;}pcur = copy->next;}
}
  • 逻辑解析
    • 遍历原链表,copy 指向当前原节点的复制节点。
    • 若原节点 pcur 的 random 有指向,则其复制节点的 random 指向原 random 指向节点的复制节点(即 pcur->random->next)。
  • 意义:利用原节点与复制节点的相邻关系,精准定位复制节点 random 的指向,确保复制链表逻辑正确。

4. 主函数整合:copyRandomList

struct Node* copyRandomList(struct Node* head) {if (head == NULL) {return head;}AddNode(head);setRandom(head);Node* pcur = head;Node* copyHead = pcur->next;Node* copyTail = copyHead;while (copyTail->next) {pcur = copyTail->next;copyTail->next = pcur->next;copyTail = copyTail->next;}return copyHead;
}
  • 执行步骤
    1. 边界检查:原链表为空时直接返回。
    2. 插入复制节点:调用 AddNode 构建交替链表。
    3. 设置随机指针:调用 setRandom 完善复制节点的 random
    4. 拆分链表
      • 提取复制链表头节点 copyHead
      • 遍历调整复制节点的 next,使其指向后续复制节点,完成与原链表的分离。

三、算法总结

  • 空间与时间:三次遍历链表,时间复杂度 O (n);利用原链表空间插入复制节点,空间复杂度 O (1)(除结果外)。
  • 核心思想:通过插入节点构建关系,借助相邻节点定位 random,最后拆分离出结果,是解决随机链表复制的经典方法。

相关文章:

数据结构C语言练习(单双链表)

本篇练习题(单链表)&#xff1a; 1.力扣 203. 移除链表元素 2.力扣 206. 反转链表 3.力扣 876. 链表的中间结点 4.力扣 21. 合并两个有序链表 5. 牛客 链表分割算法详解 6.牛客 链表回文结构判断 7. 力扣 160. 相交链表 8. 力扣 141 环形链表 9. 力扣 142 环形链表 II…...

Linux驱动开发 中断处理

目录 序言 1.中断的概念 2.如何使用中断 中断处理流程 中断上下文限制 屏蔽中断/使能 关键区别与选择 上半部中断 下半部中断 软中断&#xff08;SoftIRQ&#xff09; 小任务(Tasklet) 工作队列&#xff08;Workqueue&#xff09; 线程 IRQ&#xff08;Threaded IRQ…...

C++ set map

1.set和map是什么 set和map是 C STL 提供的容器&#xff0c;用于高效的查找数据&#xff0c;底层采用红黑树实现&#xff0c;其中set是Key模型&#xff0c;map是Key-Value模型 set和map的基本使用较为简单&#xff0c;这里不再叙述&#xff0c;直接进入实现环节 2.set和map的…...

Vue2和Vue3响应式的基本实现

目录 简介Vue2 响应式Vue2 响应式的局限性 Vue3 响应式Vue3 响应式的优点 Vue2 和 Vue3 响应式对比 简介 在 Vue 框架中&#xff0c;数据的响应式是其核心特性之一。当页面数据发生变化时&#xff0c;我们希望界面能自动更新&#xff0c;而不是手动操作 DOM。这就需要对数据进…...

PyQt6实例_批量下载pdf工具_界面开发

目录 前置&#xff1a; 代码&#xff1a; 视频&#xff1a; 前置&#xff1a; 1 本系列将以 “PyQt6实例_批量下载pdf工具”开头&#xff0c;放在 【PyQt6实例】 专栏 2 本系列涉及到的PyQt6知识点&#xff1a; 线程池&#xff1a;QThreadPool,QRunnable&#xff1b; 信号…...

FOC 控制笔记【三】磁链观测器

一、磁链观测器基础 1.1 什么是磁链 磁链&#xff08;magnetic linkage&#xff09;是电磁学中的一个重要概念&#xff0c;指导电线圈或电流回路所链环的磁通量。单位为韦伯&#xff08;Wb&#xff09;&#xff0c;又称磁通匝。 公式为&#xff1a; 线圈匝数 穿过单匝数的…...

前端Material-UI面试题及参考答案

目录 Material-UI 的设计理念与 Material Design 规范的关系是什么? 如何通过 npm/yarn/pnpm 安装 Material-UI 的核心依赖? Material-UI 的默认主题系统如何实现全局样式管理? 如何在项目中配置自定义字体和颜色方案? 什么是 emotion 和 styled-components,它们在 Ma…...

【LeetCode基础算法】链表所有类型

1. 遍历链表 二进制链表转整数找出临界点之间的最小和最大距离 2. 删除节点 移除链表元素从链表中移除在数组中存在的节点删除排序链表中的重复元素删除排序链表中的重复元素 II 3. 插入节点 在链表中插入最大公约数 计算最大公约数的内置函数gcd(a,b)&#xff0c;也可以m…...

备赛蓝桥杯之第十六届模拟赛第1期职业院校组第五题:回忆画廊

提示&#xff1a;本篇文章仅仅是作者自己目前在备赛蓝桥杯中&#xff0c;自己学习与刷题的学习笔记&#xff0c;写的不好&#xff0c;欢迎大家批评与建议 由于个别题目代码量与题目量偏大&#xff0c;请大家自己去蓝桥杯官网【连接高校和企业 - 蓝桥云课】去寻找原题&#xff0…...

51 驱动 INA219 电流电压功率测量

文章目录 一、INA219简介二、引脚功能三、寄存器介绍1.配置寄存器 0x002.分流电压寄存器 0x013.总线电压寄存器 0x024.功率寄存器 0x035.电流寄存器 0x046.基准寄存器 0x05 四、IIC 时序说明1.写时序2.读时序 五、程序六、实验现象1.线路图2.输出数据 一、INA219简介 INA219是…...

JavaScript弹出框的使用:对话框、确认框、提示框、弹窗操作

关于 Window对象和 Document 对象的详细使用,系列文章: 《Window对象的常用属性和方法》 《Document对象的常用属性和方法:getElementById()、getElementsByName()、createElement()方法》 《Document获取元素并修改内容:getElementById()方法、value属性、innerHTML属性、…...

【设计模式】深入解析设计模式:门面模式(外观模式)的定义、优点和代码实现

门面模式&#xff08;外观模式&#xff09; SLF4J是门面模式的典型应用&#xff08;但不仅仅使用了门面模式&#xff09;。 门面模式定义 门面模式&#xff08;Facade Pattern&#xff09;又称为外观模式&#xff0c;提供了一个统一的接口&#xff0c;用来访问子系统中的一群…...

UE5学习笔记 FPS游戏制作34 触发器切换关卡

文章目录 搭建关卡制作触发器传送门显示加载界面 搭建关卡 首先搭建两个关卡&#xff0c;每个关卡里至少要有一个角色 制作触发器传送门 1 新建一个蓝图&#xff0c;父类为actor&#xff0c;命名为portal&#xff08;传送门&#xff09; 2 为portal添加一个staticMesh&#…...

UE5学习笔记 FPS游戏制作26 UE中的UI

文章目录 几个概念创建一个UI蓝图添加UI获取UI的引用 切换设计器和UI蓝图将UI添加到游戏场景锚点轴点slotSizeToContent三种UI数据更新方式(Text、Image)函数绑定属性绑定事件绑定 九宫格分割图片按钮设置图片绑定按下事件 下拉框创建添加数据修改样式常用函数 滚动框创建添加数…...

Spring Boot分布式项目重试实战:九种失效场景与正确打开方式

在分布式系统架构中&#xff0c;网络抖动、服务瞬时过载、数据库死锁等临时性故障时有发生。本文将通过真实项目案例&#xff0c;深入讲解Spring Boot项目中如何正确实施重试机制&#xff0c;避免因简单粗暴的重试引发雪崩效应。 以下是使用Mermaid语法绘制的重试架构图和决策…...

首个物业plus系列展 2025上海国际智慧物业博览会开幕

AI赋能服务升级&#xff01;首个“物业plus”系列展 2025上海国际智慧物业博览会盛大开幕 3月31日&#xff0c;2025上海国际智慧物业博览会&#xff08;简称“上海物博会”&#xff09;在上海新国际博览中心N4馆隆重开幕。本届展会由广州旭杨国际展览有限公司主办&#xff0c…...

《C++多线程下单例 “锁钥” 法则》

一、概述 本文章介绍了一段 C 代码&#xff0c;该代码实现了在多线程环境下的单例模式。单例模式确保一个类只有一个实例&#xff0c;并提供全局访问点。在多线程场景中&#xff0c;需要额外的同步机制来保证单例对象创建的线程安全性。单例模式在许多场景中都有重要应用&#…...

WEB或移动端常用交互元素及组件 | Axure / 元件类型介绍(表单元件、菜单和表格 、流程元件、标记元件)

文章目录 引言I Axure / 元件类型介绍基本元件表单元件菜单和表格流程元件标记元件II Axure 基础Axure / 常用功能介绍Axure / 常用元素实例Axure / 动态交互实例Axure / 常用设计分辨率推荐III Axure / 创建自己的元件库元件库作用元件库的创建及使用引言 I Axure / 元件类型介…...

开发环境解决Secure Cookie导致302重定向

问题现象与根源分析 故障现象 前端本地开发时&#xff08;HTTP协议&#xff09;&#xff0c;调用接口返回302 Found状态码浏览器控制台警告&#xff1a;“Cookie被阻止&#xff0c;因为设置了Secure属性但未通过HTTPS传输”登录态无法保持&#xff0c;页面陷入重定向循环 技…...

华为三进制逻辑与高维量子计算的对比分析

此博客深入探讨华为三进制逻辑状态的技术意义&#xff0c;并与高维量子计算系统进行对比。文章将全面展开技术原理、实现机制、计算能力对比、未来应用前景等方面的内容。 目录 引言 华为三进制逻辑的创新意义 2.1 二进制逻辑的局限与历史探索 2.2 三进制逻辑的优势&#xff…...

网红指路机器人是否支持环境监测功能?

嘿呀&#xff0c;你可知道&#xff1f;如今的叁仟网红指路机器人那可太牛啦&#xff01;它们可不单单局限于为行人指明方向&#xff0c;还纷纷兼职当起了 “环境小卫士”&#xff0c;为咱们的城市生活注入了前所未有的超智能便利。就拿那个依托叁仟智慧杆打造的数智指路机器人来…...

【进阶】vscode 中使用 cmake 编译调试 C++ 工程

基于 MSYS2 的 MinGW-w64 GCC 工具链与 CMake 构建系统&#xff0c;结合VSCode及其扩展插件&#xff08; ms-vscode.cmake-tools&#xff09;&#xff0c;可实现高效的全流程C开发调试。既可通过 VSCode 可视化界面&#xff08;命令面板、状态栏按钮&#xff09;便捷完成配置、…...

突发,国行 iPhone 17,支持 eSIM

古人云“无心生大用”&#xff0c;往往你感到绝望的时候&#xff0c;转机就莫名其妙的来了。 根据供应链的最新消息&#xff0c;国行 iPhone 17 Air&#xff0c;有望用上 eSIM。 不仅如此&#xff0c;国产手机厂商&#xff0c;也计划推出类似iPhone 17 Air的超薄机型&#xf…...

红宝书第二十二讲:详解JavaScript类型化数组与二进制数据处理

红宝书第二十二讲&#xff1a;详解JavaScript类型化数组与二进制数据处理 资料取自《JavaScript高级程序设计&#xff08;第5版&#xff09;》。 查看总目录&#xff1a;红宝书学习大纲 一、为什么需要类型化数组&#xff1f; 普通JavaScript数组&#xff08;Array&#xff0…...

Elasticsearch安全与权限控制指南

在Elasticsearch维护中&#xff0c;安全管理是保障数据合规性和集群稳定性的关键。本文将详细介绍用户与角色管理、索引/字段级权限控制、HTTPS加密通信、审计日志与合规性检查等核心安全实践&#xff0c;希望可以帮助你构建更安全的Elasticsearch环境。 1 用户与角色管理 1.1…...

SAP 学习笔记 - 系统移行业务 - MALSY(由Excel 移行到SAP 的收费工具)

以前有关移行&#xff0c;也写过一些文章&#xff0c;比如 SAP 学习笔记 - 系统移行业务 - Migration cockpit工具 - 移行Material&#xff08;品目&#xff09;-CSDN博客 SAP 学习笔记 - 系统移行业务 - Migration cockpit工具2 - Lot导入_sap cockpit-CSDN博客 SAP学习笔记…...

【群智能算法改进】一种改进的蜣螂优化算法IDBO[3](立方混沌映射Cubic、融合鱼鹰勘探策略、混合高斯柯西变异)【Matlab代码#92】

文章目录 【获取资源请见文章第5节&#xff1a;资源获取】1. 原始DBO算法2. 改进后的IDBO算法2.1 立方混沌映射Cubic种群初始化2.2 融合鱼鹰勘探策略2.3 混合高斯柯西变异 3. 部分代码展示4. 仿真结果展示5. 资源获取 【获取资源请见文章第5节&#xff1a;资源获取】 1. 原始DB…...

《异常检测——从经典算法到深度学习》30. 在线服务系统中重复故障的可操作和可解释的故障定位

《异常检测——从经典算法到深度学习》 0 概论1 基于隔离森林的异常检测算法 2 基于LOF的异常检测算法3 基于One-Class SVM的异常检测算法4 基于高斯概率密度异常检测算法5 Opprentice——异常检测经典算法最终篇6 基于重构概率的 VAE 异常检测7 基于条件VAE异常检测8 Donut: …...

座舱与智驾“双轮驱动”,芯擎科技打造智能汽车“芯”标杆

在比亚迪、吉利、奇瑞等各大主机厂打响“全民智驾”的关键时期&#xff0c;以芯擎科技为代表中国芯片厂商开始“放大招”。 2025年3月27日&#xff0c;芯擎科技在南京举办了“擎随芯动、智融万象”生态科技日&#xff0c;重磅发布了“星辰一号”、“星辰一号Lite”&#xff0c…...

观察者模式在Java单体服务中的运用

观察者模式主要用于当一个对象发生改变时&#xff0c;其关联的所有对象都会收到通知&#xff0c;属于事件驱动类型的设计模式&#xff0c;可以对事件进行监听和响应。下面简单介绍下它的使用&#xff1a; 1 定义事件 import org.springframework.context.ApplicationEvent;pu…...

html5时钟升级!支持切换深浅模式 Canvas实现现代化动态时钟

HTML5 Canvas实现现代化动态时钟 这里写目录标题 HTML5 Canvas实现现代化动态时钟项目介绍技术实现1. 项目架构2. Canvas绘图实现2.1 表盘绘制2.2 刻度绘制2.3 指针绘制 3. 动画效果4. 主题切换 项目亮点技术要点总结项目收获改进方向结语 项目介绍 本项目使用HTML5 Canvas技术…...

Scala(2)

For循环控制 循环守卫 基本语法 for(i <- 1 to 3 if i ! 2) { print(i " ") }println() 说明&#xff1a; 循环守卫&#xff0c;即循环保护式&#xff08;也称条件判断式&#xff0c;守卫&#xff09;。保护式为 true 则进入循环体内部&#xff0c;为false 则跳…...

DataGear 5.3.0 制作支持导出表格数据的数据可视化看板

DataGear 内置表格图表底层采用的是DataTable表格组件&#xff0c;默认并未引入导出数据的JS支持库&#xff0c;如果有导出表格数据需求&#xff0c;则可以在看板中引入导出相关JS支持库&#xff0c;制作具有导出CSV、Excel、PDF功能的表格数据看板。 在新发布的5.3.0版本中&a…...

项目-苍穹外卖(十六) Apache ECharts+数据统计

一、介绍 二、营业额统计 需求分析和设计&#xff1a; Controller: Service: /*** 营业额统计* param begindate* param enddate* return* */Overridepublic TurnoverReportVO turnoverStatistics(LocalDate begindate, LocalDate enddate) {//创建时间集合List<LocalDate&…...

使用 PowerShell 脚本 + FFmpeg 在 Windows 系统中批量计算 MP4视频 文件的总时长

步骤 1&#xff1a;安装 FFmpeg 访问 FFmpeg 官网(Download FFmpeg)&#xff0c;下载 Windows 版编译包&#xff08;如 ffmpeg-release-full.7z&#xff09;。或者到&#xff08;https://download.csdn.net/download/zjx2388/90539014&#xff09;下载完整资料 解压文件&#…...

低成本文件共享解决方案:Go File本地Docker部署与外网访问全记录

文章目录 前言1. 安装Docker2. Go File使用演示3. 安装cpolar内网穿透4. 配置Go File公网地址5. 配置Go File固定公网地址 前言 在这个信息爆炸的时代&#xff0c;谁还没遇到过这样的囧事呢&#xff1f;正在办公室电脑上赶工报告&#xff0c;手机却突然蹦出一条紧急邮件&#…...

python文件的基本操作和文件读写

目录 文件的基本操作 文件读写 文件的基本操作 Python 中对文件的基本操作主要包括打开文件、读取文件、写入文件和关闭文件等操作。下面是一个简单的示例&#xff1a; 打开文件&#xff1a; file open(example.txt, r) # 使用 open() 函数打开一个名为 example.txt 的文…...

大数据与datax1.0

一、datax含义 是一个数据搬运工具 二、需要注意的点 插件(plugin)下面的reader和writer 要删除(第一步执行肯定会报错 所以请记得一定要删除reader和writer下的隐藏文件) 三、心得 做任何事要事半功倍,而不要事倍功半,好的学习方法永远比盲目的努力更重要--------谨记3.31…...

蚂蚁集团主导的ISO密码学国际标准立项,纳入国产算法

蚂蚁集团主导的ISO密码学国际标准 ISO 25330-3 立项&#xff0c; 国产算法Ferret成为标准方案。 近日&#xff0c;在美国弗吉尼亚州举行的 ISO/IEC JTC 1/SC 27 全体会议上&#xff0c;ISO/IEC 25330第三部分《Information Security — Oblivious Transfer — Part 3: Obliv…...

【新人系列】Golang 入门(十):错误处理详解 - 上

✍ 个人博客&#xff1a;https://blog.csdn.net/Newin2020?typeblog &#x1f4dd; 专栏地址&#xff1a;https://blog.csdn.net/newin2020/category_12898955.html &#x1f4e3; 专栏定位&#xff1a;为 0 基础刚入门 Golang 的小伙伴提供详细的讲解&#xff0c;也欢迎大佬们…...

Unity 2022.3.x部分Android设备播放视频黑屏问题

Android平台视频兼容性问题很多…类似的黑屏问题真的很头大&#xff0c;总结一些常见问题&#xff1a; 1. 视频文件不支持压缩 如果使用AssetBundle加载视频&#xff0c;这个AssetBundle压缩格式要选None。有人可能会说最新版Unity已经支持bundle压缩下播放视频&#xff0c;稳…...

基于Python的Django框架的个人博客管理系统

标题:基于Python的Django框架的个人博客管理系统 内容:1.摘要 本文围绕基于Python的Django框架构建个人博客管理系统展开。背景方面&#xff0c;随着互联网发展&#xff0c;个人博客成为信息分享与交流重要平台&#xff0c;传统博客管理系统在功能与灵活性上存在不足。目的是开…...

Unity加载OSGB倾斜摄影数据

Unity加载OSGB倾斜摄影数据 显而易见有一个最方便的办法就是使用CesiumForUnity确定是可以通过osgb数据转换成3dtiles进行加载的&#xff0c;然而有没有直接加载osgb格式数据的方法呢&#xff1f; 我们知道osgb的osg推出的倾斜摄影数据的数据结构&#xff0c;所以&#xff0c…...

RabbitMQ简单介绍和安装

RabbitMQ简单介绍 一.RabbitMQ介绍二.RabbitMQ的作用1.异步解耦2.流量削峰3.消息分发4.延迟通知 三.RabbitMQ安装&#xff08;Ubuntu&#xff09;1.先安装Erlang2.安装RabbitMQ3.安装RabbitMQ的管理界面4.创建虚拟机5.端口号信息 四.工作原理图 一.RabbitMQ介绍 RabbitMQ 是一款…...

【清华大学】DeepSeek政务应用场景与解决方案

目录 一、政务数字化转型三阶段演进二、人工智能政务应用场景四大方向 三、技术方案核心技术 四、解决方案案例1. 公文写作2. 合同协议智能审查3. 行政执法4. 就业指导 五、风险及对策六、落地大四步法七、未来发展展望AI职业替代逻辑空间智能与具身智能人机共生 一、政务数字化…...

spring boot自动装配原理

springboot自动装配几乎是现在面试必问的面试题&#xff0c;要是逐行分析自动装配流程肯定是很复杂的&#xff0c;因此我们从大体上来梳理即可。 一、 自动装配总览 首先要搞清楚两个问题&#xff0c;springboot自动装配是什么&#xff1f;解决了什么问题&#xff1f; springbo…...

【SDMs分析1】基于ENMTools R包的生态位分化分析和图像绘制(identity.test())

基于ENMTools包的生态位分化 1. 写在前面2. 生态位分化检验案例13. 生态位分化检验案例21. 写在前面 最近学了一个新的内容,主要是关于两个物种之间生态位分化检验的 R 语言代码。生态位分化是物种分布模型(SDM )研究中的关键部分,许多 SCI 论文都会涉及这一分析。该方法主…...

蓝桥杯比赛python程序设计——纯职业小组

问题描述 在蓝桥王国&#xff0c;国王统治着一支由 nn 个小队组成的强大军队。每个小队都由相同职业的士兵组成。具体地&#xff0c;第 ii 个小队包含了 bibi​ 名职业为 aiai​ 的士兵。 近日&#xff0c;国王计划在王宫广场举行一场盛大的士兵检阅仪式&#xff0c;以庆祝王…...

【Git教程】将dev分支合并到master后,那么dev分支该如何处理

将 dev 合并到 master 后的分支状态与操作指南 1. 合并后的分支状态 dev 分支不会消失&#xff1a; Git 的 git merge 命令仅将 dev 的内容合并到 master&#xff0c;不会删除 dev 分支。合并后&#xff0c;dev 分支仍然存在&#xff0c;其历史记录和代码保持不变。 分支的 H…...

python系统之综合案例:用python打造智能诗词生成助手

不为失败找理由&#xff0c;只为成功找方法。所有的不甘&#xff0c;因为还心存梦想&#xff0c;所以在你放弃之前&#xff0c;好好拼一把&#xff0c;只怕心老&#xff0c;不怕路长。 python系列之综合案例 前言一、项目描述二、项目需求三、 项目实现1、开发准备2、代码实现 …...