zoukankan      html  css  js  c++  java
  • LeetCode Notes_#430_扁平化多级双向链表

    LeetCode Notes_#430_扁平化多级双向链表

    Contents

    题目

    多级双向链表中,除了指向下一个节点和前一个节点指针之外,它还有一个子链表指针,可能指向单独的双向链表。这些子列表也可能会有一个或多个自己的子项,依此类推,生成多级数据结构,如下面的示例所示。
    给你位于列表第一级的头节点,请你扁平化列表,使所有结点出现在单级双链表中。

    示例 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]
    解释:

    输入的多级列表如下图所示:

    扁平化后的链表如下图:

    提示:

    • 节点数目不超过 1000
    • 1 <= Node.val <= 10^5

    思路

    我们将列表顺时针转 90 °,那么就会看到一颗二叉树,则扁平化的操作也就是对二叉树进行先序遍历(深度优先搜索)。更准确的说,除了按照先序遍历的顺序访问,还需要注意的还需要按对节点进行双向连接。

    在图中,我们将child看作二叉树的left指针,将next指针看作二叉树中的right指针。

    递归函数Node flattenDFS(Node prev, Node cur)

    两个参数就是扁平化后的双向链表的相邻两个节点。
    返回值是tail节点,即每一个节点的最小的左孩子节点。

    终止条件

    cur == null,说明它的前一个节点prev就已经是最左下角的那个节点(即tail),直接返回prev

    递推过程

    1. prevnext之间建立双向连接
    2. 暂存cur.next节点到变量tmpNext中,因为cur.next在递归调用过程中会发生改变
    3. 递归调用Node tail = flattenDFS(cur, cur.child);,将链表看成是二叉树的话,相当于递归访问左子树的过程。
      • 将每对具有父子关系的节点建立双向连接。
      • 返回tail节点,即当前已经做完扁平化处理的链表的最后一个节点,这个节点要连接到tmpNext
    4. 删除cur.child指针,原因是cur/cur_child之间已经建立了双向连接,child指针就不需要了。
    5. 递归调用并返回值return flattenDFS(tail, tmpNext);,将链表看成是二叉树的话,相当于递归访问右子树的过程。
      • 左子树的最后一个节点是刚才记下的tail,将其连接到右子树的第一个节点tmpNext
      • 返回值是tmpNext的最左下角节点。

    解答

    class Solution {
        public Node flatten(Node head) {
            if(head == null) return head;
            //因为head没有prev指针,所以添加一个dummyHead,dummyHead.next = head,那么可以直接调用递归函数处理
            Node dummyHead = new Node(0, null, head, null);
            flattenDFS(dummyHead, head);
            //断开head.prev指针
            dummyHead.next.prev = null;
            return dummyHead.next;
        }
        //这里的prev/cur指的是在扁平列表中,需要建立双向连接的两个相邻节点,可能是父子关系,也可能本来就是相邻关系
        //分为cur有孩子,没孩子两种情况
        private Node flattenDFS(Node prev, Node cur){
            if(cur == null) return prev;
            cur.prev = prev;
            prev.next = cur;
            //cur.next可能会改变,所以要提前保存
            Node tmpNext = cur.next;
            //将child看作左子树,在cur和cur.child之间建立双向连接
            //tail的意思是,如果cur.child还有左孩子,那么会不断递归,直到最小的左孩子,记作tail
            //有孩子的情况下,tail是最小的孩子,没孩子的情况下,tail就是cur本身
            Node tail = flattenDFS(cur, cur.child);
            //已经建立了双向连接后,就不需要child指针了,将它断开
            cur.child = null;
            //有孩子的情况下,将最小的左孩子遍历完之后,之后连接第一个右孩子
            return flattenDFS(tail, tmpNext);
        }
    }

    复杂度分析

    时间复杂度:O(n),n是节点个数。
    空间复杂度:O(n),递归调用层数是n,则占用栈空间是O(n)。

  • 相关阅读:
    详解javascript的深拷贝与浅拷贝
    fis3+vue+pdf.js制作预览PDF文件或其他
    那一年,2020
    H5移动端IOS/Android兼容性总结,持续更新中…
    浏览器里点击复制到剪贴板的小方法
    elementUI upload 对图片的宽高做校验
    CSS实现核辐射警告标志
    github常用命令
    偶遇vue-awesome-swiper的坑
    gauge+python+vscode搭建自动化测试框架
  • 原文地址:https://www.cnblogs.com/Howfars/p/13579506.html
Copyright © 2011-2022 走看看