C++实现LeetCode(138.拷贝带有随机指针的链表)

[LeetCode] 138. Copy List with Random Pointer 拷贝带有随机指针的链表

A linked list is given such that each node contains an additional random pointer which could point to any node in the list or null.

Return a deep copy of the list.

Example 1:

Input:
{"$id":"1","next":{"$id":"2","next":null,"random":{"$ref":"2"},"val":2},"random":{"$ref":"2"},"val":1}  Explanation: Node 1's value is 1, both of its next and random pointer points to Node 2. Node 2's value is 2, its next pointer points to null and its random pointer points to itself.

Note:

  1. You must return the copy of the given head as a reference to the cloned list.

这道链表的深度拷贝题的难点就在于如何处理随机指针的问题,由于每一个节点都有一个随机指针,这个指针可以为空,也可以指向链表的任意一个节点,如果在每生成一个新节点给其随机指针赋值时,都要去遍历原链表的话,OJ 上肯定会超时,所以可以考虑用 HashMap 来缩短查找时间,第一遍遍历生成所有新节点时同时建立一个原节点和新节点的 HashMap,第二遍给随机指针赋值时,查找时间是常数级。代码如下:

解法一:

class Solution {
public:
    Node* copyRandomList(Node* head) {
        if (!head) return nullptr;
        Node *res = new Node(head->val, nullptr, nullptr);
        Node *node = res, *cur = head->next;
        unordered_map<Node*, Node*> m;
        m[head] = res;
        while (cur) {
            Node *t = new Node(cur->val, nullptr, nullptr);
            node->next = t;
            m[cur] = t;
            node = node->next;
            cur = cur->next;
        }
        node = res; cur = head;
        while (cur) {
            node->random = m[cur->random];
            node = node->next;
            cur = cur->next;
        }
        return res;
    }
};

我们可以使用递归的解法,写起来相当的简洁,还是需要一个 HashMap 来建立原链表结点和拷贝链表结点之间的映射。在递归函数中,首先判空,若为空,则返回空指针。然后就是去 HashMap 中查找是否已经在拷贝链表中存在了该结点,是的话直接返回。否则新建一个拷贝结点 res,然后建立原结点和该拷贝结点之间的映射,然后就是要给拷贝结点的 next 和 random 指针赋值了,直接分别调用递归函数即可,参见代码如下:

解法二:

class Solution {
public:
    Node* copyRandomList(Node* head) {
        unordered_map<Node*, Node*> m;
        return helper(head, m);
    }
    Node* helper(Node* node, unordered_map<Node*, Node*>& m) {
        if (!node) return nullptr;
        if (m.count(node)) return m[node];
        Node *res = new Node(node->val, nullptr, nullptr);
        m[node] = res;
        res->next = helper(node->next, m);
        res->random = helper(node->random, m);
        return res;
    }
};

当然,如果使用 HashMap 占用额外的空间,如果这道题限制了空间的话,就要考虑别的方法。下面这个方法很巧妙,可以分为以下三个步骤:

1. 在原链表的每个节点后面拷贝出一个新的节点。

2. 依次给新的节点的随机指针赋值,而且这个赋值非常容易 cur->next->random = cur->random->next。

3. 断开链表可得到深度拷贝后的新链表。

举个例子来说吧,比如原链表是 1(2) -> 2(3) -> 3(1),括号中是其 random 指针指向的结点,那么这个解法是首先比遍历一遍原链表,在每个结点后拷贝一个同样的结点,但是拷贝结点的 random 指针仍为空,则原链表变为 1(2) -> 1(null) -> 2(3) -> 2(null) -> 3(1) -> 3(null)。然后第二次遍历,是将拷贝结点的 random 指针赋上正确的值,则原链表变为 1(2) -> 1(2) -> 2(3) -> 2(3) -> 3(1) -> 3(1),注意赋值语句为:

cur->next->random = cur->random->next;

这里的 cur 是原链表中结点,cur->next 则为拷贝链表的结点,cur->next->random 则为拷贝链表的 random 指针。cur->random 为原链表结点的 random 指针指向的结点,因为其指向的还是原链表的结点,所以我们要再加个 next,才能指向拷贝链表的结点。最后再遍历一次,就是要把原链表和拷贝链表断开即可,参见代码如下:

解法二:

class Solution {
public:
    Node* copyRandomList(Node* head) {
        if (!head) return nullptr;
        Node *cur = head;
        while (cur) {
            Node *t = new Node(cur->val, nullptr, nullptr);
            t->next = cur->next;
            cur->next = t;
            cur = t->next;
        }
        cur = head;
        while (cur) {
            if (cur->random) cur->next->random = cur->random->next;
            cur = cur->next->next;
        }
        cur = head;
        Node *res = head->next;
        while (cur) {
            Node *t = cur->next;
            cur->next = t->next;
            if (t->next) t->next = t->next->next;
            cur = cur->next;
        }
        return res;
    }
};

Github 同步地址:

https://github.com/grandyang/leetcode/issues/138

参考资料:

Clone Graph

类似题目:

https://leetcode.com/problems/copy-list-with-random-pointer/

https://leetcode.com/problems/copy-list-with-random-pointer/discuss/43488/Java-O(n)-solution

https://leetcode.com/problems/copy-list-with-random-pointer/discuss/43567/C%2B%2B-simple-recursive-solution

https://leetcode.com/problems/copy-list-with-random-pointer/discuss/43491/A-solution-with-constant-space-complexity-O(1)-and-linear-time-complexity-O(N)

到此这篇关于C++实现LeetCode(138.拷贝带有随机指针的链表)的文章就介绍到这了,更多相关C++实现拷贝带有随机指针的链表内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • C++实现LeetCode(130.包围区域)

    [LeetCode] 130. Surrounded Regions 包围区域 Given a 2D board containing 'X' and 'O'(the letter O), capture all regions surrounded by 'X'. A region is captured by flipping all 'O's into 'X's in that surrounded region. Example: X X X X X O O X X X O X X O

  • C++实现LeetCode(126.词语阶梯之二)

    [LeetCode] 126. Word Ladder II 词语阶梯之二 Given two words (beginWord and endWord), and a dictionary's word list, find all shortest transformation sequence(s) from beginWord to endWord, such that: Only one letter can be changed at a time Each transformed

  • C++实现LeetCode(124.求二叉树的最大路径和)

    [LeetCode] 124. Binary Tree Maximum Path Sum 求二叉树的最大路径和 Given a non-empty binary tree, find the maximum path sum. For this problem, a path is defined as any sequence of nodes from some starting node to any node in the tree along the parent-child conn

  • C++实现LeetCode(200.岛屿的数量)

    [LeetCode] 200. Number of Islands 岛屿的数量 Given a 2d grid map of '1's (land) and '0's (water), count the number of islands. An island is surrounded by water and is formed by connecting adjacent lands horizontally or vertically. You may assume all four

  • C++实现LeetCode(127.词语阶梯)

    [LeetCode] 127.Word Ladder 词语阶梯 Given two words (beginWord and endWord), and a dictionary's word list, find the length of shortest transformation sequence from beginWord to endWord, such that: Only one letter can be changed at a time. Each transforme

  • C++实现LeetCode(128.求最长连续序列)

    [LeetCode] 128.Longest Consecutive Sequence 求最长连续序列 Given an unsorted array of integers, find the length of the longest consecutive elements sequence. Your algorithm should run in O(n) complexity. Example: Input: [100, 4, 200, 1, 3, 2] Output: 4 Expl

  • C++验证LeetCode包围区域的DFS方法

    验证LeetCode Surrounded Regions 包围区域的DFS方法 在LeetCode中的Surrounded Regions 包围区域这道题中,我们发现用DFS方法中的最后一个条件必须是j > 1,如下面的红色字体所示,如果写成j > 0的话无法通过OJ,一直百思不得其解其中的原因,直到有网友告诉我说他验证了最后一个大集合在本地机子上可以通过,那么我也来验证看看吧. class Solution { public: void solve(vector<vector<

  • C++实现LeetCode(129.求根到叶节点数字之和)

    [LeetCode] 129. Sum Root to Leaf Numbers 求根到叶节点数字之和 Given a binary tree containing digits from 0-9 only, each root-to-leaf path could represent a number. An example is the root-to-leaf path 1->2->3 which represents the number 123. Find the total sum

  • C++实现LeetCode(138.拷贝带有随机指针的链表)

    [LeetCode] 138. Copy List with Random Pointer 拷贝带有随机指针的链表 A linked list is given such that each node contains an additional random pointer which could point to any node in the list or null. Return a deep copy of the list. Example 1: Input: {"$id"

  • PHP如何通过带尾指针的链表实现'队列'

    这篇文章是展示通过 PHP 语言实现一种带 尾指针 的链表,然后通过链表来实现队列,其中链表的头元素 head 是用于列队 出队 的,它的时间复杂度 O(1) ,若在 head 的基础上实现链表尾部 入队 时间度为 O(n),为了降低入队操作的时间复杂度,可以给链表维护一个带有尾指针的变量 tail ,这样每次入队的时候直接操作 tail ,出队的时候直接操作 head ,这样可以使得 入队 和 出队 时间复杂度都是 O(1). 1.output_queue_by_liked_list.php

  • C++实现LeetCode(25.每k个一组翻转链表)

    [LeetCode] 25. Reverse Nodes in k-Group 每k个一组翻转链表 Given a linked list, reverse the nodes of a linked list k at a time and return its modified list. k is a positive integer and is less than or equal to the length of the linked list. If the number of n

  • 嵌入式C语言二级指针在链表中的应用

    目录 重读了两本书:Stephen A.Maguire的<编程精粹:Microsoft编写优质无错C程序秘诀>和David R. Hanson的<C语言接口与实现:创建可重用软件的技术>.两本书都有对链表的操作. 假设有如图所示的链表,链表节点的pb成员指向一个缓冲块,删除节点函数根据缓冲块的首地址,找到节点并删除节点: <编程精粹>使用一个变量pbiPrev来保存前一个节点位置,并且要处理删除的是第一个节点A这种边界条件: void FreeBlockInfo(byt

  • 详解C语言中二级指针与链表的应用

    目录 前言 二级指针讲解 链表的应用 定义双链表的结构体 创建双链表 前言 这篇文章即将解决你看不懂或者不会写链表的基本操作的问题,对于初学者而言,有很多地方肯定是费解的.比如函数的参数列表的多样化,动态分配内存空间函数malloc等,其实这些知识和指针联系紧密,尤其是二级指针.那么开始好好的学习这篇文章吧! 二级指针讲解 简述:其实就是一个指针指向另一个指针的地址. 我们都知道指针指向地址,但是指针自身也是一个变量,当然也可以被二级指针所指向. 语法:形如 int x = 10; int *q

  • C语言数据结构之复杂链表的拷贝

    题目: 给你一个长度为 n 的链表,每个节点包含一个额外增加的随机指针 random ,该指针可以指向链表中的任何节点或空节点. 构造这个链表的 深拷贝. 深拷贝应该正好由 n 个 全新 节点组成,其中每个新节点的值都设为其对应的原节点的值.新节点的 next 指针和 random 指针也都应指向复制链表中的新节点,并使原链表和复制链表中的这些指针能够表示相同的链表状态.复制链表中的指针都不应指向原链表中的节点 . 例如,如果原链表中有 X 和 Y 两个节点,其中 X.random --> Y

  • Python数据结构与算法之跳表详解

    目录 0. 学习目标 1. 跳表的基本概念 1.1 跳表介绍 1.2 跳表的性能 1.3 跳表与普通链表的异同 2. 跳表的实现 2.1 跳表结点类 2.2 跳表的初始化 2.3 获取跳表长度 2.4 读取指定位置元素 2.5 查找指定元素 2.6 在跳表中插入新元素 2.7 删除跳表中指定元素 2.8 其它一些有用的操作 3. 跳表应用 3.1 跳表应用示例 0. 学习目标 在诸如单链表.双线链表等普通链表中,查找.插入和删除操作由于必须从头结点遍历链表才能找到相关链表,因此时间复杂度均为O(

  • Java 集合框架掌握 Map 和 Set 的使用(内含哈希表源码解读及面试常考题)

    目录 1. 搜索 1.1 场景引入 1.2 模型 2. Map 2.1 关于 Map 的介绍 2.2 关于 Map.Entry<K, V> 的介绍 2.3 Map 的常用方法说明 2.4 关于 HashMap 的介绍 2.5 关于 TreeMap 的介绍 2.6 HashMap 和 TreeMap 的区别 2.7 Map 使用示例代码 3. Set 3.1 关于 Set 的介绍 3.1 Set 的常用方法说明 3.3 关于 TreeSet 的介绍 3.4 关于 HashSet 的介绍 3.5

  • Java数据结构之Map与Set专篇讲解

    目录 ①只出现一次的数字 ②宝石与石头 ③坏键盘打字 ④复制带随机指针的链表 ①只出现一次的数字 给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现两次.找出那个只出现了一次的元素. 输入: [2,2,1]输出: 1 首相我们可能会想到用位运算直接解决,但我们也可以用hash色条解决. public int singleNumber(int[] nums) { int single = 0; for (int num : nums) { single ^= num; } ret

  • C++基础教程之指针拷贝详解

    C++基础教程之指针拷贝详解 指针是编程人员的梦魇,对C语言的开发者是如此,对C++的开发者也是如此.特别是在C++中,如果不注意处理类中的指针,非常容易出问题.如果朋友们不相信可以看看下面的代码: class data { int* value; public: data(int num){ if(num > 0) value = (int*)malloc(sizeof(int)* num); } ~data(){ if(value) free(value); } }; void proces

随机推荐