剑指Offer 35.复杂链表的复制(LeetCode 138.复制带随机指针的链表)

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

 

请实现 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 。

 

 

解:

解法1:

最循规蹈矩的做法,也是人能最先想到的做法,便是新建并逐个复制每个结点并链接起来,再遍历得到每个结点的random成员指向的结点在链表中的位置(联想到数组的索引),最后再遍历新的链表,将对应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) {
        if(!head)
            return nullptr;
        int Len = 0,i = 0;
        Node* copyhead = new Node(head -> val);
        Node* Nptr = copyhead;
        const auto const original_head = head;
        vector<int> rand_idx;                             //存储每结点random对应位置
        while(head)                                   //新建链表
        {
            if(head -> next)
                Nptr -> next = new Node(head -> next -> val);
            else
                Nptr -> next = nullptr;
            int i = 0;
            if(head -> random == nullptr)
                rand_idx.push_back(-1);
            else if(head -> random == head)                    //random指向自己
                rand_idx.push_back(Len);
            else
            {
                for(auto temp = original_head;temp;temp = temp -> next, ++i)//遍历老链表,得到random指向结点所在位置
                {
                    if(temp == head)
                        continue;
                    if(temp == head -> random )
                    {
                        rand_idx.push_back(i);
                        // cout<<i<<'\t';
                        break;
                    }
                }
            }
            Len++;
            Nptr = Nptr -> next;
            head = head -> next;
        }
        
        i = 0;
        Nptr = copyhead;
        while(Nptr)
        {

            int j = 0;
            for(auto temp = copyhead;temp;temp = temp -> next,++j)    //按照得到的random位置再次遍历新链表,拿到random地址
            {
                if(rand_idx[i] == -1)
                    break;
                else if(rand_idx[i] == j)
                {
                    Nptr->random = temp;
                    break;
                }
            }
            Nptr = Nptr -> next;
            i++;
        }


    return copyhead;
    }
};
复制代码

 

此法时间复杂度为O(N2),空间复杂度为O(1),容易看到此方法瓶颈在于为了得到random的索引和random的地址遍历过多。

因此便有了解法2。

 

解法2:

利用哈希表的查询特点,考虑构建 原链表节点 和 新链表对应节点 的键值对映射关系,再遍历构建新链表各节点的 next 和 random 引用指向即可。

算法流程:
1.若头节点 head 为空节点,直接返回 null;
2.初始化: 哈希表 dic , 节点 cur 指向头节点;
3.复制链表
  建立新节点,并向 dic 添加键值对 (原 cur 节点, 新 cur 节点) ;
  cur 遍历至原链表下一节点;
4.构建新链表的引用指向:
  构建新节点的 next 和 random 引用指向;
  cur 遍历至原链表下一节点;
5.返回值: 新链表的头节点 dic[cur] ;

复制代码
class Solution {
public:
    Node* copyRandomList(Node* head) {
        if(head == nullptr) return nullptr;
        Node* cur = head;
        unordered_map<Node*, Node*> map;
        // 3. 复制各节点,并建立 “原节点 -> 新节点” 的 Map 映射
        while(cur != nullptr) {
            map[cur] = new Node(cur->val);
            cur = cur->next;
        }
        cur = head;
        // 4. 构建新链表的 next 和 random 指向
        while(cur != nullptr) {
            map[cur]->next = map[cur->next];
            map[cur]->random = map[cur->random];
            cur = cur->next;
        }
        // 5. 返回新链表的头节点
        return map[head];
    }
};

/*
作者:jyd
链接:https://leetcode-cn.com/problems/fu-za-lian-biao-de-fu-zhi-lcof/solution/jian-zhi-offer-35-fu-za-lian-biao-de-fu-zhi-ha-xi-/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
*/
复制代码

 

复杂度分析:
时间复杂度 O(N) : 两轮遍历链表,使用 O(N)时间。
空间复杂度 O(N): 哈希表 dic 使用线性大小的额外空间。

 

解法3:

解法2的哈希表额外空间,以空间复杂度O(N)换来了时间复杂度O(N),还可以把空间复杂度降下来。

即原地复制,将复制后的新结点直接连在原结点后。这样random的地址很容易获得,也没占用额外空间。

共进行3次循环。

1.复制结点;

2.得到每个random地址。

3.拆分新链表。

复制代码
/*
// 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) 
    {
        if(!head)
            return nullptr;
        auto Cur = head, Next = head -> next;


        while(Next)                         //在每个节点后面复制相同的节点并链接
        {
            Cur -> next = new Node(Cur -> val);
            Cur -> next -> next = Next;
            Cur = Next;
            Next = Cur -> next;
        }
        Cur -> next = new Node(Cur -> val);
        Cur -> next -> next = nullptr;

        Cur = head;
        Next = head -> next;               //完善上述复制节点的random成员
        while(Next -> next)
        {
            if(Cur -> random)
                Next -> random = Cur -> random -> next;
            Cur = Next -> next;
            Next = Cur -> next;
        }
        if(Cur -> random)
                Next -> random = Cur -> random -> next;

        Cur = head;
        Next = head -> next;               //拆分新节点,生成新链表,还原原链表。
        auto Newhead = Next;
        while(Next -> next)
        {
            Cur -> next = Next -> next;
            Next -> next = Cur -> next -> next;
            Cur = Cur -> next;
            Next = Next -> next;
        }
        Cur -> next = nullptr;
        

        return Newhead;
    }
};
复制代码

复杂度分析:
时间复杂度 O(N): 三轮遍历链表,使用 O(N)时间。
空间复杂度 O(1): 节点引用变量使用常数大小的额外空间。

 

 

posted @   Jasmine_Sokko  阅读(58)  评论(0编辑  收藏  举报
编辑推荐:
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· 没有源码,如何修改代码逻辑?
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· DeepSeek 开源周回顾「GitHub 热点速览」
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
点击右上角即可分享
微信分享提示