Article / 文章

LeetCode 第430题:扁平化多级双向链表

你会得到一个双向链表,其中包含的节点有一个下一个指针、一个前一个指针和一个额外的 子指针 。这个子指针可能指向一个单独的双向链表,也包含这些特殊的节点。这些子列表可能有一个或多个自己的子项,以此类推,形成多级数据结构。 给定链表的头节点 head ,将链表 扁平化 ,使所有节点都出现在单级双链表中。让 curr 是一个带有子列表的节点。子列表中的节点应该出现

📖 文章摘要

本文详细解析LeetCode第430题“扁平化多级双向链表”,这是一道考察链表操作和递归的问题。文章提供了基于DFS的解题思路,包含C#、Python、C++三种语言实现,配有详细的分析和性能对比。适合想要提升链表操作和递归思维能力的程序员。

核心知识点: 双向链表、递归、DFS
难度等级: 中等
推荐人群: 具有基础数据结构知识的程序员

题目描述

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

给定链表的头节点 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]
解释:输入的多级列表如下图所示:
  1---2---NULL
  |
  3---NULL

示例 3:

输入:head = []
输出:[]

提示

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

解题思路

本题可以使用深度优先搜索(DFS)来解决,主要步骤如下:

  1. 遍历主链表:

    • 记录当前节点
    • 保存next指针
  2. 处理子链表:

    • 递归处理子链表
    • 将子链表与主链表连接
  3. 恢复连接:

    • 连接子链表的最后一个节点与原next节点
    • 清除child指针

图解思路

扁平化过程分析

步骤 操作 结果 说明
初始状态 - 多级链表 原始链表结构
遍历主链表 找到带子链表的节点 定位子链表 准备处理子链表
处理子链表 递归扁平化 扁平子链表 子链表变为单层
连接操作 合并链表 完整单层链表 最终扁平结构

指针操作示意

阶段 prev指针 next指针 child指针 说明
开始 正常连接 正常连接 指向子链表 初始状态
子链表处理 更新连接 保存原next 处理子链表 处理中间状态
完成 全部连接 全部连接 置为null 最终状态

代码实现

C# 实现

public class Solution {
    public Node Flatten(Node head) {
        if (head == null) return null;
        
        // 使用DFS扁平化链表
        FlattenDFS(head);
        return head;
    }
    
    private Node FlattenDFS(Node node) {
        if (node == null) return null;
        
        // 保存当前节点的next
        Node next = node.next;
        
        // 如果有子链表,先处理子链表
        if (node.child != null) {
            Node child = node.child;
            
            // 连接当前节点和子链表
            node.next = child;
            child.prev = node;
            node.child = null;
            
            // 获取扁平化后的子链表的最后一个节点
            Node last = FlattenDFS(child);
            
            // 如果原来的next不为空,连接它
            if (next != null) {
                last.next = next;
                next.prev = last;
            }
            
            // 返回处理后的最后一个节点
            return next == null ? last : FlattenDFS(next);
        }
        
        // 如果没有子链表,继续处理next
        return next == null ? node : FlattenDFS(next);
    }
}

Python 实现

class Solution:
    def flatten(self, head: 'Node') -> 'Node':
        if not head:
            return None
        
        # 扁平化函数
        def flatten_dfs(node):
            if not node:
                return None
            
            # 保存当前节点的next
            next_node = node.next
            
            # 如果有子链表,先处理子链表
            if node.child:
                child = node.child
                
                # 连接当前节点和子链表
                node.next = child
                child.prev = node
                node.child = None
                
                # 获取扁平化后的子链表的最后一个节点
                last = flatten_dfs(child)
                
                # 如果原来的next不为空,连接它
                if next_node:
                    last.next = next_node
                    next_node.prev = last
                
                # 返回处理后的最后一个节点
                return flatten_dfs(next_node) if next_node else last
            
            # 如果没有子链表,继续处理next
            return flatten_dfs(next_node) if next_node else node
        
        flatten_dfs(head)
        return head

C++ 实现

class Solution {
public:
    Node* flatten(Node* head) {
        if (!head) return nullptr;
        
        // 使用DFS扁平化链表
        flattenDFS(head);
        return head;
    }
    
private:
    Node* flattenDFS(Node* node) {
        if (!node) return nullptr;
        
        // 保存当前节点的next
        Node* next = node->next;
        
        // 如果有子链表,先处理子链表
        if (node->child) {
            Node* child = node->child;
            
            // 连接当前节点和子链表
            node->next = child;
            child->prev = node;
            node->child = nullptr;
            
            // 获取扁平化后的子链表的最后一个节点
            Node* last = flattenDFS(child);
            
            // 如果原来的next不为空,连接它
            if (next) {
                last->next = next;
                next->prev = last;
            }
            
            // 返回处理后的最后一个节点
            return next ? flattenDFS(next) : last;
        }
        
        // 如果没有子链表,继续处理next
        return next ? flattenDFS(next) : node;
    }
};

执行结果

C# 实现

  • 执行用时:88 ms
  • 内存消耗:37.2 MB

Python 实现

  • 执行用时:36 ms
  • 内存消耗:15.1 MB

C++ 实现

  • 执行用时:4 ms
  • 内存消耗:7.4 MB

性能对比

语言 执行用时 内存消耗 特点
C# 88 ms 37.2 MB 实现清晰但性能较差
Python 36 ms 15.1 MB 代码简洁,性能中等
C++ 4 ms 7.4 MB 性能最优

代码亮点

  1. 🎯 使用DFS优雅处理多级结构
  2. 💡 巧妙保存和恢复链表连接
  3. 🔍 高效的指针操作
  4. 🎨 代码结构清晰,易于维护

常见错误分析

  1. 🚫 未正确处理空节点
  2. 🚫 忘记清除child指针
  3. 🚫 指针连接顺序错误
  4. 🚫 递归返回值处理不当

解法对比

解法 时间复杂度 空间复杂度 优点 缺点
DFS递归 O(n) O(n) 实现简单,直观 递归栈开销
迭代 O(n) O(1) 空间复杂度低 实现复杂

相关题目


📖 系列导航

🔥 算法专题合集 - 查看完整合集

📢 关注合集更新:点击上方合集链接,关注获取最新题解!目前已更新第430题。


💬 互动交流

感谢大家耐心阅读到这里!希望这篇题解能够帮助你更好地理解和掌握这道算法题。

如果这篇文章对你有帮助,请:

  • 👍 点个赞,让更多人看到这篇文章
  • 📁 收藏文章,方便后续查阅复习
  • 🔔 关注作者,获取更多高质量算法题解
  • 💭 评论区留言,分享你的解题思路或提出疑问

你的支持是我持续分享的动力!

💡 一起进步:算法学习路上不孤单,欢迎一起交流学习!