【数据结构系列04】随机链表的复制、环形链表I、环形链表||

随机链表的复制

138. 随机链表的复制 - 力扣(LeetCode)

题目如下图所示:

思路:将复制的节点连接到其对应的拷贝节点之后

  1. 拷贝链表的每一个节点,拷贝的节点先链接到被拷贝节点的后面
  2. 复制随机指针的链接:拷贝节点的随机指针指向被拷贝节点随机指针的下一个位置
  3. 拆解链表,把拷贝的链表从原链表中拆解出来

在解答这道题目时,我们可以通过多次运行测试,发现代码中需要改进的环节,并逐步完善。

cpp 复制代码
struct Node* copyRandomList(struct Node* head) {
    if(head == NULL)
    {
        return NULL;
    }
    struct Node* cur = head;
    // 只拷贝了next和val的值
    while (cur) {
        struct Node* copy = (struct Node*)malloc(sizeof(struct Node));
        copy->val = cur->val;
        copy->next = cur->next;
        cur->next = copy;
        cur = cur->next->next;
    }

    // 拷贝了原链表中random的指向
    struct Node* pcur = head;
    struct Node* pcopy = head->next;
    while (pcur) {
        if(pcur->random == NULL)
        {
            pcopy->random = NULL;
        }
        else
        {
            pcopy->random = pcur->random->next;
        }
        if (pcur->next->next == NULL) {
            break;
        }
        pcur = pcur->next->next;
        pcopy = pcopy->next->next;
    }

    // 建立新链表
    struct Node* phead = head->next;
    struct Node* ptail = head->next;
    struct Node* p = head->next;//作为ptail的前一个节点,用于链表之间的连接
    while (ptail->next)
    {
        ptail = ptail->next->next;
        p->next = ptail;
        p = p->next;
    }
    return phead;
}

环形链表I

141. 环形链表 - 力扣(LeetCode)

题目如下图所示:

思路:快慢指针

定义快慢指针fast,slow, 如果链表确实有环,fast指针一定会在环内追上slow指针。

cpp 复制代码
bool hasCycle(struct ListNode *head) {
    
    struct ListNode *slow = head;
    struct ListNode *fast = head;
    while(fast && fast->next)
    {
        slow = slow->next;
        fast = fast->next->next;
        if(slow == fast)
        {
            return true;
        }
    }
    return false;
}

这个代码非常简单,但引申出一系列问题

问题1:为什么一定会相遇,有没有可能会错过,永远追不上

假设slow进环时,slow和fast之间的距离是N,fast追击slow的过程如下:

  • N
  • N-1
  • N-2
  • ...
  • 2
  • 1
  • 0 -->追上了

每追击一次,距离缩小1,距离为0时便是追到了,即相遇了

问题2:slow一次走一步,fast走2步,3步,4步...可以吗

假设slow进环时,slow和fast之间的距离是N,fast走3步时,fast追击slow的过程如下:

假设1:

  • N
  • N-2
  • N-4
  • ...
  • 4
  • 2
  • 0 -->追上了

假设2:

  • N
  • N-2
  • N-4
  • ...
  • 3
  • 1
  • -1 -->错过了

如果错过当前机会,系统将进入下一轮循环。这将导致前述两种假设情况继续存在,难以区分。接下来,我们通过数学表达式来阐明这一概念

  • L:链表初始位置到环的开始位置的距离
  • C:环的长度
  • N:fast到slow的距离

fast走的距离:L + x * C + C - N;

slow走的距离:L

3L = L + x * C + C - N;

解得:2L = ( x - 1 ) C - N;

  • 2L:一定是偶数

若C是偶数,N是奇数时不成立,N是偶数时成立

最终答案为否定,建议采用fast优先于slow的方案。

环形链表II

142. 环形链表 II - 力扣(LeetCode)

题目如下图所示:

思路:快慢指针

我们可以运用数学原理解决这个问题:让一个指针从相遇点出发,另一个从链表头开始,两者相遇的位置即为循环的起点。

第一次相遇时:

slow走的路程:L + N;

fast走的路程:L + C * X + N;

2L + 2N = L + C * X + N;

解得:L = C * X - N;也就是L = X - N,即相遇点到循环起点的距离 == 链表头到循环起点的距离

cpp 复制代码
struct ListNode *detectCycle(struct ListNode *head) {
    struct ListNode *slow = head;
    struct ListNode *fast = head;
    while(fast && fast->next)
    {
        slow = slow->next;
        fast = fast->next->next;
        if(slow == fast)
        {
            struct ListNode *meet = slow;
            while(head != meet)
            {
                meet = meet->next;
                head = head->next;
            }
            return meet;
        }
    }
    return NULL;
}

总结 📝

本文通过 随机链表的复制环形链表 I环形链表 II 三道经典题目,深入分析了链表操作中的不同解法效率:

随机链表的复制解法对比 🎯

思路 方法 时间复杂度 空间复杂度 优缺点
思路1 原地复制 + 拆分法 O(n)O(n) O(1)O(1) 三步操作,空间最优,推荐使用 ✅

环形链表 I 解法对比 🔍

思路 方法 时间复杂度 空间复杂度 优缺点
思路1 快慢指针 O(n)O(n) O(1)O(1) 最优解,简洁高效 ⚡

环形链表 II 解法对比 🎪

思路 方法 时间复杂度 空间复杂度 优缺点
思路1 快慢指针 + 数学推导 O(n)O(n) O(1)O(1) 最优解,找到环的入口节点 🎯

核心收获 💡

  • 原地复制法解决随机链表:通过三步操作(复制节点、处理 random、拆分链表),实现深度拷贝 🔄

  • 快慢指针判环:利用速度差判断链表是否有环,空间 O(1)O(1) 🏃‍♂️🏃‍♀️

  • 快慢指针找环入口:相遇后重置指针,同步走找到环的入口节点 🚪

  • 边界处理:链表为空的处理、随机指针为空的处理、无环情况的处理 ⚠️

  • 面试技巧:掌握经典题目的最优解,展示对链表操作的熟练度 💪


下期预告 🔜

下一期我们将继续**【数据结构系列05】** ,进入 栈和队列经典问题,一起来看看三道经典题目:

  1. [括号匹配问题]:如何判断字符串中的括号是否有效匹配? 🔄

  2. [用队列实现栈]:如何仅用队列结构模拟栈的先进后出特性? 📚

  3. [用栈实现队列]:如何用栈结构模拟队列的先进先出行为? 🚶‍♂️🚶‍♀️

如果你对链表操作还有疑问,欢迎在评论区留言讨论!如果觉得有帮助,别忘了点赞、收藏、关注,我们下期见! 🎉👋

相关推荐
senijusene1 小时前
Linux软件编程: 线程属性与线程间通信详解
java·linux·jvm·算法
weiabc1 小时前
cout << fixed << setprecision(2) << v; fixed 为什么不用括号,它是函数吗
开发语言·c++·算法
m0_531237171 小时前
C语言-内存函数
c语言·开发语言·算法
wengqidaifeng2 小时前
数据结构(四)二叉树初步:计算机科学中的分叉树
c语言·数据结构
Once_day2 小时前
数据结构(2)常见概念
数据结构
独自破碎E2 小时前
【DFS】BISHI77数水坑
算法·深度优先
小陈phd10 小时前
多模态大模型学习笔记(七)——多模态数据的表征与对齐
人工智能·算法·机器学习
雨泪丶10 小时前
代码随想录算法训练营-Day35
算法
pursuit_csdn10 小时前
LeetCode 1022. Sum of Root To Leaf Binary Numbers
算法·leetcode·深度优先