目录
这道题考的本质还是链表的增删查改,废话不多说,我们具体来看一下吧
分析思路
这道题不同于我们学过的链表的地方是它每个结点都有一个随机指针,这个指针指向的是一个随机结点也可以为空结点,这道题的目的就是拷贝一份这样的链表,要求拷贝得到的链表中的random指针指向关系与原链表指向关系一致。
拷贝原链表不难,如何让拷贝得到的链表中的random指针指向关系对应才是这道题的难点。
我们正常思路肯定是先把原链表拷贝一份得到一个新链表,这时候需要操作的就是将random指向对应位置就可以了,但是这里的问题在于我们得到的新链表是用malloc开辟出来的,它与原结点是没有关联的,而且所有结点的地址都是随机的,所以找对应结点的地址是不可能的;当然也不要想着找对应结点的值,因为值是可以相同的。
我们继续这个思路往下走,我们可以从相对位置下手试试看,比如原链表该结点的random与倒数第二个结点链接,那么在新的链表里对应的结点也应该是倒数第二个,这样对应关系就找到了,那么我们思考一下这种方法的时间复杂度是多少?答案是O(N*N)。这种方法是可行的,但我们这里要探讨的是这道题的最优解,也就是时间复杂度为O(N).
这里还有一种方法是创建两个指针数组,分别存放两个链表每个结点的地址,依然是利用上述相对位置的方法,在存放原链表结点地址的指针数组中找到random要对应的结点的下标,在存放新链表结点地址的数组中找到对应下标即可,这样虽然可以简化过程,但时间复杂度依然到不了O(N),依然是O(N*N)。
最优解
我们最优方法是要在找到原结点之后,就可以快速找到原结点的拷贝点,也就是将它们建立关系。
ok那么怎么做呢?第一步:我们将拷贝点链接到原结点的后边,如图:
第二步:拷贝结点的random是原结点的random的next
我们拿上图中的原13举例,13的random指向的原结点是7,那么13的拷贝的random指向的就是原13的random(就是原7)的next(就是拷贝7)
第三步:将拷贝结点取下,链接成新链表,恢复原链表
到这呢,我们思路就出来了,代码实现一下:
代码实现
struct Node* copyRandomList(struct Node* head) {
//1.将拷贝点链接到原结点的后边
struct Node* cur=head;
while(cur)
{
struct Node* copy=(struct Node*)malloc(sizeof(struct Node));
copy->val=cur->val;
struct Node* next=cur->next;
cur->next=copy;
copy->next=next;
cur=next;
}
//2.拷贝结点的random是原结点的random的next
cur=head;
while(cur)
{
struct Node* copy=cur->next;
if(cur->random==NULL)
{
copy->random=NULL;
}
else
{
copy->random=cur->random->next;
}
cur=cur->next->next;
}
//3.将拷贝结点取下,链接成新链表,恢复原链表
cur=head;
struct Node* copyhead=NULL,*copytail=NULL;
while(cur)
{
struct Node* copy=cur->next;
struct Node* next=copy->next;
if(copyhead==NULL)
{
copyhead=copytail=copy;
}
else
{
copytail->next=copy;
copytail=copytail->next;
}
cur->next=next;
cur=next;
}
return copyhead;
}
到这里这道题就完结啦,撒花!撒花!看到这,我只想说:期待你的关注!!!