链表面试题10之随机链表的复制
好,这篇文章将是链表面试题的最后一题,后面就将进入队列和二叉树。
老规矩可以往前回顾一下我们的思考。
这里继续把题目链接贴一下:138. 随机链表的复制 - 力扣(LeetCode)
乍一看整个图我们摸不着一点头脑,但是没关系,我们先来看一下,题目所给出的例子中,这个链表有两个指针域, 那这非常像两个单链表的叠加,或者说,我们再来看一下双向链表,是否可以把这个链表比拟成双向链表去理解,来更好地让我们探索题意。
没学过双向链表也不要紧,下面是双向链表:
也就是说,双向链表就是可以两头都进行增删查改的链表,一定程度上既可以向前遍历,又可以向后遍历,那么这道题目里面不就也可以看成这样吗,不过是前指针指向是随机的,我们不知道如何处理而已。
再回头看我们的题目,要求我们复制这个随机链表,那么对于这种遍历并输出的做法,我们一般马上就想到迭代,这应该是肌肉记忆了,因为仅用遍历是存储不了而导致输出不了内容的。
迭代法:时间复杂度O(n)
题目要求我们复制一个长度为 n 的链表,该链表除了每个节点有一个指针指向下一个节点外,还有一个额外的指针指向链表中的任意节点或者 null,如下图所示:
把原图的画直了我们就发现问题其实很简单,就是分治的思想 ,首先让我们去复制一个普通的单链表肯定没问题,难的就是复制一个带随机指针的链表。
如何去复制一个带随机指针的链表?
首先我们可以忽略 random 指针,然后对原链表的每个节点进行复制,并追加到原节点的后面,而后复制 random 指针。最后我们把原链表和复制链表拆分出来,并将原链表复原。
图示过程如下:
1、在每个节点的后面加上它的复制,并将原链表和复制链表连在一起。
2.从前往后遍历每一个原链表节点,对于有 random 指针的节点 p,我们让它的
p->next->random = p->random->next,这样我们就完成了对原链表 random 指针的复刻。
3、最后我们把原链表和复制链表拆分出来,并将原链表复原。
这里先给大家贴一下代码,再讲一下为什么要这么做:
class Solution {
public:Node* copyRandomList(Node* head) {for(auto p = head; p; p = p->next->next) //复制每个节点,并将原链表和复制链表连在一起。{auto q = new Node(p->val);q->next = p->next;p->next = q;}for(auto p = head; p; p = p->next->next) //复制random指针{if(p->random)p->next->random = p->random->next;}//拆分两个链表,并复原原链表auto dummy = new Node(-1), cur = dummy; for(auto p = head; p; p = p->next){auto q = p->next;cur = cur->next = q;p->next = q->next;}return dummy->next;}
};
为什么要想到这种方法呢?因为复制链表的时候我们无法复制原来链表的random的指向,我们只能复制单链表,那我们如果复制一个链表的话就得让新链表的指向也指向新链表的元素,两个链表之间基本上没有关联,这个时候就得别具一格,也就是说强行链接关系。
好了,这道题就讲到这里
如果你觉得对你有帮助,可以点赞关注加收藏,感谢您的阅读,我们下一篇文章再见。
一步步来,总会学会的,首先要懂思路,才能有东西写。