LeetCode 复杂链表的复制

剑指 Offer 35. 复杂链表的复制

题目描述

请实现 copyRandomList 函数,复制一个复杂链表。在复杂链表中,每个节点除了有一个 next 指针指向下一个节点,还有一个 random 指针指向链表中的任意节点或者 null。

示例 1:

输入:head = [[7,null],[13,0],[11,4],[10,2],[1,0]]

输出:[[7,null],[13,0],[11,4],[10,2],[1,0]]

示例 2:

输入:head = [[1,1],[2,1]]

输出:[[1,1],[2,1]]

示例 3:

输入:head = [[3,null],[3,0],[3,null]] 输出:[[3,null],[3,0],[3,null]]

示例 4:

输入:head = []

输出:[]

解释:给定的链表为空(空指针),因此返回 null。

提示

  • -10000 <= Node.val <= 10000

  • Node.random 为空(null)或指向链表中的节点。

  • 节点数目不超过 1000 。

作者:Krahets 链接:力扣 来源:力扣(LeetCode) 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

解题思路

利用哈希表解法:

第一步:

建立哈希表

设置对应的key值和value值,key值为原来的链表,用原来链表中的每一个结构体作为key值,,并且用cur变量定位当前key值位置,

而用map[cur]来表示对应key值的value值。

 

第二步:

复制key中的val、next、random

复制的时候新建对应key值的结构体节点,并且将原链表中结构体的val值复制给map[cur](此处表示key值为cur的value对象)。

while (cur) {
        // 复制当前节点并将其存入哈希表中
        map[cur] = new Node(cur->val);
        cur = cur->next; // 指针后移,处理下一个节点
}

创建好如图所示的几个结构体后,便开始复制原链表中的next值和random值,此时复制完next值即可将复制后的链表节点连接起来

 

cur = head; // 重新指向头节点
while (cur) {
    map[cur]->next = map[cur->next]; // 将新节点的 next 指针指向下一个新节点
    map[cur]->random = map[cur->random]; // 将新节点的随机指针指向原节点的随机指针所对应的新节点
    cur = cur->next; // 移动到下一个原节点
}

注意其中的map[cur->next]与map[cur]->next的区别,前者[cur->next]是指一个key值,可以看到图中来理解为一个嵌套的哈希表,key值为cur的节点中,还有个key值为next的对应关系,所以map[cur->next]表示原链表节点中的next值。后者map[cur]->next,应该理解为map[cur]的next值,map[cur]在之前解释过表示为value的意思,即为key值节点所对应的复制后的节点的next值。

random的复制原理与next相同。

核心代码

Node* copyRandomList(Node* head) {
    if (head == nullptr) return head; // 如果头节点为空,则直接返回空指针
    Node* cur = head; // 创建指向当前节点的指针
    unordered_map<Node*, Node*> map; // 创建哈希表,用于存储原节点与新节点的对应关系
    while (cur) {
        // 复制当前节点并将其存入哈希表中
        map[cur] = new Node(cur->val);
        cur = cur->next; // 指针后移,处理下一个节点
    }
    // 更新新链表中节点的 next 和 random 指针
    cur = head; // 重新指向头节点
    while (cur) {
        map[cur]->next = map[cur->next]; // 将新节点的 next 指针指向下一个新节点
        map[cur]->random = map[cur->random]; // 将新节点的随机指针指向原节点的随机指针所对应的新节点
        cur = cur->next; // 移动到下一个原节点
    }
    return map[head]; // 返回头节点的对应新节点
}

完整代码

#include <iostream>
#include <unordered_map>
#include <string>
using namespace std;
​
// 定义链表节点结构体
struct Node {
    int val; // 当前节点的值
    Node* next; // 指向下一个节点的指针
    Node* random; // 随机指针
    Node(int x) : val(x), next(nullptr), random(nullptr) {} // 构造函数
};
​
// 复制带随机指针的链表
Node* copyRandomList(Node* head) {
    if (head == nullptr) return head; // 如果头节点为空,则直接返回空指针
    Node* cur = head; // 创建指向当前节点的指针
    unordered_map<Node*, Node*> map; // 创建哈希表,用于存储原节点与新节点的对应关系
    while (cur) {
        // 复制当前节点并将其存入哈希表中
        map[cur] = new Node(cur->val);
        cur = cur->next; // 指针后移,处理下一个节点
    }
    // 更新新链表中节点的 next 和 random 指针
    cur = head; // 重新指向头节点
    while (cur) {
        map[cur]->next = map[cur->next]; // 将新节点的 next 指针指向下一个新节点
        map[cur]->random = map[cur->random]; // 将新节点的随机指针指向原节点的随机指针所对应的新节点
        cur = cur->next; // 移动到下一个原节点
    }
    return map[head]; // 返回头节点的对应新节点
}
​
// 打印链表信息
void printList(Node* head) {
    Node* cur1 = head; // 创建指向头节点的指针
    while (cur1) { // 遍历链表,直到结束
        cout << cur1->val << " "; // 输出当前节点的值
        Node* cur2 = head; // 创建新指针,用于查找随机指针所对应的节点
        int i = 0; // 计数器,用于记录随机指针所指向节点的位置
        while (cur2) {
            if (cur1->random == cur2) { // 如果当前随机指针与当前节点相同,则找到了随机指针所指向的节点
                cout << i; // 输出该节点在链表中的位置,从 0 开始计数
                break;
            }
            cur2 = cur2->next; // 向后移动指针
            i++; // 更新计数器值
        }
        if (cur1->random == nullptr) cout << "null"; // 如果当前节点随机指针为空,则输出 null
        cout << endl; 
        cur1 = cur1->next; // 移动到下一个节点
    }
}
​
int main() {
    // 创建原链表
    int num,ran,cnt=1;
    char c;
    cout<<"输入对应val值:"; 
    cin>>num;
    Node* head=new Node(num);
    Node* cur=head;
    while(c!='\n'){
        cin>>num;
        cnt++;
        while(cur->next!=nullptr){
            cur=cur->next;
        }
        Node* newNode=new Node(num);
        cur->next=newNode;
        c=getchar();
    }
​
    Node* cur1=head;
    cout<<"输入对应random值:"; 
    // 输入链表节点的随机指针所指向的位置,并将其对应到相应的新链表节点上
    for(int i=0;i<cnt;i++){
        string s;
        cin>>s; // 读入节点位置信息,使用字符串类型进行存储
        if (s == "null") { // 如果输入的是 null,则随机指针为空
            cur1->random = nullptr;
        } else {
            int ran = stoi(s); // 将字符串转换为整型,得到随机指针指向的节点位置
            Node* cur2=head; // 创建指向头节点的新指针,用于查找随机指针所指向的节点
            for(int j=0;j<ran;j++){ // 从头节点开始,往后移动指针,直到找到随机指针所指向的节点
                cur2=cur2->next; 
            }
            cur1->random=cur2; // 更新随机指针所指向的新节点
        }
        cur1=cur1->next; // 移动到下一个原节点对应的新节点
    }
​
    // 打印原链表信息
    cout << "Original List:" << endl;
    printList(head);
​
    // 复制链表并打印信息
    Node* newHead = copyRandomList(head);
    cout << "Copied List:" << endl;
    printList(newHead);
​
    return 0;
}
  • 时间复杂度 O(N) : 两轮遍历链表,使用 O(N) 时间。

  • 空间复杂度 O(N) : 哈希表 dic 使用线性大小的额外空间。

  • 6
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
Rust 是一种现代的编程语言,特别适合处理内存安全和线程安全的代码。在 LeetCode 中,链表是经常出现的题目练习类型,Rust 语言也是一种非常适合处理链表的语言。接下来,本文将从 Rust 语言的特点、链表的定义和操作,以及 Rust 在 LeetCode链表题目的练习等几个方面进行介绍和讲解。 Rust 语言的特点: Rust 是一种现代化的高性能、系统级、功能强大的编程语言,旨在提高软件的可靠性和安全性。Rust 语言具有如下几个特点: 1. 内存安全性:Rust 语言支持内存安全性和原语级的并发,可以有效地预防内存泄漏,空悬指针以及数据竞争等问题,保证程序的稳定性和可靠性。 2. 高性能:Rust 语言采用了“零成本抽象化”的设计思想,具有 C/C++ 等传统高性能语言的速度和效率。 3. 静态类型检查:Rust 语言支持静态类型检查,可以在编译时检查类型错误,避免一些运行时错误。 链表的定义和操作: 链表是一种数据结构,由一个个节点组成,每个节点保存着数据,并指向下一个节点。链表的定义和操作如下: 1. 定义:链表是由节点组成的数据结构,每个节点包含一个数据元素和一个指向下一个节点的指针。 2. 操作:链表的常用操作包括插入、删除、查找等,其中,插入操作主要包括在链表首尾插入节点和在指定位置插入节点等,删除操作主要包括删除链表首尾节点和删除指定位置节点等,查找操作主要包括根据数据元素查找节点和根据指针查找节点等。 Rust 在 LeetCode链表题目的练习: 在 LeetCode 中,链表是常见的题目类型,而 Rust 语言也是一个非常适合练习链表题目的语言。在 Rust 中,我们可以定义结构体表示链表的节点,使用指针表示节点的指向关系,然后实现各种操作函数来处理链表操作。 例如,针对 LeetCode 中的链表题目,我们可以用 Rust 语言来编写解法,例如,反转链表,合并两个有序链表,删除链表中的重复元素等等,这样可以更好地熟悉 Rust 语言的使用和链表的操作,提高算法和编程能力。 总之,在 Rust 中处理链表是非常方便和高效的,而 LeetCode 中的练习也是一个非常好的机会,让我们更好地掌握 Rust 语言和链表数据结构的知识。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

搬砖c个铁

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值