[链表]leetcode430-扁平化多级双向链表-递归和迭代

[链表]–扁平化多级双向链表


题目链接

leetcode 430.扁平化多级双向链表

题目

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

示例

输入链表:
在这里插入图片描述
输出:
在这里插入图片描述

输入:head = [1,2,3,4,5,6,null,null,null,7,8,9,10,null,null,11,12]
也就是这样的: 1---2---3---4---5---6--NULL
      								  |
         							 7---8---9---10--NULL
          								   |
            							 11--12--NULL
输出:[1,2,3,7,8,11,12,9,10,4,5,6]

解析

每一层有 child 的节点需要先连接 child,遍历完了再继续遍历连接上一层,这很递归!
0. 递归终止:咱没有 child 就终止了;

  1. 假设这有 head 的是第一级,chld 是第二级,依次向下。首先肯定是遍历链表了;
  2. 如果说没有 child,那就继续向同一级后面走;有的话就需要向下一级走了;
  3. 上一步说到这个存在 child 的当前节点 cur 需要向下走了。接着来:
    (1)首先为了防止走丢,先把 cur.next 保存起来(curNext=cur.next);接下来就大胆向下走
    (2)万一这 child 还有 child?这还得继续向下走啊!哟典型递归。递什么?有孩子节点就向下递;归呢?自底向上每次遍历完了整个子链表就归。需要注意的是 child 遍历过了(作为了“父节点”的 next )就置为 null,不然这个节点还存在就不是双向链表了;
    (3)说回来:归的时候就需要连接了,将链表的尾结点和上一级的 curNext 连接起来,然后依次向上归,直到遍历完第一级的链表。还有个注意的:假如说原本的的 curNext是 null,这一层就不需要连接了,直接继续向上。
  4. 递归不好理解啊,所以头向左一偏,瞅一眼这示例图:有点二叉树的味了!那就试试!

代码实现

public class Solution430 {

    /**
     * Definition for a node
     */
    class Node {
        int val;
        Node prev;
        Node next;
        Node child;
    }

    /**
     * 将 head 所在一级视为第一级, 子链表所在一级看作下一级, 依次向下; 将有 child 的结点看作 "父结点"————递归
     * 1.没有 child 的结点不需要改变, 向后遍历即可, 有 child 的 "父结点" 去遍历 child 这一子链表
     * 2.递归进行上一操作, 由上而下, 也就是有 child 就先去遍历子链表, 直到最后一个子链表被遍历
     * 3.递归到最后一层子链表, 将末尾结点的 next 指向上一级 "父结点" 的下一个连接起来,
     *              !!注意需要修改 "父结点" 的 child = null(否则不是一个双向链表, 因为还存在 child)
     * 4.由下而上, 将每一级的子链表的最后一个连接到响应位置, 直到回到第一级链表遍历完
     */
    public Node flatten(Node head) {
        if (head == null) return null;
        Node cur = head;
        while (cur != null) {
            // 没有 child 结点就保持原有连接
            if (cur.child == null) {
                cur = cur.next;
            }else {
                // 有 child 结点
                // 保存有 child 结点这一级的 next -> 连接子链表的最后一个结点
                Node curNext = cur.next;
                // 递归遍历所有的子链表, 并把子链表连接在子链表 "父结点"(上一级)后面
                Node child = flatten(cur.child);
                cur.next = child;
                child.prev = cur;
                cur.child = null;
                // 向后遍历,直到遍历完当前这一级
                while (cur.next != null) {
                    cur = cur.next;
                }
                // 将子链表最后一个结点连接在 curNext 之前
                if (curNext != null) {
                    cur.next = curNext;
                    curNext.prev = cur;
                }
            }
        }
        return head;
    }
}

说到整体图看起来像二叉树,那就可以把 child 看作左子树,next 看作右子树,然后这结果不是前序遍历嘛
用栈!

  1. 头节点不为 null 入栈,为了更好连接:定义一个 dummyHead 是傀儡头结点,定义一个 prev 作为弹出节点的前驱节点;
  2. 弹出栈顶节点 temp 进行连接,prev.next = temp;temp.prev = prev;判断弹出节点的左右子树(child 和 next)不为空就压栈(先右后左压栈)。右子树(child)压栈需要置为 null;
  3. prev 再次作为弹出节点的前驱节点用于连接;继续遍历直至栈为空;
  4. 最后头节点的前驱置为 null。

代码实现

public class Solution430 {

    /**
     * Definition for a node
     */
    class Node {
        int val;
        Node prev;
        Node next;
        Node child;
    }

    /**
     * 将多级双向链表看作是二叉树, head 就是根节点, 前序遍历即可————迭代
     * 1.定义 prev 结点用于记录当前弹出结点 cur 的前驱结点
     * 2.判断 cur 的 next 和 child 是否为空, 不为空入栈
     * 3.child 结点入栈后需要将其"父结点"的 child 修改为 null
     */
    public Node flatten(Node head) {
        if (head == null) return null;

        // 傀儡头结点
        Node dummyHead = new Node();
        // 记录遍历结点的前驱结点 -> 为了方便进行结点之间的连接
        Node prev = dummyHead;
        Stack<Node> stack = new Stack<>();
        stack.push(head);
        while (!stack.isEmpty()) {
            // 弹出栈顶结点, 同时连接它和它的前驱结点
            Node temp = stack.pop();
            prev.next = temp;
            temp.prev = prev;
            // next 不为空入栈————相当于右子树
            if (temp.next != null) {
                stack.push(temp.next);
            }
            // child 不为空入栈————相当于左子树
            // 注意需要将 child 置为 null, 否则不是一个双向链表, 还存在 child
            if (temp.child != null) {
                stack.push(temp.child);
                temp.child = null;
            }
            // prev 跟着 temp 移动
            prev = temp;
        }
        // 头结点的前驱是 null
        dummyHead.next.prev = null;
        return dummyHead.next;
    }
}

-----------------------------------------------------------------------------有始有终分割线----------------------------------------------------------------------------------

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值