Leetcode——扁平化多级双向链表

1. 扁平化多级双向链表

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

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

示例 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]

解释:
输入的多级列表如下图所示:
在这里插入图片描述
扁平化后的链表如下图:
在这里插入图片描述
在这里插入图片描述

(1)递归(模拟)

为防止空节点等边界问题,起始时建立一个哨兵节点 dummy 指向 head,然后利用 head 指针从前往后处理链表:

  • 当前节点 head 没有 child 节点:直接让指针后即可,即 head = head.nexthead=head.next;
  • 当前节点 head 有 child 节点:将 head.child 传入函数 flatten 递归处理,拿到普遍化后的头结点 chead,然后将 head 和 chead 建立“相邻”关系(注意要先存起来原本的 tmp = head.next 以及将 head.child置空),然后继续往后处理,直到扁平化的 chead 链表的尾部,将其与 tmp 建立“相邻”关系。
/*
// Definition for a Node.
class Node {
    public int val;
    public Node prev;
    public Node next;
    public Node child;
};
*/

class Solution {
    public Node flatten(Node head) {
        Node dummy = new Node(0);
        dummy.next = head;
        
        while (head != null) {
            if (head.child == null) {
                head = head.next;
            } else {
                //存储下一个节点
                Node tmp = head.next; 

                //开始遍历子节点,直到遍历完最后一个子节点
                Node chead = flatten(head.child);
                head.next = chead;
                chead.prev = head;
                head.child = null;      //将 head.child置空
                while (head.next != null)
                    head = head.next;
                
                //开始继续遍历下一个节点(非子节点)
                head.next = tmp;
                if (tmp != null)
                    tmp.prev = head;
                head = tmp;
            }
        }
        return dummy.next;
    }
}

(2)递归(优化)

上述解法中,由于我们直接使用 flatten 作为递归函数,导致递归处理 head.childhead.child 后不得不再进行遍历来找当前层的“尾结点”,这导致算法复杂度为 O(n2)

可以额外设计一个递归函数 dfs 用于返回扁平化后的链表“尾结点”,从而确保我们找尾结点的动作不会在每层发生。

/*
// Definition for a Node.
class Node {
    public int val;
    public Node prev;
    public Node next;
    public Node child;
};
*/

class Solution {
    public Node flatten(Node head) {
        dfs(head);
        return head;
    }

    public Node dfs(Node head) {
        Node last = head;
        while (head != null) {
            //如果没有子节点
            if (head.child == null) {
                last = head;
                head = head.next;
            }
            else {
                //存储下一个节点 
                Node tmp = head.next;

                //开始遍历子节点,直到遍历完最后一个子节点
                Node childLast = dfs(head.child);
                head.next = head.child;
                head.child.prev = head;
                head.child = null;

                //开始继续遍历下一个节点(非子节点)
                if (childLast != null) 
                    childLast.next = tmp;
                if (tmp != null) 
                    tmp.prev = childLast;
                last = head;
                head = childLast;
            }
        }
        return last;
    }
}

(3)迭代

与「递归」不同的是,「迭代」是以“段”为单位进行扁平化,而「递归」是以深度(方向)进行扁平化,这就导致了两种方式对每个扁平节点的处理顺序不同。

递归 的处理节点(新的 nextnext 指针的构建)顺序为:
在这里插入图片描述
迭代 的处理节点(新的 nextnext 指针的构建)顺序为:
在这里插入图片描述

class Solution {
    public Node flatten(Node head) {
        Node dummy = new Node(0);
        dummy.next = head;
        for (; head != null; head = head.next) {
            if (head.child != null) {
                Node tmp = head.next;
                Node child = head.child;
                head.next = child;
                child.prev = head;
                head.child = null;
                Node last = head;
                while (last.next != null) last = last.next;
                last.next = tmp;
                if (tmp != null) tmp.prev = last;
            }
        }
        return dummy.next;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Yawn__

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

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

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

打赏作者

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

抵扣说明:

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

余额充值