扁平化多级双向链表
难度:
标签:
题目描述
你会得到一个双链表,其中包含的节点有一个下一个指针、一个前一个指针和一个额外的 子指针 。这个子指针可能指向一个单独的双向链表,也包含这些特殊的节点。这些子列表可以有一个或多个自己的子列表,以此类推,以生成如下面的示例所示的 多层数据结构 。
给定链表的头节点 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] 解释:输入的多级列表如上图所示。 扁平化后的链表如下图:![]()
示例 2:
输入:head = [1,2,null,3] 输出:[1,3,2] 解释:输入的多级列表如上图所示。 扁平化后的链表如下图:![]()
示例 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]
代码结果
运行时间: 32 ms, 内存: 16.1 MB
/*
* Problem: Flatten a multilevel doubly linked list using Java Streams.
*
* Approach:
* 1. Convert the linked list into a list of nodes, including child nodes.
* 2. Flatten the list of nodes into a single list using a stream.
* 3. Reconstruct the linked list from the flattened list.
*/
import java.util.*;
import java.util.stream.*;
class Node {
int val;
Node next;
Node prev;
Node child;
Node(int val) {
this.val = val;
}
}
public class Solution {
public Node flatten(Node head) {
if (head == null) return null;
// Create a list of all nodes, including child nodes
List<Node> nodes = new ArrayList<>();
flattenList(head, nodes);
// Flatten the list using streams and set the child pointers to null
List<Node> flattenedList = nodes.stream()
.flatMap(n -> n.child == null ? Stream.of(n) : Stream.concat(Stream.of(n), flattenChild(n.child).stream()))
.peek(n -> n.child = null)
.collect(Collectors.toList());
// Reconstruct the linked list
Node dummy = new Node(0);
Node curr = dummy;
for (Node node : flattenedList) {
curr.next = node;
node.prev = curr;
curr = curr.next;
}
dummy.next.prev = null;
return dummy.next;
}
private void flattenList(Node head, List<Node> nodes) {
Node curr = head;
while (curr != null) {
nodes.add(curr);
if (curr.child != null) flattenList(curr.child, nodes);
curr = curr.next;
}
}
private List<Node> flattenChild(Node child) {
List<Node> childList = new ArrayList<>();
flattenList(child, childList);
return childList;
}
}
解释
方法:
该题解采用了迭代的方式来处理多级双向链表的扁平化。首先,创建一个虚拟头节点 `dummy1` 来简化边界条件的处理,并使得返回扁平化链表时更为方便。然后,使用一个指针 `cur` 从虚拟头节点开始遍历原始链表。对于每个节点,检查是否存在子节点:
1. 如果当前节点 `cur` 有子节点,将其子节点插入到 `cur` 和 `cur.next` 之间,并更新相应的指针。
2. 如果 `cur` 没有子节点,则直接移动到下一个节点。
3. 在将子链表插入后,原始的 `cur.next` 节点(如果存在的话)会被暂存起来,并在处理完所有子链表后,接在当前链表的末尾。
这种方法确保了所有子链表被逐一插入到正确的位置,最终实现整个链表的扁平化。
时间复杂度:
O(n)
空间复杂度:
O(1)
代码细节讲解
🦆
在处理子链表时,你是如何确保所有的子链表都能正确地插入到它们相应的父节点之后的?
▷🦆
对于多层子链表的情况,你的算法是否能够递归地扁平化所有子链表,直到没有更多的子链表存在?
▷🦆
在重新连接原始的 `cur.next` 链表时,你如何处理可能出现的循环引用问题?
▷相关问题
二叉树展开为链表
给你二叉树的根结点 root
,请你将它展开为一个单链表:
- 展开后的单链表应该同样使用
TreeNode
,其中right
子指针指向链表中下一个结点,而左子指针始终为null
。 - 展开后的单链表应该与二叉树 先序遍历 顺序相同。
示例 1:

输入:root = [1,2,5,3,4,null,6] 输出:[1,null,2,null,3,null,4,null,5,null,6]
示例 2:
输入:root = [] 输出:[]
示例 3:
输入:root = [0] 输出:[0]
提示:
- 树中结点数在范围
[0, 2000]
内 -100 <= Node.val <= 100
进阶:你可以使用原地算法(O(1)
额外空间)展开这棵树吗?