leetcode.116. 填充每个节点的下一个右侧节点指针

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

题目
给定一个完美二叉树,其所有叶子节点都在同一层,每个父节点都有两个子节点。二叉树定义如下:

struct Node {
int val;
Node *left;
Node *right;
Node *next;
}
填充它的每个 next 指针,让这个指针指向其下一个右侧节点。如果找不到下一个右侧节点,则将 next 指针设置为 NULL。

初始状态下,所有 next 指针都被设置为 NULL。
示例:
在这里插入图片描述

输入:{
    
    "$id":"1","left":{
    
    "$id":"2","left":{
    
    "$id":"3","left":null,"next":null,"right":null,"val":4},"next":null,"right":{
    
    "$id":"4","left":null,"next":null,"right":null,"val":5},"val":2},"next":null,"right":{
    
    "$id":"5","left":{
    
    "$id":"6","left":null,"next":null,"right":null,"val":6},"next":null,"right":{
    
    "$id":"7","left":null,"next":null,"right":null,"val":7},"val":3},"val":1}

输出:{
    
    "$id":"1","left":{
    
    "$id":"2","left":{
    
    "$id":"3","left":null,"next":{
    
    "$id":"4","left":null,"next":{
    
    "$id":"5","left":null,"next":{
    
    "$id":"6","left":null,"next":null,"right":null,"val":7},"right":null,"val":6},"right":null,"val":5},"right":null,"val":4},"next":{
    
    "$id":"7","left":{
    
    "$ref":"5"},"next":null,"right":{
    
    "$ref":"6"},"val":3},"right":{
    
    "$ref":"4"},"val":2},"next":null,"right":{
    
    "$ref":"7"},"val":1}

解释:给定二叉树如图 A 所示,你的函数应该填充它的每个 next 指针,以指向其下一个右侧节点,如图 B 所示。

提示:

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

思路

看到二叉树,首先要想到的就是前序遍历中序遍历后序遍历,还有深度优先搜索(DFS),广度优先搜索(BFS)。
这道题用,广度优先搜索(BFS)的方法来做比较容易。

    public void levelOrder(TreeNode tree) {
    
    
        if (tree == null)
            return;
        Queue<TreeNode> queue = new LinkedList<>();
        queue.add(tree);//相当于把数据加入到队列尾部
        while (!queue.isEmpty()) {
    
    
            //poll方法相当于移除队列头部的元素
            TreeNode node = queue.poll();
            System.out.println(node.val);
            if (node.left != null)
                queue.add(node.left);
            if (node.right != null)
                queue.add(node.right);
        }
    }

修改一下,就可以得到方法一。

方法一:层次遍历

在这里插入图片描述

class Solution {
    
    
    public Node connect(Node root) {
    
    
        if (root == null) {
    
    
            return root;
        }
        
        // 初始化队列同时将第一层节点加入队列中,即根节点
        Queue<Node> queue = new LinkedList<Node>(); 
        queue.add(root);
        
        // 外层的 while 循环迭代的是层数
        while (!queue.isEmpty()) {
    
    
            
            // 记录当前队列大小
            int size = queue.size();
            
            // 遍历这一层的所有节点
            for (int i = 0; i < size; i++) {
    
    
                
                // 从队首取出元素
                Node node = queue.poll();
                
                // 连接
                if (i < size - 1) {
    
    
                    node.next = queue.peek();
                }
                
                // 拓展下一层节点
                if (node.left != null) {
    
    
                    queue.add(node.left);
                }
                if (node.right != null) {
    
    
                    queue.add(node.right);
                }
            }
        }
        
        // 返回根节点
        return root;
    }
}

方法二:使用已建立的 next 指针

如果当前节点是左节点,我们知道当前节点的父节点,那么很容易连接它的右节点,(注意,题目说的是完美二叉树)。
在这里插入图片描述
但问题是,同一层的两个节点的的父节点不同,如下图
在这里插入图片描述
如果上一层的节点,连起来了,那是不是可以通过上一层来找到它的next节点呢。
在这里插入图片描述
所以

curr.right.next = curr.next.left;

具体来说

从根节点开始。因为第 0 层只有一个节点,不需要处理。可以在上一层为下一层建立 next 指针。该方法最重要的一点是:位于第 x 层时为第 x+1 层建立next 指针。一旦完成这些连接操作,移至第 x+1 层为第 x+2 层建立 next 指针。
当遍历到某层节点时,该层节点的 next 指针已经建立。这样就不需要队列从而节省空间。每次只要知道下一层的最左边的节点,就可以从该节点开始,像遍历链表一样遍历该层的所有节点。

代码

class Solution {
    
    
    public Node connect(Node root) {
    
    
        if (root == null) {
    
    
            return root;
        }
        
        // 从根节点开始
        Node leftmost = root;
        
        while (leftmost.left != null) {
    
    
            
            // 遍历这一层节点组织成的链表,为下一层的节点更新 next 指针
            Node head = leftmost;
            
            while (head != null) {
    
    
                
                // CONNECTION 1
                head.left.next = head.right;
                
                // CONNECTION 2
                if (head.next != null) {
    
    
                    head.right.next = head.next.left;
                }
                
                // 指针向后移动
                head = head.next;
            }
            
            // 去下一层的最左的节点
            leftmost = leftmost.left;
        }
        
        return root;
    }
}

递归

如果理解了上一层的方式,就不难理解dfs了,

    public Node connect(Node root) {
    
    
        dfs(root, null);
        return root;
    }

    private void dfs(Node curr, Node next) {
    
    
        if (curr == null)
            return;
        curr.next = next;
        dfs(curr.left, curr.right);
        dfs(curr.right, curr.next == null ? null : curr.next.left);
    }

参考
BFS和递归共4种方式解决(最后3种击败了100%的用户)

猜你喜欢

转载自blog.csdn.net/e891377/article/details/109091232