开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第22天,点击查看活动详情
430. 扁平化多级双向链表
来源:力扣(LeetCode) 链接:leetcode-cn.com/problems/fl…
多级双向链表中,除了指向下一个节点和前一个节点指针之外,它还有一个子链表指针,可能指向单独的双向链表。这些子列表也可能会有一个或多个自己的子项,依此类推,生成多级数据结构,如下面的示例所示。
给你位于列表第一级的头节点,请你扁平化列表,使所有结点出现在单级双链表中。
示例 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
解法
- 递归:当我们遍历到某个节点 node 时,如果它的 child 成员不为空,那么我们需要将 child 指向的链表结构进行扁平化,并且插入 node 与node 的下一个节点之间。
因此,我们在遇到 child 成员不为空的节点时,就要先去处理child 指向的链表结构,这就是一个「深度优先搜索」的过程。当我们完成了对child 指向的链表结构的扁平化之后,就可以「回溯」到 node 节点。
为了能够将扁平化的链表插入node 与 node 的下一个节点之间,我们需要知道扁平化的链表的最后一个节点last,随后进行如下的三步操作:
将 node 与 node 的下一个节点 next 断开:
将node child 相连;
将last 与next 相连。
"""
# Definition for a Node.
class Node:
def __init__(self, val, prev, next, child):
self.val = val
self.prev = prev
self.next = next
self.child = child
"""
class Solution:
def flatten(self, head: 'Node') -> 'Node':
class Solution:
def flatten(self, head: "Node") -> "Node":
def dfs(node: "Node") -> "Node":
cur = node
# 记录链表的最后一个节点
last = None
while cur:
nxt = cur.next
# 如果有子节点,那么首先处理子节点
if cur.child:
child_last = dfs(cur.child)
nxt = cur.next
# 将 node 与 child 相连
cur.next = cur.child
cur.child.prev = cur
# 如果 nxt 不为空,就将 last 与 nxt 相连
if nxt:
child_last.next = nxt
nxt.prev = child_last
# 将 child 置为空
cur.child = None
last = child_last
else:
last = cur
cur = nxt
return last
dfs(head)
return head
- 迭代: 想的简单一点,遇到一个节点有 childchild 的
优先把 childchild 挪到该节点的 nextnext 上面 然后一口气找到这个 child 链的最末尾(不要管这条 child 链中间是否还有其他的节点存在 child 分支,先跳过,后面处理) 把该末尾节点跟前面开的口子接上 完成上面一轮, currcurr 指针向前挪动一步,继续处理以上三步,这样就能够把题目的意思给模拟出来,扁平化链表。
class Solution:
def flatten(self, head: "Node") -> "Node":
def dfs(node: "Node") -> "Node":
cur = node
# 记录链表的最后一个节点
last = None
while cur:
nxt = cur.next
# 如果有子节点,那么首先处理子节点
if cur.child:
child_last = dfs(cur.child)
nxt = cur.next
# 将 node 与 child 相连
cur.next = cur.child
cur.child.prev = cur
# 如果 nxt 不为空,就将 last 与 nxt 相连
if nxt:
child_last.next = nxt
nxt.prev = child_last
# 将 child 置为空
cur.child = None
last = child_last
else:
last = cur
cur = nxt
return last
dfs(head)
return head
- c++
/*
// Definition for a Node.
class Node {
public:
int val;
Node* prev;
Node* next;
Node* child;
};
*/
class Solution {
public:
Node* flatten(Node* head) {
Node * curr = head;
while(curr)
{
if (curr->child)
{
Node * nex = curr->next;
Node * child = curr->child;
// 开口子
curr->next = child;
child->prev = curr;
curr->child = nullptr;
while(child->next)
{
child = child->next;
}
// 接上去
child->next = nex;
if(nex)
{
nex->prev = child;
}
}
curr = curr->next;
}
return head;
}
};
复杂度分析
最好的情况下
- 时间复杂度:O(n)
- 空间复杂度:O(n)。上述代码中使用的空间为深度优先搜索中的栈空间,如果给定的链表的「深度」为 d,那么空间复杂度为 O(d)。在最换情况下,链表中的每个节点的 next 都为空,且除了最后一个节点外,每个节点的 child 都不为空,整个链表的深度为 nn,因此时间复杂度为 O(n)