【算法】430.扁平化多级双向链表--通俗讲解

通俗易懂讲解"扁平化多级双向链表"算法题目

一、题目是啥?一句话说清

给定一个多级双向链表,其中节点可能包含指向子链表的指针,要求将所有层级的节点扁平化到同一层级,保持原有顺序。

示例:

  • 输入:1 ↔ 2 ↔ 3 ↔ 4

    5 ↔ 6 ↔ 7

    8 ↔ 9
  • 输出:1 ↔ 2 ↔ 5 ↔ 6 ↔ 8 ↔ 9 ↔ 7 ↔ 3 ↔ 4

二、解题核心

使用深度优先搜索(DFS),当遇到有子节点的节点时,先处理子链表,将子链表插入到当前节点和下一个节点之间,然后继续处理。

这就像整理一个多层文件夹结构,把子文件夹里的文件都拿出来,按顺序放在主文件夹中。

三、关键在哪里?(3个核心点)

想理解并解决这道题,必须抓住以下三个关键点:

1. 深度优先遍历

  • 是什么:当遇到有子节点的节点时,先深入处理子链表,再继续处理主链表。
  • 为什么重要:这确保了子链表中的节点能够正确地插入到主链表中,保持深度优先的顺序。

2. 指针的正确连接

  • 是什么:在插入子链表时,需要正确调整当前节点、子链表头、子链表尾和下一个节点之间的前后指针。
  • 为什么重要:如果指针连接错误,会导致链表断裂或形成环,破坏链表结构。

3. 子指针的处理

  • 是什么:处理完子链表后,需要将节点的子指针设置为null。
  • 为什么重要:这是题目要求的一部分,确保扁平化后的链表所有子指针都为null。

四、看图理解流程(通俗理解版本)

假设多级链表如下:

复制代码
1 ↔ 2 ↔ 3 ↔ 4
     │
     5 ↔ 6 ↔ 7
          │
          8 ↔ 9
  1. 遍历到节点2

    • 发现节点2有子节点5
    • 先处理子链表:5 ↔ 6 ↔ 7(其中6有子节点8)
  2. 处理子链表5 ↔ 6 ↔ 7

    • 遍历到节点6,发现它有子节点8
    • 先处理子子链表:8 ↔ 9
    • 将8 ↔ 9插入到6和7之间:6 ↔ 8 ↔ 9 ↔ 7
    • 子链表变为:5 ↔ 6 ↔ 8 ↔ 9 ↔ 7
  3. 将子链表插入主链表

    • 将5 ↔ 6 ↔ 8 ↔ 9 ↔ 7插入到2和3之间
    • 主链表变为:1 ↔ 2 ↔ 5 ↔ 6 ↔ 8 ↔ 9 ↔ 7 ↔ 3 ↔ 4
  4. 设置子指针为null

    • 将节点2和节点6的子指针设置为null

五、C++ 代码实现(附详细注释)

cpp 复制代码
#include <iostream>
using namespace std;

// 多级双向链表节点定义
class Node {
public:
    int val;
    Node* prev;
    Node* next;
    Node* child;

    Node(int _val) : val(_val), prev(nullptr), next(nullptr), child(nullptr) {}
};

class Solution {
public:
    Node* flatten(Node* head) {
        if (head == nullptr) return nullptr;
        
        Node* current = head;
        while (current != nullptr) {
            // 如果当前节点有子节点
            if (current->child != nullptr) {
                // 保存当前节点的下一个节点
                Node* next = current->next;
                
                // 递归扁平化子链表
                Node* childHead = flatten(current->child);
                current->child = nullptr; // 将子指针设为null
                
                // 将当前节点与子链表头连接
                current->next = childHead;
                childHead->prev = current;
                
                // 找到子链表的尾节点
                Node* childTail = childHead;
                while (childTail->next != nullptr) {
                    childTail = childTail->next;
                }
                
                // 将子链表尾与下一个节点连接
                if (next != nullptr) {
                    childTail->next = next;
                    next->prev = childTail;
                }
                
                // 移动到子链表处理后的下一个节点
                current = next;
            } else {
                // 没有子节点,继续遍历
                current = current->next;
            }
        }
        return head;
    }
};

// 辅助函数:打印双向链表
void printList(Node* head) {
    Node* current = head;
    while (current != nullptr) {
        cout << current->val << " ";
        current = current->next;
    }
    cout << endl;
}

// 测试代码
int main() {
    // 构建示例多级链表
    Node* head = new Node(1);
    head->next = new Node(2);
    head->next->prev = head;
    head->next->next = new Node(3);
    head->next->next->prev = head->next;
    head->next->next->next = new Node(4);
    head->next->next->next->prev = head->next->next;
    
    // 创建子链表 5->6->7
    head->next->child = new Node(5);
    head->next->child->next = new Node(6);
    head->next->child->next->prev = head->next->child;
    head->next->child->next->next = new Node(7);
    head->next->child->next->next->prev = head->next->child->next;
    
    // 创建子子链表 8->9
    head->next->child->next->child = new Node(8);
    head->next->child->next->child->next = new Node(9);
    head->next->child->next->child->next->prev = head->next->child->next->child;

    Solution solution;
    Node* result = solution.flatten(head);
    
    printList(result); // 输出:1 2 5 6 8 9 7 3 4
    
    // 释放内存(简单示例)
    // 注意:由于链表已经扁平化,需要小心释放以避免重复释放
    // 这里为了简单,不完整释放
    return 0;
}

六、时间空间复杂度

  • 时间复杂度:O(n),其中n是所有节点的总数(包括所有层级的节点)。每个节点被访问一次。
  • 空间复杂度:O(d),其中d是链表的最大深度。这是由于递归调用栈的空间。最坏情况下,如果链表是一条链状的多级结构,d可能等于n。

七、注意事项

  • 递归深度:对于深度很大的多级链表,递归可能导致栈溢出。可以考虑使用迭代方法(如栈)来避免递归。
  • 指针连接顺序:在插入子链表时,要确保正确连接所有指针,包括prev和next指针。
  • 子指针处理:处理完子链表后,一定要将子指针设置为null,满足题目要求。
  • 边界条件:处理空链表、只有一层链表、子链表为空等情况。
  • 内存管理:在C++中,如果需要释放内存,要注意链表已经扁平化,避免重复释放或遗漏释放。
相关推荐
LDG_AGI2 分钟前
【机器学习】深度学习推荐系统(二十六):X 推荐算法多模型融合机制详解
人工智能·分布式·深度学习·算法·机器学习·推荐算法
高山上有一只小老虎3 分钟前
小红的矩阵染色
java·算法·矩阵
WuChao_JMUer3 分钟前
YOLO26 on RDK S100P 端侧部署技术报告
人工智能·算法·yolo·rdk
Ro Jace4 分钟前
传统雷达信号分选方法之SDIF:Improved algorithm for the deinterleaving of radar pulses
网络·人工智能·算法
小杨同学496 分钟前
【嵌入式 C 语言实战】手动实现字符串四大核心函数(strcpy/strcat/strlen/strcmp)
后端·深度学习·算法
Gofarlic_OMS11 分钟前
MATLAB许可证闲置自动检测与智能提醒
java·大数据·运维·开发语言·人工智能·算法·matlab
工业甲酰苯胺14 分钟前
推荐算法闲谈:如何在不同业务场景下理解和拆解核心指标
算法·机器学习·推荐算法
平生不喜凡桃李15 分钟前
LeetCode: 基本计算器详解
算法·leetcode·计算器·逆波兰表达式
haing201918 分钟前
卡尔曼滤波(Kalman Filter)原理
线性代数·算法·机器学习
Dev7z19 分钟前
基于深度学习的泳池溺水行为检测算法设计
人工智能·深度学习·算法