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

环形链表、数组交集与随机链表复制解析

涵盖三个经典链表与数组算法题。环形链表使用哈希集合检测环入口。数组交集通过排序去重后遍历对比实现,避免重复元素干扰。随机链表复制提供两种方案:C 语言通过插入法将副本节点穿插于原节点间以复用 random 指针关系,C++ 则利用哈希表映射原节点与新节点的对应关系。代码均经过优化,注重空间复杂度与逻辑清晰度,适合数据结构进阶学习。

全栈工匠发布于 2026/3/15更新于 2026/9/459 浏览
环形链表、数组交集与随机链表复制解析

一、环形链表

1.1 题目描述

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

为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。如果 pos 是 -1,则在该链表中没有环。注意:pos 不作为参数进行传递,仅仅是为了标识链表的实际情况。注意不允许修改链表。

1.2 解题思路

检测环入口通常有两种主流方法:快慢指针法或哈希集合遍历。

这里我们采用 C++ STL 的 set 容器来遍历链表。核心逻辑很简单:如果链表中的节点不在 set 中就插入;如果在就代表带环,且该节点就是环入口点。需要注意的是,这里存储的是节点的指针而非值,因为节点的值可能有重复,而指针地址是唯一的。

1.3 代码实现
class Solution {
public:
    ListNode *detectCycle(ListNode *head) {
        std::set<ListNode*> s;
        ListNode* cur = head;
        while(cur) {
            auto it = s.find(cur);
            if(it == s.end()) {
                s.insert(cur);
            } else {
                return *it;
            }
            cur = cur->next;
        }
        return nullptr;
    }
};

二、两个数组中的交集

2.1 题目描述

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

2.2 解题思路

暴力遍历虽然可行,但容易受数组大小和重复元素影响。例如示例中若拿 nums2 的值去 nums1 遍历,可能会得到重复结果。

更稳健的思路是先对两个数组去重,再取交集。利用 STL 中的 set 天然有序且去重的特性,可以将一个数组的值依次在另一个数组中查找,找到后存入结果容器即可。

2.3 代码实现
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.4 对比算法扩展

在实际工程中,除了找交集,往往还需要处理差集或并集。对于有序集合(如 std::set),可以使用双指针对比算法高效完成。

对比算法逻辑: 由于 set 遍历是有序的,可以依次比较两个迭代器指向的值。较小的那个向后移动,相等的即为交集,同时移动两个指针。

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> 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;
    }
};

三、随机链表的复制

3.1 题目描述

给你一个长度为 n 的链表,每个节点包含一个额外增加的随机指针 random,该指针可以指向链表中的任何节点或空节点。构造这个链表的深拷贝。深拷贝应该正好由 n 个全新节点组成,其中新节点的 next 指针和 random 指针也都应指向复制链表中的新节点。

3.2 解题思路

这个问题难点在于 random 指针的映射关系。原链表和拷贝出的链表之间没有直接关联,如何建立对应关系是关键。

方案一:C 语言插针法(空间复杂度 O(1)) 核心思想是在原链表的基础上拷贝节点,将新节点插入到原节点之后。这样原节点 pcur 和新节点 copy 就有了物理上的相邻关系。设置 random 时,利用 copy->random = pcur->random->next 这一关系式即可。最后断开新旧链表。

方案二:C++ 哈希表法(空间复杂度 O(N)) C++ 中可以利用 map 存储原节点与拷贝节点的映射关系。遍历时先创建所有新节点并存入 map,再次遍历设置 random 指针时,通过 map 查找对应的拷贝节点。

3.3 代码实现

C 语言版本:

/**
 * Definition for a Node.
 * struct Node {
 *     int val;
 *     struct Node *next;
 *     struct Node *random;
 * };
 */
typedef struct 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
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;
    
    AddNode(head);
    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++ 版本:

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;
            }
            nodeMap[cur] = copytail;
            cur = cur->next;
        }
        
        // 第二次遍历:设置 random 指针
        cur = head;
        Node* copy = copyhead;
        while(cur) {
            if(cur->random == nullptr) {
                copy->random = nullptr;
            } else {
                copy->random = nodeMap[cur->random];
            }
            cur = cur->next;
            copy = copy->next;
        }
        return copyhead;
    }
};

目录

  1. 一、环形链表
  2. 1.1 题目描述
  3. 1.2 解题思路
  4. 1.3 代码实现
  5. 二、两个数组中的交集
  6. 2.1 题目描述
  7. 2.2 解题思路
  8. 2.3 代码实现
  9. 2.4 对比算法扩展
  10. 三、随机链表的复制
  11. 3.1 题目描述
  12. 3.2 解题思路
  13. 3.3 代码实现

更多推荐文章

查看全部
  • Pico 4XVR 1.10.13 安装与使用教程
  • Selenium webdriver_manager 浏览器驱动管理指南
  • 《Agent Runtime 工程化》第七章 权限与安全边界
  • AI 大模型:国内外发展现状与趋势分析
  • 企业微信视频号去水印解析机器人搭建指南
  • 2025 AI 年度复盘:从 DeepSeek R1 开源到 Manus 商业落地
  • Linux 基本操作与 Java 项目部署指南
  • Unitree 机器人 Python SDK 使用指南
  • SpringBoot 低代码 JSON 表单引擎与审批流实现
  • 前后端分离架构深度解析:模式对比与选型指南
  • Qwen3-4B-Instruct 模型本地 CPU 部署与 WebUI 配置
  • AI 绘画实战:从关键词到高质量图像生成的技术实现与优化
  • 发那科机器人与西门子 PLC 通讯方案:网关与 Modbus TCP 实战
  • 计算机视觉高级应用与前沿技术解析
  • 飞算 JavaAI 实战指南:用自然语言加速 Java 开发
  • 推荐系统 10 大必读经典论文:构建完整知识体系
  • 18 款主流 AI Agent 框架技术选型与对比分析
  • Linux System V 共享内存:原理、实操与避坑指南
  • cann-recipes-train 实战:昇腾平台 DeepSeek-R1 与 Qwen2.5 RL 训练
  • Qwen3Guard-Gen-WEB 审核规则定制与策略引擎部署实战

相关免费在线工具

  • 加密/解密文本

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