剑指 Offer 35. 复杂链表的复制

题目描述:

请实现 copyRandomList 函数,复制一个复杂链表。在复杂链表中,每个节点除了有一个 next 指针指向下一个节点,还有一个 random 指针指向链表中的任意节点或者 null。

 

示例 1:

输入:head = [[7,null],[13,0],[11,4],[10,2],[1,0]]
输出:[[7,null],[13,0],[11,4],[10,2],[1,0]]
示例 2:

 

输入:head = [[1,1],[2,1]]
输出:[[1,1],[2,1]]
示例 3:

输入:head = [[3,null],[3,0],[3,null]]

 


输出:[[3,null],[3,0],[3,null]]
示例 4:

输入:head = []
输出:[]
解释:给定的链表为空(空指针),因此返回 null。
 

提示:

-10000 <= Node.val <= 10000
Node.random 为空(null)或指向链表中的节点。
节点数目不超过 1000 。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/fu-za-lian-biao-de-fu-zhi-lcof
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

分析:本来以为这道题会简单一些,但是写着写着发现不是这样的,有几个问题:

1.random指向的节点可能当时还并未创建

2.节点的value是可能相同的

于是,我能想到的办法就是,先遍历第一遍,不管random,按照next的顺序,复制出来链表。然后遍历第二遍,针对原链表节点中的random,来寻找该random指向的节点在原链表中的位置(第k个),然后再去复制的链表中,找到第k个节点,把它连接到random上。事实证明也通过了,下面附上代码:

/*
// Definition for a Node.
class Node {
public:
    int val;
    Node* next;
    Node* random;
    
    Node(int _val) {
        val = _val;
        next = NULL;
        random = NULL;
    }
};
*/
class Solution {
public:
    Node* copyRandomList(Node* head) {
            Node *res=new Node(0);
            Node *head1=res;
            if(head==NULL) return NULL;
            Node *p=head;
            while(p!=NULL)
            {
                Node *temp=new Node(p->val);
                res->next=temp;
                res=res->next;
                // if(p->random==NULL)
                // res->random=NULL;
                // else
                // res->random=new Node(p->random->val);
                p=p->next;
                
            }
            p=head;
            res=head1->next;
            while(p!=NULL)
            {
                if(p->random==NULL)
                res->random=NULL;
                else
                {
                    int k=findinhead(head,p->random);
                    if(k!=-1)
                    {
                        Node* temp=findinhead1(head1,k);
                        res->random=temp;
                    }
                    else
                    {
                        res->random=NULL;

                    }
                }
                p=p->next;
                res=res->next;
            }
            return head1->next;
    }
    int findinhead(Node *head,Node *random)
    {
        int res=0;
        while(head!=NULL)
        {
            if(head==random)
            return res;
            head=head->next;
            res++;
        }
        return -1;
    }
    Node *findinhead1(Node *head1,int k)
    {
        head1=head1->next;
        if(k==0) return head1;
        
        while(k--)
        {
            
            head1=head1->next;
        }

        return head1;
    }
};

然而这个方法肯定不是最简单的,因为遍历了好几次链表,有没有只用遍历一次就可以完成链表的深拷贝呢?我想不出来,看了看题解,如果用hash表的话,或许可以实现。

思路如下:

遍历节点的时候,节点的next和random如果未创建,那么就创建节点,并把指针保存到hash表中,当再次用到该节点时,再去hash表里寻找。如果hash表中没有,再进行创建。

代码如下:

/*
// Definition for a Node.
class Node {
public:
    int val;
    Node* next;
    Node* random;
    
    Node(int _val) {
        val = _val;
        next = NULL;
        random = NULL;
    }
};
*/
class Solution {
public:
    Node* copyRandomList(Node* head) {
        // 创建hash表
        unordered_map<Node* ,Node*> map;
        Node *p=head;
        // 伪头节点
        Node *head1=new Node(0);
        Node *p1=head1;
        // 原链表进行遍历
        while(p!=NULL)
        {
            // 如果该节点未被创建
            if(map.find(p)==map.end())
            {
                // 创立新节点并连接
                Node *temp=new Node(p->val);
                p1->next=temp;
                p1=p1->next;
                map.insert(make_pair(p,temp));
            }
            else
            {
                // 如果该节点已经被创建
                // 找到该节点并连接
                auto it=map.find(p);
                p1->next=it->second;
                p1=p1->next;
            }
           
            if(p->random==NULL)
            p1->random=NULL;
            else
            {
                // 如果random未被创建
                if(map.find(p->random)==map.end())
                {
                    // 创建并连接
                    Node *temp=new Node(p->random->val);
                    p1->random=temp;
                    map.insert(make_pair(p->random,temp));
                }
                else
                {
                    // 如果random已经创建
                    // 找到并连接
                    auto it=map.find(p->random);
                    p1->random=it->second;
                }
            }
            p=p->next;
        }
        return head1->next;
    }
};

看了看题解,感觉自己的还是太复杂了,我没有用递归,但是题解用了递归,使得代码很简洁,思路很清晰,但是我一开始是想不到的。 

 看完题解,我只能说自己真的太弱了,别人的方法对于我几乎是当头一棒,醍醐灌顶。

/*
// Definition for a Node.
class Node {
public:
    int val;
    Node* next;
    Node* random;
    
    Node(int _val) {
        val = _val;
        next = NULL;
        random = NULL;
    }
};
*/
class Solution {
public:
    unordered_map<Node*,Node*> map;
    Node* copyRandomList(Node* head) {
        // 如果当前节点为空,就返回NULL
        if(head==NULL)
        return NULL;
        // 如果当前节点没有被创建
        if(map.find(head)==map.end())
        {
            // 创建当前节点
            Node *temp=new Node(head->val);
            // 当前节点存入hash表
            map[head]=temp;
            // 递归的方法,来对temp->next和temp->random进行赋值
            temp->next=copyRandomList(head->next);
            temp->random=copyRandomList(head->random);
        }
        // 如果当前节点已经创建,直接从hash表里找到,并返回即可
        return map[head];
    }
};

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值