leetcode
leetcode 2901 ~ 2950
扁平化多级双向链表

扁平化多级双向链表

难度:

标签:

题目描述

English description is not available for the problem. Please switch to Chinese.

代码结果

运行时间: 25 ms, 内存: 16.7 MB


// Java Stream Solution
// 思路:
// 使用递归和流的思想,将每一个节点及其子节点展平成列表。

import java.util.ArrayList;
import java.util.List;

class Node {
    public int val;
    public Node prev;
    public Node next;
    public Node child;

    public Node(int val) {
        this.val = val;
        this.prev = null;
        this.next = null;
        this.child = null;
    }
}

public class Solution {
    public Node flatten(Node head) {
        List<Node> nodeList = new ArrayList<>();
        flattenToList(head, nodeList);
        Node pseudoHead = new Node(0);
        Node curr = pseudoHead;
        for (Node node : nodeList) {
            curr.next = node;
            node.prev = curr;
            curr = curr.next;
            curr.child = null;
        }
        pseudoHead.next.prev = null;
        return pseudoHead.next;
    }

    private void flattenToList(Node head, List<Node> nodeList) {
        if (head == null) return;
        nodeList.add(head);
        flattenToList(head.child, nodeList);
        flattenToList(head.next, nodeList);
    }
}

解释

方法:

题解采用的是深度优先遍历(DFS)的迭代实现,利用栈(在代码中是stash列表)来管理还未完全扁平化的节点。首先创建一个虚拟头节点dummy来简化边界条件处理,然后使用一个循环遍历所有节点。对于每个节点,如果它具有子节点,将其子节点插入到当前节点的下一个位置,并将原来的next节点压入栈中。如果当前节点没有next节点,且栈中还有节点,从栈中弹出一个节点作为next节点。继续这个过程,直到所有节点都被遍历完,从而实现链表的完全扁平化。

时间复杂度:

O(n)

空间复杂度:

O(n)

代码细节讲解

🦆
在处理多级双向链表的扁平化时,为什么选择使用栈(DFS)而不是队列(BFS)?
在扁平化多级双向链表时,使用栈(DFS)可以更自然地维护链表的顺序。DFS策略优先深入到最内层的子链表并完成其扁平化,这符合多级链表扁平化的直观顺序,即优先处理当前节点的子节点直到其结束,然后再回到上一级节点的next节点。这种方式确保了处理流程的连续性和顺序性,使得扁平化后的链表能够保持正确的顺序。相比之下,使用队列(BFS)会导致先广泛地处理同一层的所有节点,这不符合直观的顺序要求,可能需要额外的操作来重新调整节点的顺序,从而增加了复杂度。
🦆
在将子节点连接为当前节点的下一个节点时,原本的next节点是如何处理确保不丢失的?
在将子节点接入为当前节点的下一个节点时,题解中首先检查当前节点是否有next节点。如果存在,这个原本的next节点会被保存到栈中。这样做确保了在子节点和它的所有后续节点被完全处理并扁平化后,可以从栈中恢复这个next节点,接在已扁平化部分的末尾。这种方法保证了原本的next节点不会丢失,并且可以在适当的时候重新链接到链表的正确位置。
🦆
代码中使用了虚拟头节点dummy,这种做法的主要优点是什么?
使用虚拟头节点(dummy node)主要优点是简化了链表操作中的边界条件处理。通过引入一个虚拟的头节点,可以避免在处理原始链表头部是空的情况下的特殊判断,同时在链表前端的操作中不需要额外的条件来处理头节点可能变化的情况。虚拟头节点作为一个稳定的前置节点,确保了无论如何操作,返回的始终是dummy.next,从而简化了代码逻辑并提高了代码的可读性和健壮性。

相关问题