0430-扁平化多级双向链表

Raphael Liu Lv10

你会得到一个双链表,其中包含的节点有一个下一个指针、一个前一个指针和一个额外的 子指针
。这个子指针可能指向一个单独的双向链表,也包含这些特殊的节点。这些子列表可以有一个或多个自己的子列表,以此类推,以生成如下面的示例所示的
多层数据结构

给定链表的头节点 head ,将链表 扁平化 ,以便所有节点都出现在单层双链表中。让 curr 是一个带有子列表的节点。子列表中的节点应该出现在
扁平化列表 中的 curr 之后curr.next 之前

返回 扁平列表的head 。列表中的节点必须将其 所有 子指针设置为 null

示例 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]
**解释:** 输入的多级列表如上图所示。
扁平化后的链表如下图:
![](https://assets.leetcode.com/uploads/2021/11/09/flatten12.jpg)

示例 2:

**输入:** head = [1,2,null,3]
**输出:** [1,3,2]
**解释:** 输入的多级列表如上图所示。
扁平化后的链表如下图:
![](https://assets.leetcode.com/uploads/2021/11/24/list.jpg)

示例 3:

**输入:** head = []
**输出:** []
**说明:** 输入中可能存在空列表。

提示:

  • 节点数目不超过 1000
  • 1 <= Node.val <= 105

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

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

方法一:深度优先搜索

思路与算法

当我们遍历到某个节点 node 时,如果它的 child 成员不为空,那么我们需要将 child 指向的链表结构进行扁平化,并且插入 node 与 node 的下一个节点之间。

因此,我们在遇到 child 成员不为空的节点时,就要先去处理 child 指向的链表结构,这就是一个「深度优先搜索」的过程。当我们完成了对 child 指向的链表结构的扁平化之后,就可以「回溯」到 node 节点。

为了能够将扁平化的链表插入 node 与 node 的下一个节点之间,我们需要知道扁平化的链表的最后一个节点 last,随后进行如下的三步操作:

  • 将 node 与 node 的下一个节点 next 断开:

  • 将 node 与 child 相连;

  • 将 last 与 next 相连。

这样一来,我们就可以将扁平化的链表成功地插入。

fig1

在深度优先搜索完成后,我们返回给定的首节点即可。

细节

需要注意的是,node 可能没有下一个节点,即 next 为空。此时,我们只需进行第二步操作。

此外,在插入扁平化的链表后,我们需要将 node 的 child 成员置为空。

代码

[sol1-C++]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
class Solution {
public:
Node* flatten(Node* head) {
function<Node*(Node*)> dfs = [&](Node* node) {
Node* cur = node;
// 记录链表的最后一个节点
Node* last = nullptr;

while (cur) {
Node* next = cur->next;
// 如果有子节点,那么首先处理子节点
if (cur->child) {
Node* child_last = dfs(cur->child);

next = cur->next;
// 将 node 与 child 相连
cur->next = cur->child;
cur->child->prev = cur;

// 如果 next 不为空,就将 last 与 next 相连
if (next) {
child_last->next = next;
next->prev = child_last;
}

// 将 child 置为空
cur->child = nullptr;
last = child_last;
}
else {
last = cur;
}
cur = next;
}
return last;
};

dfs(head);
return head;
}
};
[sol1-Java]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
class Solution {
public Node flatten(Node head) {
dfs(head);
return head;
}

public Node dfs(Node node) {
Node cur = node;
// 记录链表的最后一个节点
Node last = null;

while (cur != null) {
Node next = cur.next;
// 如果有子节点,那么首先处理子节点
if (cur.child != null) {
Node childLast = dfs(cur.child);

next = cur.next;
// 将 node 与 child 相连
cur.next = cur.child;
cur.child.prev = cur;

// 如果 next 不为空,就将 last 与 next 相连
if (next != null) {
childLast.next = next;
next.prev = childLast;
}

// 将 child 置为空
cur.child = null;
last = childLast;
} else {
last = cur;
}
cur = next;
}
return last;
}
}
[sol1-C#]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
public class Solution {
public Node Flatten(Node head) {
DFS(head);
return head;
}

public Node DFS(Node node) {
Node cur = node;
// 记录链表的最后一个节点
Node last = null;

while (cur != null) {
Node next = cur.next;
// 如果有子节点,那么首先处理子节点
if (cur.child != null) {
Node childLast = DFS(cur.child);

next = cur.next;
// 将 node 与 child 相连
cur.next = cur.child;
cur.child.prev = cur;

// 如果 next 不为空,就将 last 与 next 相连
if (next != null) {
childLast.next = next;
next.prev = childLast;
}

// 将 child 置为空
cur.child = null;
last = childLast;
} else {
last = cur;
}
cur = next;
}
return last;
}
}
[sol1-Python3]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
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
[sol1-JavaScript]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
var flatten = function(head) {
const dfs = (node) => {
let cur = node;
// 记录链表的最后一个节点
let last = null;

while (cur) {
let next = cur.next;
// 如果有子节点,那么首先处理子节点
if (cur.child) {
const childLast = dfs(cur.child);

next = cur.next;
// 将 node 与 child 相连
cur.next = cur.child;
cur.child.prev = cur;

// 如果 next 不为空,就将 last 与 next 相连
if (next != null) {
childLast.next = next;
next.prev = childLast;
}

// 将 child 置为空
cur.child = null;
last = childLast;
} else {
last = cur;
}
cur = next;

}
return last;
}

dfs(head);
return head;
};
[sol1-Golang]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
func dfs(node *Node) (last *Node) {
cur := node
for cur != nil {
next := cur.Next
// 如果有子节点,那么首先处理子节点
if cur.Child != nil {
childLast := dfs(cur.Child)

next = cur.Next
// 将 node 与 child 相连
cur.Next = cur.Child
cur.Child.Prev = cur

// 如果 next 不为空,就将 last 与 next 相连
if next != nil {
childLast.Next = next
next.Prev = childLast
}

// 将 child 置为空
cur.Child = nil
last = childLast
} else {
last = cur
}
cur = next
}
return
}

func flatten(root *Node) *Node {
dfs(root)
return root
}

复杂度分析

  • 时间复杂度:$O(n)$,其中 $n$ 是链表中的节点个数。

  • 空间复杂度:$O(n)$。上述代码中使用的空间为深度优先搜索中的栈空间,如果给定的链表的「深度」为 $d$,那么空间复杂度为 $O(d)$。在最换情况下,链表中的每个节点的 next 都为空,且除了最后一个节点外,每个节点的 child 都不为空,整个链表的深度为 $n$,因此时间复杂度为 $O(n)$。

 Comments
On this page
0430-扁平化多级双向链表