LeetCode 430. 扁平化多级双向链表
题目描述




题意分析
将多级双向链表改成单层双向链表。当前节点有子链时,要把整条子链及其更深层节点插在当前节点与原后继之间;结果中的所有
child都为空,next和prev必须互相对应。
解法:栈模拟 DFS
核心思路
[!blue]
要求的顺序是「当前节点、完整子链、原后继」,也就是子链优先的深度优先遍历。进入子链时,原后继暂时不能访问,但它还要在子链结束后接回来,因此用栈保存这些待续后继。
遇到
child时,先把非空的原next入栈,再让cur.next指向子链头,同时把子链头的prev指回cur,最后清空cur.child。这样下一步沿next前进就会进入子链,原后继也不会丢失。如果当前节点既没有子链,也没有后继,说明当前分支已走完。这时从栈顶取出最近保存的后继,成对连接
cur.next和后继的prev,再继续遍历。必须后进先出,因为内层子链结束后,应先恢复最近一层被搁置的部分,之后才轮到更外层。每轮开始时,
cur之前的节点已经按要求排好,所有待续部分都保存在栈中。当前轮只确定下一条正确连接,再沿修改后的next前进,因此既不会漏掉原后继,也不会提前跳过子链。最终走到空指针时,栈也已清空,所有节点都已接成单链,头节点保持不变。
解题步骤
- 空链表直接返回空;否则令
cur指向头节点,创建空栈。- 当前有子链时,先保存非空原后继,再接入子链头,修复反向链接并清空
child。- 当前没有子链且
next为空时,若栈非空,就弹出最近的待续后继并接回。- 沿更新后的
next前进,直到cur为空,返回原头节点。
代码实现
// 继续向前走,若链表走到尽头,则从栈中弹出之前保存的 next 接回。
class Solution {
public Node flatten(Node head) {
if (head == null) {
return null;
}
Deque<Node> stack = new ArrayDeque<>();
Node cur = head;
while (cur != null) {
if (cur.child != null) {
if (cur.next != null) {
// 覆盖下一跳之前,先保存稍后需要恢复的原后继
stack.push(cur.next);
}
Node child = cur.child;
// 双向连接成对修改,子指针随后清空
cur.next = child;
child.prev = cur;
cur.child = null;
} else if (cur.next == null && !stack.isEmpty()) {
// 只在当前分支末尾恢复最近的待续后继
Node next = stack.pop();
cur.next = next;
next.prev = cur;
}
cur = cur.next;
}
return head;
}
}
// 继续向前走,若链表走到尽头,则从栈中弹出之前保存的 next 接回。
func flatten(root *Node) *Node {
if root == nil {
return nil
}
stack := make([]*Node, 0)
cur := root
for cur != nil {
if cur.Child != nil {
if cur.Next != nil {
// 覆盖下一跳之前,先保存稍后需要恢复的原后继
stack = append(stack, cur.Next)
}
child := cur.Child
// 双向连接成对修改,子指针随后清空
cur.Next = child
child.Prev = cur
cur.Child = nil
} else if cur.Next == nil && len(stack) > 0 {
// 只在当前分支末尾恢复最近的待续后继
next := stack[len(stack)-1]
stack = stack[:len(stack)-1]
cur.Next = next
next.Prev = cur
}
cur = cur.Next
}
return root
}
复杂度分析
- 时间复杂度:$O(n)$,每个节点访问一次,待续后继至多入栈、出栈各一次。
- 空间复杂度:$O(n)$,最坏情况下,栈保存线性数量的待续后继。
关键点总结
[!green]
- 栈中保存待恢复的后继,不是已经处理的所有节点。
- 连接边时同步修改
next与prev。- 后进先出保证先完成内层子链,再恢复外层后继。
- 每次都沿修改后的
next前进,整个过程直接在原节点上完成。
易错点总结
[!yellow]
- 覆盖
next之后才保存,会把原后继丢掉。- 尚未走到分支末尾就弹栈,会截断子链。
- 只连接正向指针或不清
child,结果仍不符合单层双向结构。
相似题目
| 题目 | 难度 | 关联与区别 |
|---|---|---|
| 114. 二叉树展开为链表 | 中等 | 同样把树形层级结构改成线性链,本题还需维护prev并将child清空。 |
| 341. 扁平化嵌套列表迭代器 | 中等 | 同样按深度优先顺序展开嵌套结构,原题只提供迭代访问,本题实际修改链表指针。 |
转载与许可
许可
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处!