复杂链表的复制

   对应letecode链接:

力扣

题目描述:

请实现 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:哈希表

创建哈希表保存已拷贝结点,格式 {原结点:拷贝结点}

8.jpg

第二步我们再遍历原链表,这次我们要将新链表的next和random指针给链接好

9.jpg

从上图中我们可以发现,原节点和新节点是一一对应的关系,所以

hash(原节点),得到的就是对应的新节点
hash(原节点->next),得到的就是对应的新节点->next
hash(原节点->random),得到的就是对应的新节点->random
所以,我们只需要再次遍历原链表,然后设置:
新节点->next -> hash[原节点->next]
新节点.random -> hash[原节点->random]
这样新链表的next和random都被串联起来了
最后,我们然后我们返回hash[head],也就是对应的新链表的头节点,就可以解决此问题了。

对应代码:

class Solution {
public:
    Node* copyRandomList(Node* head) {
        Node*cur=head;
        unordered_map<Node*,Node*>hash;//创建哈希表
        while(cur){
            hash.insert(make_pair(cur,new Node(cur->val)));//复制节点
            cur=cur->next;
        }
        cur=head;//复制节点的指向
        while(cur){
            hash[cur]->next=hash[cur->next];//新结点next指向同旧结点的next指向
            hash[cur]->random=hash[cur->random];///新结点random指向同旧结点的random指向
            cur=cur->next;//迭代往后走
        }
        return hash[head];//返回复制后的节点
    }
};

 方法二:迭代

个人认为这道题最大的难度就在于复制链表中的随机节点

4.jpg

比如说上面这个图中

1.节点值为1的random指向的是节点3

2.节点值为2的random指向空

3.节点值为3的random指向的是1.  

复制的链表和原链表没有任何的链接关系这使得我们很难去复制节点。但是我们可以让原链表和新链表建立链接关系 

根据遍历到的原节点创建对应的新节点,每个新创建的节点是在原节点后面,比如下图中原节点1不再指向原节点2,而是指向新节点1

5.jpg

第二步是最关键的一步,用来设置新链表的随机指针 

6.jpg

上图中,我们可以观察到这么一个规律

原节点1的随机指针指向原节点3,新节点1的随机指针指向的是原节点3的next
原节点3的随机指针指向原节点2,新节点3的随机指针指向的是原节点2的next
也就是,原节点i的随机指针(如果有的话),指向的是原节点j
那么新节点i的随机指针,指向的是原节点j的next

第三步就简单了,只要将两个链表分离开,再返回新链表就可以了

7.jpg

对应代码:

class Solution {
public:
    Node* copyRandomList(Node* head) {
          Node*cur=head;
          //链接节点
          while(cur){
              Node*copyNode=new Node(cur->val);
              Node*next=cur->next;//保存下一个
              cur->next=copyNode;
              copyNode->next=next;
              cur=next;
          }
          //链接拷贝节点中的random
          cur=head;
          while(cur){
              Node*copyNode=cur->next;
              if(cur->random==nullptr){//如果原节点中的random指向空那么我也指向空
                     copyNode->random=nullptr;
              }
              else{
                  copyNode->random=cur->random->next;//拷贝节点的random等于原节点random的next
              }
                     cur=copyNode->next;//迭代往后走
          }
              cur=head;
              Node*copyHead=new Node(-1);
              Node*ans=copyHead;
          while(cur){
            Node*copyNode=cur->next;//保存拷贝节点
            Node*next=copyNode->next;//保存原节点的下一个
            cur->next=next;//链接
            copyHead->next=copyNode;
            copyHead=copyHead->next;
            cur=next;//迭代往后走;
          }
          return ans->next;

    }
};

扁平化多级双向链表

对应letecode链接:

430. 扁平化多级双向链表 - 力扣(LeetCode) (leetcode-cn.com)

题目描述:

多级双向链表中,除了指向下一个节点和前一个节点指针之外,它还有一个子链表指针,可能指向单独的双向链表。这些子列表也可能会有一个或多个自己的子项,依此类推,生成多级数据结构,如下面的示例所示。

给你位于列表第一级的头节点,请你扁平化列表,使所有结点出现在单级双链表中。

示例 1:

输入:head = [1,2,3,4,5,6,null,null,null,7,8,9,10,null,null,11,12]
输出:[1,2,3,7,8,11,12,9,10,4,5,6]
解释:

输入的多级列表如下图所示:

扁平化后的链表如下图:


示例 2:

输入:head = [1,2,null,3]
输出:[1,3,2]
解释:

输入的多级列表如下图所示:

  1---2---NULL
  |
  3---NULL
示例 3:

输入:head = []
输出:[]

如何表示测试用例中的多级链表?

以 示例 1 为例:

 1---2---3---4---5---6--NULL
         |
         7---8---9---10--NULL
             |
             11--12--NULL
序列化其中的每一级之后:

[1,2,3,4,5,6,null]
[7,8,9,10,null]
[11,12,null]
为了将每一级都序列化到一起,我们需要每一级中添加值为 null 的元素,以表示没有节点连接到上一级的上级节点。

[1,2,3,4,5,6,null]
[null,null,7,8,9,10,null]
[null,11,12,null]
合并所有序列化结果,并去除末尾的 null 。

[1,2,3,4,5,6,null,null,null,7,8,9,10,null,null,11,12]

提示:

节点数目不超过 1000
1 <= Node.val <= 10^5

迭代:

首先扁平化第一层和第一层的孩子节点得到新的链表
对新的链表进行两层链表的扁平化,直到是单级双链表

class Solution {
public:
    Node* flatten(Node* head) {
        Node*cur=head;
        while(cur){
             Node*next=cur->next;//保存下一个
            if(cur->child){
                Node*child=cur->child;
                cur->next=child;//插入到当前节点的后面
                child->prev=cur;
                cur->child=nullptr;
                while(child&&child->next){
                    child=child->next;//一直遍历到最后一个元素
                }
                child->next=next;//链接
                if(next!=nullptr){
                    next->prev=child;//链接
                }
            }
           cur=cur->next;//迭代往后走
        }
        return head;
    }
};

使用栈:

class Solution {
public:
    Node* flatten(Node* head) {
        if(head==NULL) return nullptr;
        Node* newhead = new Node();
        stack<Node*> st;
        st.push(head);
        while(!st.empty()){
            Node* temp = st.top();
            st.pop();
            newhead->next = temp;
            temp->prev = newhead;
            newhead = temp;
            if(temp->next!=NULL){
                st.push(temp->next);
            }
            if(temp->child!=NULL){
                st.push(temp->child);   
                temp->child=NULL;
            }
        }
        head->prev=NULL;
        return head;
    }
};
class Solution {
public:
    Node* flatten(Node* head) {
        Node* node = head;
        stack<Node*> s;
        while(node != nullptr) {
            // 孩子不为空
            if(node -> child != nullptr) {
                if(node -> next != nullptr) {
                    s.push(node -> next);
                }
                Node* child = node -> child;
                // 扁平化
                node -> next = child;
                child -> prev = node;
                // 孩子要去掉
                node -> child = nullptr;
            }
            if(node -> next == nullptr && !s.empty()) {
                Node* next = s.top();
                s.pop();
                node -> next = next;
                next -> prev = node;
            }
            node = node -> next;
        }
        return head;
    }
};

  • 12
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 8
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

一个追梦的少年

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值