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.html
Return a deep copy of the list.node
Example 1:git
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:github
这道链表的深度拷贝题的难点就在于如何处理随机指针的问题,因为每个节点都有一个随机指针,这个指针能够为空,也能够指向链表的任意一个节点,若是在每生成一个新节点给其随机指针赋值时,都要去遍历原链表的话,OJ 上确定会超时,因此能够考虑用 HashMap 来缩短查找时间,第一遍遍历生成全部新节点时同时创建一个原节点和新节点的 HashMap,第二遍给随机指针赋值时,查找时间是常数级。代码以下:dom
解法一:函数
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 指针赋值了,直接分别调用递归函数便可,参见代码以下:post
解法二:url
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 占用额外的空间,若是这道题限制了空间的话,就要考虑别的方法。下面这个方法很巧妙,能够分为如下三个步骤:spa
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
参考资料:
相似题目:
https://leetcode.com/problems/copy-list-with-random-pointer/
https://leetcode.com/problems/copy-list-with-random-pointer/discuss/43488/Java-O(n)-solution