【剑指offer-C++】JZ35:复杂链表的复制

【剑指offer-C++】JZ35:复杂链表的复制

题目描述

描述:输入一个复杂链表(每个节点中有节点值,以及两个指针,一个指向下一个节点,另一个特殊指针random指向一个随机节点),请对此链表进行深拷贝,并返回拷贝后的头结点。(注意,输出结果中请不要返回参数中的节点引用,否则判题程序会直接返回空)。 下图是一个含有5个结点的复杂链表。图中实线箭头表示next指针,虚线箭头表示random指针。为简单起见,指向null的指针没有画出。

在这里插入图片描述

示例:
输入:{1,2,3,4,5,3,5,#,2,#}
输出:{1,2,3,4,5,3,5,#,2,#}
解析:我们将链表分为两段,前半部分{1,2,3,4,5}为ListNode,后半部分{3,5,#,2,#}是随机指针域表示。

以上示例前半部分可以表示链表为的ListNode:1->2->3->4->5;后半部分,3,5,#,2,#分别的表示为1的位置指向3,2的位置指向5,3的位置指向null,4的位置指向2,5的位置指向null

如下图:

在这里插入图片描述

输入:{1,2,3,4,5,3,5,#,2,#}
返回值:{1,2,3,4,5,3,5,#,2,#}

解题思路

复杂链表的复制:最直观的想法是,如果没有随机指针,那么直接进行尾插法即可,如果寻找链表的中间位置,那么使用快慢指针其中每次快指针走两步慢指针走一步即可,但是随机指针可能指向空,如何判断是尾指针呢?深拷贝后随机指针与原指针不一致,如何实现映射呢?方法来啦!虽然随机指针可能指向空,但是前半部分原链表各个节点均会出现且不为空,线性遍历判断是否为空是不可能的,所以此处使用一个map映射,这样只会将原链表部分映射一次即可,注意,此处的映射是原链表节点和深拷贝节点的映射,因为原链表的链表指针以及随机指针均有。

RandomListNode* Clone(RandomListNode* pHead) 
{
    //创建头结点
    RandomListNode *head=new RandomListNode(-1);
    head->next=nullptr;
    head->random=nullptr;
    //尾结点
    RandomListNode *tail=head;
    //当前结点
    RandomListNode *cur=pHead;
    //原链表结点与深拷贝结点的映射
    unordered_map<RandomListNode *,RandomListNode *> umap;
    //当当前结点不为空并且没有映射 创建原链表结点并且实现next
    while(cur&&umap.find(cur)==umap.end())
    {
        //拷贝结点
        RandomListNode *dcopy=new RandomListNode(cur->label);
        dcopy->next=nullptr;
        //衔接结点
        tail->next=dcopy;
        //映射结点
        umap[cur]=dcopy;
        //移动结点
        tail=tail->next;
        cur=cur->next;
     }
     //根据映射完成原链表节点的random指针
     for(auto [key,value]:umap)
     {
        value->random=key->random==nullptr?nullptr:umap[key->random];
     }
     return head->next;
}

优化:上述方法我们使用了哈希表即使用了辅助空间,那么能不能写出原地实现的方法呢?当然可以!我们首先遍历原链表并创建拷贝链表,将拷贝链表插在对应的原链表之后,得到形如原1-拷1-原2-拷2…的链表,然后通过原链表的随机指针来设置拷贝链表的随机指针,接着将原链表和拷贝链表进行拆分出来,即一个指针指向原链表第一个结点,一个指针指向拷贝链表第一个结点,然后两个指针均每次移动两个位置即可实现。

RandomListNode* Clone(RandomListNode* pHead) 
{
   if(!pHead) return NULL; //空
   //拷贝结点
   RandomListNode *cur=pHead;
   while(cur)
   {
       //创建结点
       RandomListNode *Node=new RandomListNode(cur->label);
       Node->next=NULL;
       //将拷贝结点插在源结点后面
       Node->next=cur->next;
       cur->next=Node;
       //更新当前结点为拷贝结点的下一位
       cur=Node->next;
    }
    //处理拷贝结点的随机指针
    RandomListNode *old=pHead,*newn=pHead->next;
    while(old)
    {
        //将原链表的随机指针赋值给拷贝链表随机指针
        newn->random=old->random==NULL?NULL:old->random->next;
        //原链表走两步
        if(old->next) old=old->next->next;
        //拷贝链表走两步
        if(newn->next) newn=newn->next->next;
    }
    //分离出原链表和拷贝链表
    old=pHead;
    newn=pHead->next;
    //拷贝链表头结点
    RandomListNode *head=newn;
    while(old)
    {
        if(old->next) old->next=old->next->next;
        if(newn->next) newn->next=newn->next->next;
        old=old->next;
        newn=newn->next;
    }
    return head;
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值