LeetCode 117. 填充每个节点的下一个右侧节点指针 II

117. 填充每个节点的下一个右侧节点指针 II

难度 中等

给定一个二叉树

struct Node {
  int val;
  Node *left;
  Node *right;
  Node *next;
}

填充它的每个 next 指针,让这个指针指向其下一个右侧节点。如果找不到下一个右侧节点,则将 next 指针设置为 NULL

初始状态下,所有 next 指针都被设置为 NULL

进阶:

  • 你只能使用常量级额外空间。
  • 使用递归解题也符合要求,本题中递归程序占用的栈空间不算做额外的空间复杂度。

示例:

LeetCode 117. 填充每个节点的下一个右侧节点指针 II_第1张图片

输入:root = [1,2,3,4,5,null,7]
输出:[1,#,2,3,#,4,5,7,#]
解释:给定二叉树如图 A 所示,你的函数应该填充它的每个 next 指针,以指向其下一个右侧节点,如图 B 所示。序列化输出按层序遍历顺序(由 next 指针连接),'#' 表示每层的末尾。

提示:

  • 树中的节点数小于 6000
  • -100 <= node.val <= 100

题解

解法一:层序遍历

​ 主要通过广度搜索来实现层序遍历,需要辅助队列记录节点和层次,还需要保存前一个节点和层次。通过判断前一个节点和当前节点是否在同一层,如果是同一层,就连接。

class Solution {
    public Node connect(Node root) {
        if(root == null){
            return root;
        }
        Queue<Node> nodeQueue = new LinkedList<Node>();//辅助队列,记录节点
        Queue<Integer> levelQueue = new LinkedList<Integer>();//辅助队列,记录层次
        nodeQueue.offer(root);//节点入队
        levelQueue.offer(1);//层次入队
        Node preNode = root;//前一个节点
        Integer preLevel = 0; //前一个节点层次
        while(!nodeQueue.isEmpty()){//队列不为空
            Node nowNode = nodeQueue.poll();//节点出队
            Integer nowlevel = levelQueue.poll();//层次出队
            if(nowlevel == preLevel){//如果是同一层
                preNode.next = nowNode;//前一个节点指针指向当前节点
            }
            if(nowNode.left != null){//左孩子不为空
                nodeQueue.offer(nowNode.left);
                levelQueue.offer(nowlevel + 1);
            }
            if(nowNode.right != null){//右孩子不为空
                nodeQueue.offer(nowNode.right);
                levelQueue.offer(nowlevel + 1);
            }
            preNode = nowNode;//更新节点
            preLevel = nowlevel;//更新层次
        }
        return root;
    }
}

​ 看到官方题解之后,确实秒,不需要记录层次,通过for循环直接把一层的节点连接起来。

class Solution {
    public Node connect(Node root) {
        if(root == null){
            return root;
        }
        Queue<Node> queue = new LinkedList<Node>();//辅助队列
        queue.offer(root);//入队
        while(!queue.isEmpty()){//队列不为空
            int n = queue.size();//队列大小
            Node last = null;//当前链表尾指针
            for(int i = 1; i <= n; i++){
                Node now = queue.poll();//出队
                if(now.left != null){//左节点不为空
                    queue.offer(now.left);
                }
                if(now.right != null){//右节点不为空
                    queue.offer(now.right);
                }
                if(i != 1){//如果不是当层开始节点,当前节点连接上链表最后
                    last.next = now;
                }
                last = now;//更新节点
            }
        }
        return root;
    }
}
解法二:优化存储空间

​ 上面方法都需要一个辅助队列进行入队出队,但是我们可以发现,我们节点还有个next指针,在我们遍历当前层的时候,可以连接到下一个节点,然后通过next更新到下个节点,如此反复,这样我们就并不需要队列帮我们记录了。

class Solution {
    Node last = null;//当前链表尾指针
    Node nextStart = null;//下一层头指针
    public Node connect(Node root) {
        if(root == null){
            return null;
        }
        Node start = root;
        while(start != null){
            last = null;//初始化下一层链表尾指针为空
            nextStart = null;//初始化下一层头指针为空
            for(Node p = start; p != null; p = p.next){//遍历当前层(按着链表遍历)
                if(p.left != null){//左孩子不为空
                    handle(p.left);
                }
                if(p.right != null){//右孩子不为空
                    handle(p.right);
                }
            }
            start = nextStart;//指向下一层的头指针
        }
        return root;
    }

    //此函数最好带入实例容易理解
    //当遍历到第一层的时候,我们为下一层进行链接
    //此时初始化last和nextStart为空
    //假设左孩子不为空,即进入此方法
    //	第一个判断不成立;第二个判断成立,此时找到了下一层第一个节点(头指针);最后还更新了尾指针last
    //假设右孩子不为空,即进入此方法
    //	第一个判断成立,尾指针不为空,上个节点指向当前节点;第二个判断不成立;最后还更新了尾指针last
    //当第二层都链接完之后,退出for循环,执行start = nextStart;指向下一层的头指针
    //然后进行while判断,重复执行上面的注释过程
    public void handle(Node p){
        if(last != null){//如果当前链表不为空,可进行链接
            last.next = p;
        }
        if(nextStart == null){//如果下一层开始节点为空,则这个节点是开始节点(配合的代码 nextStart = null;)
            nextStart = p;
        }
        last = p;//更新尾指针
    }
}

你可能感兴趣的:(LeetCode,ACM,leetcode,算法)