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

环形链表、两个数组中的交集与随机链表复制算法解析

环形链表检测利用快慢指针或哈希集合定位环入口;两数组交集通过去重后遍历对比实现;随机链表深拷贝采用节点映射法或穿插法。提供 C++ STL 及 C 语言代码示例,涵盖算法思路与具体实现细节。

CodeArtist发布于 2026/3/29更新于 2026/5/3015 浏览
环形链表、两个数组中的交集与随机链表复制算法解析

一 环形链表

1.1 题目链接:环形链表 II

1.2 题目描述:

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

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

1.3 题目示例:

文章配图

1.4 题目思路:

在之前的讲解中介绍过 C 语言的快慢指针方法,C 语言相对复杂,这里我们使用 C++ STL 实现。利用 STL 的 set 遍历链表,如果链表中的节点不在 set 中就插入(注意这里不是插入节点的值,而是节点的指针,因为节点的值可能有几个相等);如果在就代表带环,并且该节点就是环入口点。

1.5 解决代码

// 1 cur 不在 set 对象中,就插入到 set 对象中
// 2 cur 在 set 对象中就带环,并且该节点就是环入口点
class Solution {
public:
    ListNode *detectCycle(ListNode *head) {
        std::set<ListNode*> s;
        ListNode* cur = head;
        while(cur) {
            auto it = s.find(cur); // 查找 cur 是否在 set 对象中
            if(it == s.end()) { // cur 不在 set 对象中,就插入到 set 对象中
                s.insert(cur);
            } else { // cur 在 set 对象中就带环,并且该节点就是环入口点
                return *it;
            }
            cur = cur->next;
        }
        return nullptr;
    }
};

二 两个数组中的交集

2.1 题目链接:两个数组的交集

2.2 题目描述:

给定两个数组 nums1 和 nums2,返回它们的交集。输出结果中的每个元素一定是唯一的。我们可以不考虑输出结果的顺序。

2.3 题目示例:

**示例 1:**输入:nums1 = [1,2,2,1], nums2 = [2,2] 输出:[2]

**示例 2:**输入:nums1 = [4,9,5], nums2 = [9,4,9,8,4] 输出:[9,4] 解释:[4,9] 也是可通过的

2.4 题目思路:

思路 1:一个数组的每个值到另一个数组中遍历,这种虽然可行,但是和拿哪个数组中的值到哪个数组中遍历有关。例如示例 2 如果拿 nums1 中的每个值到 nums2 中遍历可行,但是如果拿 nums2 中的值到 nums1 中就会得到结果 [9,4,9,4] 是不正确的,所以这种方法不行。

思路 2(正确思路):先将两个数组去重(要先排序只有排序了才能真正的去重),去重可用 STL 中的 set,也可以用算法库中的去重算法 std::unique。再拿一个数组中的值分别到另一个数组中找(使用 STL->set->count),最后将遍历找到的插入到一个容器中返回即可。

2.5 解决代码:

class Solution {
public:
    vector<int> intersection(vector<int>& nums1, vector<int>& nums2) {
        // 去重
        std::set<int> s1(nums1.begin(), nums1.end());
        std::set<int> s2(nums2.begin(), nums2.end());
        vector<int> v;
        // 将一个数组中的值依次到另一个数组中查找
        for(auto e : s1) {
            if(s2.count(e)) {
                v.push_back(e); // 在另一个数组中插入到一个容器中
            }
        }
        return v;
    }
};

2.6 作用:

从上面来看这个是没有什么作用的,但是现实生活中不只是用来查找交集的,一般是要找交/差/并集,这时候就有著名的对比算法。

两个数组中找交/差/并集的整体思路(对比算法):

文章配图

【示例】使用对比算法找交集:

class Solution {
public:
    vector<int> intersection(vector<int>& nums1, vector<int>& nums2) {
        std::set<int> s1(nums1.begin(), nums1.end());
        std::set<int> s2(nums2.begin(), nums2.end());
        // 因为 set 遍历是有序的,有序值,依次比较
        // 小的++,相等的就是交集
        vector<int> ret;
        auto it1 = s1.begin();
        auto it2 = s2.begin();
        while(it1 != s1.end() && it2 != s2.end()) {
            if(*it1 < *it2) {
                it1++;
            } else if(*it1 > *it2) {
                it2++;
            } else {
                ret.push_back(*it1);
                it1++;
                it2++;
            }
        }
        return ret;
    }
};

【对比算法在生活中的应用】

例如我们要更换手机,而之前的手机中有许多的照片是要的,此时怎么办呢?这时候可以将云端照片和本地照片进行对比,判断是否要导入到云端,而照片存在,但是照片可能存在更改,这时候可以比较照片最后的时间。所以这个对比算法在生活中还是有很多用处的。

三 随机链表的复制 (两种解决办法 C 语言 [了解思路]、C++)

3.1 题目链接:随机链表的复制

3.2 题目描述:

给你一个长度为 n 的链表,每个节点包含一个额外增加的随机指针 random,该指针可以指向链表中的任何节点或空节点。

构造这个链表的深拷贝。深拷贝应该正好由 n 个全新节点组成,其中每个新节点的值都设为其对应的原节点的值。新节点的 next 指针和 random 指针也都应指向复制链表中的新节点,并使原链表和复制链表中的这些指针能够表示相同的链表状态。复制链表中的指针都不应指向原链表中的节点。

例如,如果原链表中有 X 和 Y 两个节点,其中 X.random --> Y。那么在复制链表中对应的两个节点 x 和 y,同样有 x.random --> y。

返回复制链表的头节点。

用一个由 n 个节点组成的链表来表示输入/输出中的链表。每个节点用一个 [val, random_index] 表示:

  • val:一个表示 Node.val 的整数。
  • random_index:随机指针指向的节点索引(范围从 0 到 n-1);如果不指向任何节点,则为 null。

你的代码只接受原链表的头节点 head 作为传入参数。

3.3 题目示例:

文章配图

3.4 题目思路:

文章配图

3.5 解决代码:

【C 语言解决代码】注意利用这个了解思路

/** 
 * Definition for a Node.
 * struct Node {
 *     int val;
 *     struct Node *next;
 *     struct Node *random;
 * };
 */
typedef struct Node Node;

// 构造一个简单 Node*
Node* buyNode(int x) {
    Node* newnode = (Node*)malloc(sizeof(Node));
    newnode->val = x;
    newnode->next = newnode->random = NULL;
    return newnode;
}

// 在原链表的基础上拷贝节点 (即原链表的每个节点后面拷贝一个一样的节点)
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;
    }
}

// 设置新节点的 random 利用关系式
// copy(新节点)->random = pcur(原链表节点)->random->next;
void setRandom(Node* head) {
    Node* pcur = head;
    while(pcur) {
        Node* copy = pcur->next;
        if(pcur->random) {
            copy->random = pcur->random->next;
        }
        pcur = copy->next;
    }
}

struct Node* copyRandomList(struct Node* head) {
    if(head == NULL) // 如果头节点为空要特殊处理
    {
        return head;
    }
    // 1 在原链表基础上拷贝节点并插入到原链表的每个节点之后
    AddNode(head);
    // 2 设置新节点的 random
    setRandom(head);
    // 断开新旧链表
    // 注意这里要返回新链表的根节点,所以这里要定义两个变量
    // 一个用于储存新链表的根节点位置,一个用于从新链表的根节点位置开始遍历。
    Node* pcur = head;
    Node* copyHead, *copyTail;
    copyHead = copyTail = pcur->next;
    while(copyTail->next) {
        pcur = copyTail->next;
        copyTail->next = pcur->next;
        copyTail = copyTail->next;
    }
    return copyHead;
}

【C++ 使用 STL 解决代码】

上面讲了 C 语言的处理方法,由于出现一个很难处理的问题节点中的 random(随机) 节点怎么拷贝,如果通过节点中的值进行判断指向关系(如果一个节点的 random 指向的节点值是 1,而节点值为 1 的节点不止一个有怎么解决呢?显然这种方法是不行的),如果想走这种匹配关系,那只能暴力遍历,此时时间复杂度为 O(N^2)。这是因为原链表和拷贝出的链表直接的节点没关系才导致的,C 语言的处理方法是先拷贝在原链表中在就导致原链表和拷贝出的链表之间的节点有了关系。那 C++ 怎么处理了,C++ 所以 STL 中的 map 由于储存 map(原链表节点,拷贝链表的节点),这不就巧妙的将原链表和拷贝出的链表之间的节点联系起来了吗,可以通过原链表节点得到拷贝对应的节点。

【解决代码】

class Solution {
public:
    Node* copyRandomList(Node* head) {
        std::map<Node*, Node*> nodeMap; // 由于存在原链表节点和拷贝的节点,从而导致两者之间有关系
        Node* copyhead = nullptr, *copytail = nullptr; // 由于储存拷贝链表的根节点和遍历拷贝链表
        Node* cur = head;
        while(cur) {
            if(copytail == nullptr) // 如果为空特殊处理
            {
                copyhead = copytail = new Node(cur->val);
            } else { // 如果不为空进行尾插
                copytail->next = new Node(cur->val);
                copytail = copytail->next;
            }
            // 原节点和拷贝节点 map kv 存储
            nodeMap[cur] = copytail;
            cur = cur->next;
        }
        // 处理 random
        cur = head;
        Node* copy = copyhead;
        while(cur) {
            if(cur->random == nullptr) // 如果原节点的 random 为空则拷贝节点的 random 也为空
            {
                copy->random = nullptr;
            } else { // 如果原节点的 random 不为空则,则使用 map[],传入参数原链表节点指针,得到拷贝出的对应节点
                copy->random = nodeMap[cur->random];
            }
            cur = cur->next;
            copy = copy->next;
        }
        return copyhead;
    }
};

目录

  1. 一 环形链表
  2. 二 两个数组中的交集
  3. 三 随机链表的复制 (两种解决办法 C 语言 [了解思路]、C++)
  • 💰 8折买阿里云服务器限时8折了解详情
  • Magick API 一键接入全球大模型注册送1000万token查看
  • 🤖 一键搭建Deepseek满血版了解详情
  • 一键打造专属AI 智能体了解详情
极客日志微信公众号二维码

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

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

更多推荐文章

查看全部
  • 前端 AI 与营销增长领域的 AI 应用核心趋势
  • 深入解析:为什么 list.sort() 比 Stream().sorted() 更快
  • 如何在 Windows 上本地部署开源大语言模型
  • AI 应用开发工程师(Agent 方向):核心技能与实战路径
  • AI 辅助修复前端动态导入失败问题的实践与反思
  • Rio:用纯 Python 开发现代 Web 和桌面应用的 UI 框架
  • Linux 线程控制
  • AI 绘画在商业设计中的应用与版权探讨
  • DeepSeek 各版本演进路线与核心差异深度解析
  • 基于FPGA的FIR数字滤波器设计(Quartus与Vivado实现)
  • OpenClaw 实战:AI 摄像头访问与 WSL2 解决方案
  • 基于 FastGPT 与 MCP 协议构建工具增强型智能体
  • VSCode 集成 Git Bash 终端配置与优化指南
  • Claude-Code 2.1.88 源码结构解析:基于 Source Map 的逆向分析
  • Edict:基于三省六部制的 AI Agent 协作架构解析
  • Flutter for OpenHarmony 实战:Material Color Utilities 算法驱动动态换肤
  • Paperclip:AI 代理公司编排平台
  • n8n AI 自动化流程编排平台部署与使用实战
  • 基于 AI 智能体的费曼学习法知识助手实战
  • Stable Diffusion 画质增强:Consistency Decoder 使用教程

相关免费在线工具

  • 加密/解密文本

    使用加密算法(如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