• LeetCode Notes_#116_填充每个节点的下一个右侧节点指针


    LeetCode Notes_#116_填充每个节点的下一个右侧节点指针

    Contents

    题目

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

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

    填充它的每个 next 指针,让这个指针指向其下一个右侧节点。如果找不到下一个右侧节点,则将 next 指针设置为 NULL。
    初始状态下,所有 next 指针都被设置为 NULL。
    示例:

    提示:

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

    思路分析

    方法1:层序遍历

    在普通的层序遍历代码基础上进行修改,可以解决本题。但是需要借助的辅助队列空间为O(n),不符合题目要求,仅作为参考。

    方法2:借助已有的next指针

    分析可知,可以分为两种连接类型:

    • 第一类连接:两个节点有相同父节点
      • 设节点2是head,可以通过如下代码连接:
      head.left.next = head.right


    • 第二类连接:两个节点没有相同父节点
      • 设节点2是head,可以通过如下代码连接:
    if(head.next != null) head.right.next = head.next.left;


    这样一来,可以利用当前层的next指针,把下一层的节点连接起来。可以将每一层的遍历过程看作链表的遍历,这是内层循环。
    外层循环应该是从上到下遍历每一层,维护一个leftmost指针,指向每一层的最左侧节点。

    方法3:递归

    参考如下图解,整体思路:

    • 将一个树分解为左子树和右子树,每次连接左子树和右子树的相邻部分(只连接左子树和右子树相邻的节点,而不连接左子树和右子树内部的节点)
    • 继续将左子树分解为左子树的左子树,左子树的右子树,连接相邻部分。右子树同理。
    • ...

    这是一个递归过程。

    解答

    解答1:层序遍历

    在普通的层序遍历代码基础上进行修改,可以解决本题。但是需要借助的辅助队列空间为O(n),不符合题目要求,仅作为参考。

    class Solution {
        public Node connect(Node root) {
            if(root == null) return null;
            Queue<Node> queue = new LinkedList<>();
            queue.add(root);
            while(!queue.isEmpty()){
                int size = queue.size();
                //遍历当前层所有元素,将他们连接起来,并将他们的子节点顺序入队
                for(int i = 0;i <= size - 1;i++){
                    //如果用remove(),则会抛出异常
                    Node node = queue.poll();
                    //这里不能用poll(),否则会跳过一个节点,没有访问到
                    //必须加入条件判断,否则当前层最后节点会连接到下一层
                    if(i <= size - 2) node.next = queue.peek();
                    if(node.left != null) queue.offer(node.left);
                    if(node.right != null) queue.offer(node.right);
                }
            }
            return root;
        }
    }

    复杂度分析

    时间复杂度:O(n)
    空间复杂度:O(n)

    解答2:利用已有的next指针

    class Solution {
        public Node connect(Node root) {
            if(root == null) return null;
            Node leftmost = root;
            //注意:在当前层连接的是下一层的节点
            //如果下一层最左侧节点为null,说明已经连接了所有层的节点,可以终止循环
            while(leftmost.left != null){
                //把当前层节点当成链表,向后遍历
                Node head = leftmost;
                while(head != null){
                    //第一种连接(具有相同父节点)
                    head.left.next = head.right;
                    //第二种连接(不具有相同父节点)
                    if(head.next != null) head.right.next = head.next.left;
                    //链表指针后移
                    head = head.next;
                }
                //继续连接下一层
                leftmost = leftmost.left;
            }
            return root;  
        }
    }

    复杂度分析

    时间复杂度:O(n)
    空间复杂度:O(1)

    解答3:递归

    class Solution {
        public Node connect(Node root) {
            dfs(root);
            return root; 
        }
    
        void dfs(Node root){
            if(root == null) return;
            Node left = root.left;
            Node right = root.right;
            //以下是最核心的代码,即连接root左右子树,参考图解
            while(left != null){
                left.next = right;
                left = left.right;
                right = right.left;
            }
            dfs(root.left);
            dfs(root.right);
        }
    }

    复杂度分析

    时间复杂度:O(n)
    空间复杂度:O(h),h是树的深度

  • 相关阅读:
    Go入门笔记-14 EdgeX读取配置文件
    Go入门笔记-13 使用EdgeX日志输出
    Go入门笔记-12 输出unix时间戳
    Go入门笔记-11 Go 获取Linux系统CPU占用率
    htop使用
    Ubuntu子系统默认使用root登录
    函数参数传递数组
    c 'CRTSCTS' undeclared
    c 数组指针使用
    使用SD卡刷OpenWRT后,调整分区大小
  • 原文地址:https://www.cnblogs.com/Howfars/p/13447415.html
Copyright © 2020-2023  润新知