跳到主要内容
极客日志极客日志面向AI+效率的开发者社区
首页博客GitHub 精选镜像工具UI配色美学隐私政策关于联系
搜索内容 / 工具 / 仓库 / 镜像...⌘K搜索
注册
博客列表
C++算法

链表经典算法实战:相交、回文与随机指针复制

本文涵盖三个核心链表算法题。相交链表通过双指针对齐长度实现 O(M+N) 时间复杂度;回文链表利用快慢指针找中点并反转后半段进行比对;随机链表复制采用插针法,在原节点后插入副本并设置 random 指针,最终分离得到深拷贝。所有方案均优化空间复杂度至 O(1)。

云间运维发布于 2026/3/15更新于 2026/6/2326 浏览
链表经典算法实战:相交、回文与随机指针复制

链表经典算法实战

一、相交链表问题

问题描述

给定两个单链表,判断它们是否相交。若相交,返回相交的节点。注意,判断依据是节点地址是否相同,而非节点值(因为节点值可能存在重复)。

解题思路分析

暴力遍历法
  • 方法:遍历链表 A,对于 A 中的每一个节点,再遍历整个链表 B,检查是否存在地址相同的节点。
  • 复杂度:时间 O(M*N),空间 O(1)。
  • 评价:效率低,长链表场景下不推荐。
双指针对齐法(最优解)
  • 方法:
    1. 分别遍历两个链表,计算各自长度。
    2. 求出两链表长度差 gap。
    3. 让较长链表的指针先移动 gap 步,使剩余长度一致。
    4. 两个指针同时移动,逐个比较节点地址,第一个相同的即为交点。
  • 复杂度:时间 O(M + N),空间 O(1)。
  • 优势:逻辑清晰,效率高,适用于任意长度的链表。

实际开发中,我们优先选择双指针对齐法。

struct ListNode* getIntersectionNode(struct ListNode* headA, struct ListNode* headB) {
    struct ListNode* curA = headA, *curB = headB;
    int lenA = 1, lenB = 1;

    // 计算链表 A 长度
    while (curA->next) {
        curA = curA->next;
        lenA++;
    }
    // 计算链表 B 长度
    while (curB->next) {
        curB = curB->next;
        lenB++;
    }

    int gap = abs(lenA - lenB);
    struct ListNode* longList = headA;
    struct ListNode* shortList = headB;

    // 确保 longList 指向较长的链表
    if (lenA < lenB) {
        longList = headB;
        shortList = headA;
    }

    // 长链表指针先走 gap 步
    while (gap--) {
        longList = longList->next;
    }

    
     (longList) {
         (longList == shortList)
             longList;
        longList = longList->next;
        shortList = shortList->next;
    }
     ;
}
// 同时移动,寻找交点
while
if
return
return
NULL

二、链表的回文结构

问题描述

判断一个单链表是否为回文结构,即正着读和反着读都一样。

解题思路

回文链表判断的核心在于对称性验证。我们可以分三步走:

  1. 找到中间节点:使用快慢指针法。快指针每次走两步,慢指针每次走一步,当快指针到达末尾时,慢指针正好在中间。
  2. 反转后半部分链表:从中间节点开始,将后半部分链表原地反转。
  3. 比较前后两部分:从头节点和反转后的中间节点开始,逐个比较节点值是否相同。

完整代码

class PalindromeList {
public:
    // 寻找中间节点
    struct ListNode* middleNode(struct ListNode* head) {
        struct ListNode* slow = head;
        struct ListNode* fast = head;
        while (fast != NULL && fast->next != NULL) {
            slow = slow->next;      // 慢指针每次移动一步
            fast = fast->next->next; // 快指针每次移动两步
        }
        return slow; // 慢指针指向中间节点
    }

    // 反转链表
    struct ListNode* reverseList(struct ListNode* head) {
        struct ListNode* n1 = NULL, *n2 = head, *n3 = NULL;
        if (n2) n3 = n2->next;
        while (n2) {
            n2->next = n1;
            n1 = n2;
            n2 = n3;
            if (n3) n3 = n3->next;
        }
        return n1;
    }

    bool chkPalindrome(ListNode* A) {
        struct ListNode* mid = middleNode(A);
        struct ListNode* rmid = reverseList(mid);
        while (rmid && A) {
            if (rmid->val != A->val)
                return false;
            rmid = rmid->next;
            A = A->next;
        }
        return true;
    }
};

三、随机链表的复制

问题描述

实现一个函数,复制一个含有随机指针的链表。随机指针可以指向链表中的任何节点或为空。

解题思路

常规链表复制很简单,但随机指针的存在使得问题复杂化,因为它可能指向尚未复制的节点。我们可以通过'插针法'三步解决:

  1. 插入拷贝节点:在原链表的每个节点后插入一个拷贝节点,值与原节点相同。
  2. 设置随机指针:拷贝节点的随机指针应指向原节点随机指针所指节点的下一个节点(即其对应的拷贝)。
  3. 分离链表:将混合链表分离为原链表和拷贝链表。
struct Node* copyRandomList(struct Node* head) {
    // 1. 拷贝节点插到原节点后边
    struct Node* cur = head;
    while (cur) {
        struct Node* copy = (struct Node*)malloc(sizeof(struct Node));
        copy->next = cur->next;
        cur->next = copy;
        copy->val = cur->val;
        cur = copy->next; // cur 走到原链表的下一个
    }

    // 2. 控制 random 指针
    cur = head;
    while (cur) {
        struct Node* copy = cur->next;
        if (cur->random == NULL) {
            copy->random = NULL;
        } else {
            copy->random = cur->random->next; // 核心代码
        }
        cur = copy->next;
    }

    // 3. 把拷贝链表尾插起来
    struct Node* copyhead = NULL, *copytail = NULL;
    cur = head;
    while (cur) {
        struct Node* copy = cur->next;
        if (copytail == NULL) {
            copyhead = copytail = copy;
        } else {
            copytail->next = copy;
            copytail = copytail->next;
        }
        cur = copy->next;
    }
    return copyhead;
}

复杂度分析

  • 时间复杂度:O(N),需要三次遍历链表。
  • 空间复杂度:O(1),除了返回的拷贝链表外,仅使用了常数个额外指针。

目录

  1. 链表经典算法实战
  2. 一、相交链表问题
  3. 问题描述
  4. 解题思路分析
  5. 暴力遍历法
  6. 双指针对齐法(最优解)
  7. 二、链表的回文结构
  8. 问题描述
  9. 解题思路
  10. 完整代码
  11. 三、随机链表的复制
  12. 问题描述
  13. 解题思路
  14. 复杂度分析
  • 免费图片AI生成工具免费生成了解详情
  • Magick API 一键接入全球大模型注册送1000万token查看
  • 免费图片视频在线生成30秒,将你的创意变成现实开始设计
  • X/Twitter免费视频下载器免登陆无限额度免费视频解析下载了解详情
  • 100+免费在线小游戏爽一把
极客日志微信公众号二维码

微信扫一扫,关注极客日志

微信公众号「极客日志V2」,在微信中扫描左侧二维码关注。展示文案:极客日志V2 zeeklog

更多推荐文章

查看全部
  • 基于 Java 的校园二手物品在线交易平台设计与实现
  • OpenClaw 接入飞书实战:让 AI 机器人读写文档与表格
  • Git 远程操作与标签管理实战指南
  • Llama 3.1 本地部署指南:Ollama、OpenWeb UI 与 Spring AI
  • 智能车电磁组进阶:ADC 信号处理与差比和差算法
  • macOS 部署安装 IndexTTS2
  • 解决 NVIDIA RTX 50 系列 (sm_120) 架构下的 PyTorch 与 Unsloth 依赖冲突
  • 异构数据迁移工具:DataX 与 DataX-Web 使用指南
  • Nuxt 4 生产环境部署指南 (Node.js + Nginx)
  • AIGC 背后的深度学习魔法:从原理到实践
  • MISRA C++静态分析报告解读与实战指南
  • 机器视觉缺陷检测:基于C++、Halcon、Qt 5.8与VS2015的实战
  • Java 长字符串处理的 5 种实用技巧
  • MySQL 约束详解:非空、主键与外键的核心作用
  • Linux 运维命令速查:进程查看与日志分析
  • 基于 Azure 与 OpenAI 构建智能语音客服系统
  • OpenCode 开源 AI 编程 Agent 完全指南:从安装到实战的 8 个步骤(2026最新)
  • 基于 CTCM 算法的复杂城市地形下无人机避障三维航迹规划
  • C++ 类完全指南:从基础到实践
  • Java 多态详解:概念、实现机制与实践应用

相关免费在线工具

  • 加密/解密文本

    使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online

  • Gemini 图片去水印

    基于开源反向 Alpha 混合算法去除 Gemini/Nano Banana 图片水印,支持批量处理与下载。 在线工具,Gemini 图片去水印在线工具,online

  • Base64 字符串编码/解码

    将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online

  • Base64 文件转换器

    将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online

  • Markdown转HTML

    将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online

  • HTML转Markdown

    将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML转Markdown在线工具,online