代码随想录 二叉树 Java (一)

文章目录

  • (简单)144. 二叉树的前序遍历
  • (简单)94. 二叉树的中序遍历
  • (简单)145. 二叉树的后序遍历
  • 二叉树的统一遍历方法(参考代码随想录)
  • (中等)102. 二叉树的层序遍历
  • (中等)107. 二叉树的层序遍历II
  • (中等)199. 二叉树的右视图
  • (简单)637. 二叉树的层平均值
  • (中等)429. N叉树的层序遍历
  • (中等)在每个树行中找最大值
  • (中等)116. 填充每个节点的下一个右侧节点指针
  • (中等)117. 填充每个节点的下一个右侧节点指针II
  • (简单)104. 二叉树的最大深度
  • (简单)111. 二叉树的最小深度
  • (简单 经典)226. 翻转二叉树
  • (简单)101. 对称二叉树
  • (简单)100. 相同的树
  • (简单)572. 另一棵树的子树
  • (简单)559. N叉树的最大深度


(简单)144. 二叉树的前序遍历

代码随想录 二叉树 Java (一)_第1张图片
代码随想录 二叉树 Java (一)_第2张图片

递归方式

class Solution {

    public List<Integer> preorderTraversal(TreeNode root) {
        ArrayList<Integer> list = new ArrayList<>();
        preOrder(root, list);
        return list;
    }

    public void preOrder(TreeNode root, List<Integer> list) {
        if (root == null) {
            return;
        }
        list.add(root.val);
        preOrder(root.left, list);
        preOrder(root.right, list);
    }
}

复杂度分析:

  • 时间复杂度:O(n),其中n是二叉树的节点数。每一个节点恰好被遍历一次。
  • 空间复杂度:O(n),为递归过程中栈的开销,平均情况下为O(logn),最坏情况下树呈现链状,为O(n)。

代码随想录 二叉树 Java (一)_第3张图片

非递归方式,使用Stack

class Solution {
    public List<Integer> preorderTraversal(TreeNode root) {
        List<Integer> list = new ArrayList<>();
        if (root == null) {
            return list;
        }
        Stack<TreeNode> stack = new Stack<>();
        stack.push(root);
        list.add(root.val);
        TreeNode node = root.left;
        while (!stack.isEmpty() || node != null) {
            while (node != null) {
                list.add(node.val);
                stack.push(node);
                node = node.left;
            }
            node = stack.pop().right;
        }
        return list;
    }
}

复杂度分析:

  • 时间复杂度:O(n),其中n是二叉树的节点数。每一个节点恰好被遍历一次。
  • 空间复杂度:O(n),为递归过程中栈的开销,平均情况下为O(logn),最坏情况下树呈现链状,为O(n)。
    代码随想录 二叉树 Java (一)_第4张图片

使用BFS的思想,利用栈,实现二叉树的前序遍历

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
class Solution {
    public List<Integer> preorderTraversal(TreeNode root) {
        List<Integer> list = new ArrayList<>();
        if (root == null) {
            return list;
        }
        Stack<TreeNode> stack = new Stack<>();
        stack.push(root);
        while (!stack.isEmpty()) {
            TreeNode node = stack.pop();
            list.add(node.val);
            if (node.right != null) {
                stack.push(node.right);
            }
            if (node.left != null) {
                stack.push(node.left);
            }
        }
        return list;
    }
}

代码随想录 二叉树 Java (一)_第5张图片

(简单)94. 二叉树的中序遍历

代码随想录 二叉树 Java (一)_第6张图片
代码随想录 二叉树 Java (一)_第7张图片
递归方式

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
class Solution {
    public List<Integer> inorderTraversal(TreeNode root) {
        List<Integer> list = new ArrayList<>();
        if (root == null) {
            return list;
        }
        inOrder(root, list);
        return list;
    }

    public void inOrder(TreeNode root, List<Integer> list) {
        if (root == null) {
            return;
        }
        inOrder(root.left, list);
        list.add(root.val);
        inOrder(root.right, list);
    }
}

复杂度分析:

  • 时间复杂度:O(n),其中n为二叉树节点的个数。二叉树的遍历中每个节点会被访问一次且只会被访问一次。
  • 空间复杂度:O(n)。空间复杂度取决于递归的栈深度,而栈深度在二叉树为一条链的情况下会达到O(n)的级别。

非递归方式

class Solution {
    public List<Integer> inorderTraversal(TreeNode root) {
        ArrayList<Integer> list = new ArrayList<>();
        if (root == null) {
            return list;
        }
        Stack<TreeNode> stack = new Stack<>();
        stack.push(root);
        TreeNode node = root.left;
        while (!stack.isEmpty() || node != null) {
            while (node != null) {
                stack.push(node);
                node = node.left;
            }
            node = stack.pop();
            list.add(node.val);
            node = node.right;
        }
        return list;
    }
}

代码随想录 二叉树 Java (一)_第8张图片
复杂度分析:

  • 时间复杂度:O(n),其中n为二叉树节点的个数。二叉树的遍历中每个节点会被访问一次且只会被访问一次。
  • 空间复杂度:O(n)。空间复杂度取决于递归的栈深度,而栈深度在二叉树为一条链的情况下会达到O(n)的级别。

(简单)145. 二叉树的后序遍历

代码随想录 二叉树 Java (一)_第9张图片
代码随想录 二叉树 Java (一)_第10张图片
递归方式

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
import java.util.ArrayList;
import java.util.List;

class Solution {
    public List<Integer> postorderTraversal(TreeNode root) {
        List<Integer> list = new ArrayList<>();
        if (root == null) {
            return list;
        }
        postOrder(root, list);
        return list;
    }

    public void postOrder(TreeNode root, List<Integer> list) {
        if (root == null) {
            return;
        }
        postOrder(root.left, list);
        postOrder(root.right, list);
        list.add(root.val);
    }
}

复杂度分析:

  • 时间复杂度:O(n),其中n是二叉搜索树的节点数。每一个节点恰好被遍历一遍
  • 空间复杂度:O(n),为递归过程中栈的开销,平均情况下为O(logn),最坏情况下呈现链状,为O(n)
    代码随想录 二叉树 Java (一)_第11张图片
    非递归形式

后序遍历的顺序是,左,右,根

后序遍历和前序、中序遍历略微有点不同,因为每一棵子树的根节点可能需要二次入栈。从栈中弹出某一根节点时,需要判断该节点的右子树是否为空,如果为空,或者该节点的右子树已经遍历过,则将该根节点的值加入list中。如果没有遍历过该节点的右子树,则重新将根节点入栈,并开始遍历右子树。

import java.util.ArrayList;
import java.util.List;
import java.util.Stack;

class Solution {
    public List<Integer> postorderTraversal(TreeNode root) {
        List<Integer> list = new ArrayList<>();
        if (root == null) {
            return list;
        }
        Stack<TreeNode> stack = new Stack<>();
        stack.push(root);
        TreeNode node = root.left;
        TreeNode prev = null;
        while (!stack.isEmpty() || node != null) {
            while (node != null) {
                stack.push(node);
                node = node.left;
            }
            node = stack.pop();
            //root.right==prev,表示该节点的右子树一定遍历完成
            if (node.right == null || node.right == prev) {
                list.add(node.val);
                prev = node;
                node = null;
            } else {
                //还没有遍历右子树
                stack.push(node);
                node = node.right;
            }

        }
        return list;
    }
}

代码随想录 二叉树 Java (一)_第12张图片
复杂度分析:

  • 时间复杂度:O(n),其中n是二叉搜索树的节点数。每一个节点恰好被遍历一遍
  • 空间复杂度:O(n),为递归过程中栈的开销,平均情况下为O(logn),最坏情况下呈现链状,为O(n)

前一个非递归实现的后续遍历是深度有限搜索DFS的思想

在代码随想录中还提到一种后序遍历的方式,先序遍历时根左右、后序遍历的左右根,只需要调整一下先序遍历的代码顺序,就变成了根右左的顺序,然后反转result数组,输出的结果顺序就是左右中了

这种方法是广度优先遍历BFS的思想,依照该思想,也可以实现前序遍历,已在上面的前序遍历题目中进行补充

下面代码从栈中弹出来的顺序是:根、右、左,然后将结果list反转一下即可

class Solution {
    public List<Integer> postorderTraversal(TreeNode root) {
        List<Integer> result = new ArrayList<>();
        if (root == null){
            return result;
        }
        Stack<TreeNode> stack = new Stack<>();
        stack.push(root);
        while (!stack.isEmpty()){
            TreeNode node = stack.pop();
            result.add(node.val);
            if (node.left != null){
                stack.push(node.left);
            }
            if (node.right != null){
                stack.push(node.right);
            }
        }
        Collections.reverse(result);
        return result;
    }
}

二叉树的统一遍历方法(参考代码随想录)

非递归方式,也称作迭代法

在DFS的思想下,前序遍历和中序遍历的代码类似,只需要调整代码中节点值加入结果list的位置即可,和后序遍历的代码差别较大

使用BFS的思想,前序遍历和后序遍历的代码类似,只需要修改左子节点和右子节点入栈的先后顺序即可,和中序遍历的代码差别较大

但是,使用递归的方式,前序遍历、中序遍历、后序遍历都只需要微微调整一下代码就可以,代码随想录中给出了一种统一风格的代码

以中序遍历为例,使用栈,无法同时解决访问节点(遍历节点)和处理节点(将元素放进结果集)不一致的情况

对上面这段话的简单解释:
中序遍历是左根右,先访问二叉树顶部的节点,然后一层一层向下访问,直到到达树左面的最底部,再开始处理节点(也就是把节点的数值放进result数组中),这就造成了处理顺序与访问顺序不一致的情况

为了解决这个不一致,将访问的节点放入栈中,把要处理的节点也放入栈中但是要做标记。标记的方法是把要处理的节点放入栈中之后,紧接着放入一个空指针作为标记

使用“统一”的方式写中序遍历

再进行了一两次单步调试后,有点理解这种方式,首先,依赖栈后进先出的特性,按照右、中、左的顺序将节点压入栈中,其次是,对于已经访问过,但是还没有处理的根节点,使用null进行标记。

import java.util.LinkedList;
import java.util.List;
import java.util.Stack;

class Solution {
    public List<Integer> inorderTraversal(TreeNode root) {
        List<Integer> result = new LinkedList<>();
        Stack<TreeNode> stack = new Stack<>();
        if (root != null) {
            stack.push(root);
        }
        while (!stack.isEmpty()) {
            TreeNode node = stack.peek();
            stack.pop();
            if (node != null) {
                if (node.right != null) {
                    stack.push(node.right);//右
                }
                stack.push(node);//中
                stack.push(null);
                if (node.left != null) {
                    stack.push(node.left);//左
                }
            } else {
                node = stack.peek();
                result.add(node.val);
                stack.pop();
            }
        }
        return result;
    }
}

代码随想录 二叉树 Java (一)_第13张图片
使用同样的方法,前序遍历代码

class Solution {
    public List<Integer> preorderTraversal(TreeNode root) {
        List<Integer> result = new LinkedList<>();
        Stack<TreeNode> stack = new Stack<>();
        if (root != null) {
            stack.push(root);
        }
        while (!stack.isEmpty()) {
            TreeNode node = stack.peek();
            stack.pop();
            if (node != null) {
                if (node.right != null) {
                    stack.push(node.right);
                }
                if (node.left != null) {
                    stack.push(node.left);
                }
                stack.push(node);
                stack.push(null);
            } else {
                node = stack.peek();
                result.add(node.val);
                stack.pop();
            }
        }
        return result;
    }
}

代码随想录 二叉树 Java (一)_第14张图片

使用类似的方法,实现后序遍历

class Solution {
    public List<Integer> postorderTraversal(TreeNode root) {
        List<Integer> result = new LinkedList<>();
        Stack<TreeNode> stack = new Stack<>();
        if (root != null) {
            stack.push(root);
        }
        while (!stack.isEmpty()) {
            TreeNode node = stack.peek();
            stack.pop();
            if (node != null) {
                stack.push(node);
                stack.push(null);
                if (node.right != null) {
                    stack.push(node.right);
                }
                if (node.left != null) {
                    stack.push(node.left);
                }
            } else {
                node = stack.peek();
                result.add(node.val);
                stack.pop();
            }
        }
        return result;
    }
}

代码随想录 二叉树 Java (一)_第15张图片

(中等)102. 二叉树的层序遍历

代码随想录 二叉树 Java (一)_第16张图片

在处理某一层的节点之前,先记录一下队列中元素的个数,也就是这一层的节点个数,然后依次弹出这一层的节点,然后再向队列中添加该节点的左子节点和右子节点(如果有的话)

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
class Solution {
    public List<List<Integer>> levelOrder(TreeNode root) {
        List<List<Integer>> result = new ArrayList<>();
        if (root == null) {
            return result;
        }
        Queue<TreeNode> queue = new LinkedList<>();
        queue.add(root);
        TreeNode node;
        while (!queue.isEmpty()) {
            ArrayList<Integer> list = new ArrayList<>();
            int size = queue.size();
            for (int i = 0; i < size; i++) {
                node = queue.poll();
                list.add(node.val);
                if (node.left != null) {
                    queue.add(node.left);
                }
                if (node.right != null) {
                    queue.add(node.right);
                }
            }
            result.add(list);
        }
        return result;
    }
}

(中等)107. 二叉树的层序遍历II

代码随想录 二叉树 Java (一)_第17张图片

因为返回的结果只要是实现了List接口就可以,所以,ArrayList、LinkedList都可以,本题中要求层序遍历是自底向上的层序遍历,依然可以按照102题中的方法去写,只不过每一次往结果List中添加结果时,需要添加在头部的位置,ArrayList底层是数组实现,所以总是从头部插入元素效率较低,所以使用LinkedList,底层使用双向链表,在头部插入元素在O(1)的时间内即可完成

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
class Solution {
    public List<List<Integer>> levelOrderBottom(TreeNode root) {
        LinkedList<List<Integer>> result = new LinkedList<>();
        if (root == null) {
            return result;
        }

        Queue<TreeNode> queue = new LinkedList<>();
        int size = 0;
        TreeNode tmp;
        queue.offer(root);
        while (!queue.isEmpty()) {
            size = queue.size();
            LinkedList<Integer> list = new LinkedList<>();
            for (int i = 0; i < size; i++) {
                tmp = queue.poll();
                list.addLast(tmp.val);
                if (tmp.left != null) {
                    queue.add(tmp.left);
                }
                if (tmp.right != null) {
                    queue.add(tmp.right);
                }
            }
            result.addFirst(list);
        }
        return result;
    }
}

代码随想录 二叉树 Java (一)_第18张图片
复杂度分析:

  • 时间复杂度:O(n),其中n是二叉树节点的个数。每个节点访问一次,结果列表使用链表的结构时,在结果列表头部添加一层节点值的列表的时间复杂度是O(1),因此总时间复杂度是O(n)。
  • 空间复杂度:O(n),其中n是二叉树中的节点个数。空间复杂度取决于队列开销,队列中的节点个数不会超过n。

(中等)199. 二叉树的右视图

代码随想录 二叉树 Java (一)_第19张图片

代码随想录 二叉树 Java (一)_第20张图片
还是使用层次遍历的思想(BFS),只不过结果列表中添加的是每一层的最右边的节点

class Solution {
    public List<Integer> rightSideView(TreeNode root) {
        List<Integer> result = new ArrayList<>();
        if (root == null) {
            return result;
        }
        int size = 0;
        TreeNode node;
        Queue<TreeNode> queue = new LinkedList<>();
        queue.add(root);
        while (!queue.isEmpty()) {
            size = queue.size();
            for (int i = 1; i <= size; i++) {
                node = queue.poll();
                if (i == size) {
                    result.add(node.val);
                }

                if (node.left != null) {
                    queue.add(node.left);
                }
                if (node.right != null) {
                    queue.add(node.right);
                }
            }
        }
        return result;
    }
}

代码随想录 二叉树 Java (一)_第21张图片
看题解,也可以使用DFS的思路

按照 根节点、右子树、左子树的顺序访问,就可以保证每层都是最先访问最右边的节点的。

(与先序遍历 根节点、左子树、右子树 正好相反,先序遍历每层最先访问的是最左边节点)

时间复杂度:O(N),每个节点都被访问过1次
空间复杂度:O(N),因为这不是一棵平衡二叉树,二叉树的深度至少是logN,最坏的情况下会退化成一条链表,深度为N,因此递归使用的栈空间O(N)的。

class Solution {

    List<Integer> result = new ArrayList<>();

    public List<Integer> rightSideView(TreeNode root) {
        dfs(root, 0);
        return result;
    }

    public void dfs(TreeNode root, int depth) {
        if (root == null) {
            return;
        }
        if (depth == result.size()) {
            result.add(root.val);
        }
        depth++;
        dfs(root.right, depth);
        dfs(root.left, depth);
    }
}

代码随想录 二叉树 Java (一)_第22张图片

(简单)637. 二叉树的层平均值

代码随想录 二叉树 Java (一)_第23张图片
在这里插入图片描述

广度优先搜索BFS

还是层次遍历的思路,在弹出每一层节点之前,记录队列当前的size,在弹出这一层每一个节点时,对它们的值进行累加求和,当这一层的节点全部弹出时,将求和的结果除以size,就是这一层的平均值,添加到结果列表中

class Solution {
    public List<Double> averageOfLevels(TreeNode root) {
        List<Double> res = new ArrayList<>();
        Queue<TreeNode> queue = new LinkedList<>();
        queue.add(root);
        while (!queue.isEmpty()) {
            int size = queue.size();
            double sum = 0.0;
            for (int i = 0; i < size; i++) {
                TreeNode node = queue.poll();
                sum += node.val;
                if (node.left != null) {
                    queue.add(node.left);
                }
                if (node.right != null) {
                    queue.add(node.right);
                }
            }
            sum /= size;
            res.add(sum);
        }
        return res;
    }
}

代码随想录 二叉树 Java (一)_第24张图片
复杂度分析:

  • 时间复杂度:O(n),其中n是二叉树中的节点个数

    • 广度优先搜索需要对每个节点访问一次,时间复杂度是O(n)。
    • 需要对二叉树的每一层计算平均值,时间复杂度是O(h),其中h是二叉树的高度,在任何情况下都满足h<=n。
    • 因此总时间复杂度是O(n)
  • 空间复杂度:O(n),其中n是二叉树中的节点个数。空间复杂度取决于队列开销,队列中的节点个数不会超过n。

深度优先搜索DFS

使用深度优先搜索计算二叉树的层平均值,需要维护两个数组,counts用于存储二叉树的每一层的节点数,sums用于存储二叉树的每一层的节点值之和。搜索过程中需要记录当前节点所在层,如果访问到的节点在第i层,则将counts[i]的值加1,并将该节点的值加到sums[i]。

遍历结束之后,第i层的平均值即为sums[i]/counts[i]。

class Solution {
    public List<Double> averageOfLevels(TreeNode root) {
        ArrayList<Integer> counts = new ArrayList<>();
        ArrayList<Double> sums = new ArrayList<>();
        dfs(root, 0, counts, sums);
        ArrayList<Double> res = new ArrayList<>();
        //size表示该二叉树有多少层
        dfs(root, 0, counts, sums);
        int size = sums.size();
        for (int i = 0; i < size; i++) {
            res.add(sums.get(i) / counts.get(i));
        }
        return res;
    }

    private void dfs(TreeNode root, int depth, ArrayList<Integer> counts, ArrayList<Double> sums) {
        if (root == null) {
            return;
        }
        if (depth < sums.size()) {
            sums.set(depth, sums.get(depth) + root.val);
            counts.set(depth, counts.get(depth) + 1);
        } else {
            sums.add(1.0 * root.val);
            counts.add(1);
        }
        dfs(root.left, depth + 1, counts, sums);
        dfs(root.right, depth + 1, counts, sums);
    }
}

代码随想录 二叉树 Java (一)_第25张图片

(中等)429. N叉树的层序遍历

代码随想录 二叉树 Java (一)_第26张图片
代码随想录 二叉树 Java (一)_第27张图片
这一题和二叉树的层序遍历思想一样,区别在于,二叉树的层序遍历,向结果列表中添加当前节点的值以后,需要判断该节点的左子节点和右子节点是否为空,不为空的话加入队列。对于N叉树,则需要判断当前Node对象的children属性,如果里面有不为空的子节点,需要将它们加入队列

/*
// Definition for a Node.
class Node {
    public int val;
    public List children;

    public Node() {}

    public Node(int _val) {
        val = _val;
    }

    public Node(int _val, List _children) {
        val = _val;
        children = _children;
    }
};
*/
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;

class Solution {
    public List<List<Integer>> levelOrder(Node root) {
        ArrayList<List<Integer>> res = new ArrayList<>();
        if (root == null) {
            return res;
        }
        
        Queue<Node> queue = new LinkedList<>();
        
        int size;
        Node node;
        List<Integer> list;

        queue.add(root);
        while (!queue.isEmpty()) {

            size = queue.size();
            list = new ArrayList<>();

            for (int i = 0; i < size; i++) {
                node = queue.poll();
                list.add(node.val);
                for (Node child : node.children) {
                    if (child != null) {
                        queue.add(child);
                    }
                }
            }

            res.add(list);
        }
        return res;
    }
}

代码随想录 二叉树 Java (一)_第28张图片
复杂度分析:

  • 时间复杂度:O(n),其中n是树中包含的节点个数。在广度优先搜索的过程中,我们需要遍历每个节点恰好一次。
  • 空间复杂度:O(n),即为队列需要使用的空间。在最坏的情况下,树只有两层,且最后一层有n-1个节点,此时就需要O(n)的空间。

(中等)在每个树行中找最大值

代码随想录 二叉树 Java (一)_第29张图片
代码随想录 二叉树 Java (一)_第30张图片
采用层序遍历的思想,使用一个变量tmp来记录当前层的最大值,在比较完当前层的所有值以后,将其添加到结果列表中

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;

class Solution {
    public List<Integer> largestValues(TreeNode root) {
        List<Integer> res = new ArrayList<>();
        if (root == null) {
            return res;
        }

        int tmp;
        int size;
        TreeNode node;
        Queue<TreeNode> queue = new LinkedList<>();

        queue.add(root);
        while (!queue.isEmpty()) {
            size = queue.size();
            tmp = Integer.MIN_VALUE;
            for (int i = 0; i < size; i++) {
                node = queue.poll();
                tmp = Math.max(tmp, node.val);
                if (node.left != null) {
                    queue.add(node.left);
                }
                if (node.right != null) {
                    queue.add(node.right);
                }
            }
            res.add(tmp);
        }
        return res;
    }
}

代码随想录 二叉树 Java (一)_第31张图片

复杂度分析:

  • 时间复杂度:O(n),其中n为二叉树节点个数,每一个节点仅会进出队列一次。
  • 空间复杂度:O(n),存储二叉树节点的空间开销。

深度优先搜索DFS

用树的【先序遍历】来进行【深度优先搜索】处理,并用curHeight来标记遍历到的当前节点的高度。当遍历到curHeight高度的节点就判断是否更新该节点的最大值。

import java.util.ArrayList;
import java.util.List;

class Solution {
    public List<Integer> largestValues(TreeNode root) {
        List<Integer> res = new ArrayList<>();
        if (root == null) {
            return res;
        }
        dfs(root, 0, res);
        return res;
    }

    private void dfs(TreeNode root, int curHeight, List<Integer> res) {
        if (root == null) {
            return;
        }
        if (curHeight == res.size()) {
            res.add(root.val);
        } else {
            res.set(curHeight, Math.max(root.val, res.get(curHeight)));
        }
        dfs(root.left, curHeight + 1, res);
        dfs(root.right, curHeight + 1, res);
    }
}

代码随想录 二叉树 Java (一)_第32张图片
复杂度分析:

  • 时间复杂度:O(n),其中n为二叉树节点个数。二叉树的遍历中每个节点都会被访问一次且只会被访问一次。
  • 空间复杂度:O(height),其中height表示二叉树的高度。递归函数需要栈空间,而栈空间取决于递归的深度,因此空间复杂度等价于二叉树的高度。

(中等)116. 填充每个节点的下一个右侧节点指针

代码随想录 二叉树 Java (一)_第33张图片
代码随想录 二叉树 Java (一)_第34张图片
代码随想录 二叉树 Java (一)_第35张图片
层序遍历的思路。

import java.util.LinkedList;
import java.util.Queue;

class Solution {
    public Node connect(Node root) {
        if (root == null) {
            return null;
        }

        Queue<Node> queue = new LinkedList<>();
        int size;
        Node node;

        queue.add(root);
        while (!queue.isEmpty()) {
            size = queue.size();
            for (int i = 1; i <= size; i++) {
                node = queue.poll();
                if (i == size) {
                    node.next = null;
                } else {
                    node.next = queue.peek();
                }
                if (node.left != null) {
                    queue.add(node.left);
                }
                if (node.right != null) {
                    queue.add(node.right);
                }
            }
        }
        return root;
    }
}

代码随想录 二叉树 Java (一)_第36张图片
复杂度分析:

  • 时间复杂度:O(n)。每个节点会被访问一次,即从队列中弹出,并建立next指针。
  • 空间复杂度:O(N)。这是一棵完美二叉树,它的最后一个层级包含N/2个节点。广度优先遍历的复杂度取决于一个层级上的最大元素数量。

官方提供的另一种方法,使用已建立的next指针

思路:

一棵树中存在两种类型的next指针

  1. 第一种情况是连接同一个父节点的两个子节点。它们可以通过同一个节点直接访问到,因此直接可以执行下面的操作即可完成连接。
node.left.next = node.right;

代码随想录 二叉树 Java (一)_第37张图片

  1. 第二种情况在不同父亲的子节点之间建立连接,这种情况不能直接连接。
    • 如果每个节点有指向父节点的指针,可以通过该指针找到next节点。第N层节点之间建立next指针后,再建立第N+1层节点的next指针。可以通过next指针访问同一层的所有节点,因此可以使用第N层的next指针,为第N+1层节点建立next指针。
node.right.next = node.next.left;

代码随想录 二叉树 Java (一)_第38张图片
从根节点开始,由于第0层只有一个节点,所以不需要连接,直接为第一层节点建立next指针即可。该算法中需要注意一点是,当我们为第N层节点建立next指针时,处于第N-1层。当第N层节点的next指针全部建立完成后,移至第N+1层节点的next指针。

遍历某一层的节点时,这层节点的next指针已经建立。因此我们只需要知道这一层的最左节点,就可以按照链表方式遍历,不需要使用队列。

完成当前层的连接后,进入下一层重复操作,直到所有的节点全部连接。进入下一层后需要更新最左节点,然后从新的最左结点开始遍历该层所有节点。因为是完美二叉树,因此最左节点一定是当前最左节点的左孩子。如果当前最左节点的左孩子不存在,说明已经到达该树的最后一层,完成了所有节点的连接。

/*
// Definition for a Node.
class Node {
    public int val;
    public Node left;
    public Node right;
    public Node next;

    public Node() {}

    public Node(int _val) {
        val = _val;
    }

    public Node(int _val, Node _left, Node _right, Node _next) {
        val = _val;
        left = _left;
        right = _right;
        next = _next;
    }
};
*/

class Solution {
    public Node connect(Node root) {
        if (root == null) {
            return null;
        }
        //从根节点开始
        Node leftmost = root;
        while (leftmost.left != null) {
            Node head = leftmost;
            while (head != null) {
                //连接1
                head.left.next = head.right;

                //连接2
                if (head.next != null) {
                    head.right.next = head.next.left;
                }

                //指针向后移动
                head = head.next;
            }
            //去往下一层
            leftmost = leftmost.left;
        }
        return root;
    }
}

指针永远指向的是被操作行的上一层,因为上一层的next指针已经建立完毕

代码随想录 二叉树 Java (一)_第39张图片
复杂度分析:

  • 时间复杂度:O(n),每个节点只访问一次。
  • 空间复杂度:O(1),不需要存储额外的节点。

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

代码随想录 二叉树 Java (一)_第40张图片
代码随想录 二叉树 Java (一)_第41张图片
代码随想录 二叉树 Java (一)_第42张图片
这一题和上一题116的区别是,上一题是完美二叉树,这一题是普通二叉树,其实思路是一样的。

/*
// Definition for a Node.
class Node {
    public int val;
    public Node left;
    public Node right;
    public Node next;

    public Node() {}

    public Node(int _val) {
        val = _val;
    }

    public Node(int _val, Node _left, Node _right, Node _next) {
        val = _val;
        left = _left;
        right = _right;
        next = _next;
    }
};
*/

import java.util.LinkedList;
import java.util.Queue;

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 = 1; i <= size; i++) {
                Node node = queue.poll();
                if (i == size) {
                    node.next = null;
                } else {
                    node.next = queue.peek();
                }
                if (node.left != null) {
                    queue.add(node.left);
                }
                if (node.right != null) {
                    queue.add(node.right);
                }
            }
        }
        return root;
    }
}

代码随想录 二叉树 Java (一)_第43张图片
上面代码的运行效率并不是很高,这是因为我们把节点不停地入队,然后不停地出队,其实可以不需要队列,每一行都可以看成一个链表比如第一行就只有一个节点的链表,第二行是只有两个节点的链表(假如根节点的左右两个子节点都不为空)…

/*
// Definition for a Node.
class Node {
    public int val;
    public Node left;
    public Node right;
    public Node next;

    public Node() {}

    public Node(int _val) {
        val = _val;
    }

    public Node(int _val, Node _left, Node _right, Node _next) {
        val = _val;
        left = _left;
        right = _right;
        next = _next;
    }
};
*/

class Solution {
    public Node connect(Node root) {
        if (root == null) {
            return null;
        }
        //cur可以把它看作是每一层的链表
        Node cur = root;
        while (cur != null) {
            //遍历当前层的时候
            //为了方便操作在下一层前面添加一个哑节点
            //(注意,这里是访问当前层的节点,然后把下一层的节点串起来)
            Node dummy = new Node(0);
            Node pre = dummy;

            //开始遍历当前层的链表
            while (cur != null) {
                if (cur.left != null) {
                    //如果当前节点的左子节点不为空
                    //就让pre节点的next指向它,把它串起来
                    pre.next = cur.left;
                    pre = pre.next;
                }
                //同理
                if (cur.right != null) {
                    pre.next = cur.right;
                    pre = pre.next;
                }
                //继续访问这一行的下一个节点
                cur = cur.next;
            }
            //把下一层串联成一个链表之后,让他赋值给cur
            //后续继续循环,直到cur为空为止
            cur = dummy.next;
        }
        return root;
    }
}

因为上一题的二叉树是完美二叉树,也就是二叉树每一层都是满的状态。不过这一题就不是了,所以需要引入一个哑节点来,该节点的next指针指向的就是当前层的第一个二叉树节点。

代码随想录 二叉树 Java (一)_第44张图片

(简单)104. 二叉树的最大深度

代码随想录 二叉树 Java (一)_第45张图片

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
class Solution {
    public int maxDepth(TreeNode root) {
        if (root == null) {
            return 0;
        }
        return Math.max(maxDepth(root.left), maxDepth(root.right)) + 1;
    }
}

如果知道了左子树和右子树的最大深度l和r,那么该二叉树的最大深度即为max(l,r)+1

而左子树和右子树的最大深度又可以以同样的方式进行计算。因此可以用【深度优先搜索】的方法来计算二叉树的最大深度。具体而言,再计算当前二叉树的最大深度时,可以先递归计算出其左子树和右子树的最大深度,然后再O(1)时间内算出当前二叉树的最大深度。递归再访问到空节点时退出。
代码随想录 二叉树 Java (一)_第46张图片
复杂度分析:

  • 时间复杂度:O(n),其中n为二叉树节点的个数。每个节点在递归中只被遍历一次。
  • 空间复杂度:O(height),其中height表示二叉树的高度。递归函数需要栈空间,而栈空间取决于递归的深度,因此空间复杂度等价于二叉树的高度。

另一种思路:广度优先搜索

层序遍历的思路,队列中存放的是当前层的所有节点。每次扩展下一层的时候,不同于广度优先搜索的每次只从队列中拿出一个节点,而是需要将队列里所有节点都拿出来进行扩展,这样能保证每次拓展完的时候队列里存放的是当前层的所有节点,即是一层一层地进行拓展,最后用一个变量ans来维护拓展地次数,该二叉树地最大深度即为ans。

import java.util.LinkedList;
import java.util.Queue;

class Solution {
    public int maxDepth(TreeNode root) {
        if (root == null) {
            return 0;
        }

        //res记录要返回的结果
        int res = 0;

        //size表示当前层的节点个数
        int size = 0;

        Queue<TreeNode> queue = new LinkedList<>();
        queue.add(root);
        while (!queue.isEmpty()) {
            size = queue.size();
            for (int i = 0; i < size; i++) {
                TreeNode node = queue.poll();
                if (node.left != null) {
                    queue.add(node.left);
                }
                if (node.right != null) {
                    queue.add(node.right);
                }
            }
            //该层的所有节点都已经扩展完毕,深度+1
            res++;
        }
        return res;
        
    }
}

(简单)111. 二叉树的最小深度

代码随想录 二叉树 Java (一)_第47张图片

深度优先搜索

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
class Solution {
    public int minDepth(TreeNode root) {
        if (root == null) {
            return 0;
        }
        //该节点的左右子树都不为空
        if (root.left != null && root.right != null) {
            return Math.min(minDepth(root.left), minDepth(root.right)) + 1;
        } else if (root.left == null && root.right != null) {
            //该节点的左子树为空,右子树不为空
            return minDepth(root.right) + 1;
        } else if (root.left != null) {
            //该节点的左子树不为空,右子树为空
            return minDepth(root.left) + 1;
        } else {
            //该节点为叶子节点,左子树右子树均为空
            return 1;
        }
    }
}

代码随想录 二叉树 Java (一)_第48张图片

对于每一个非叶子节点,只需要分别计算其左右子树的最小叶子节点深度。

class Solution {
    public int minDepth(TreeNode root) {
        if (root == null) {
            return 0;
        }
        if (root.left == null && root.right == null) {
            return 1;
        }
        int min_depth = Integer.MAX_VALUE;
        if (root.left != null) {
            min_depth = Math.min(minDepth(root.left), min_depth);
        }
        if (root.right != null) {
            min_depth = Math.min(minDepth(root.right), min_depth);
        }
        return min_depth + 1;
    }
}

代码随想录 二叉树 Java (一)_第49张图片
复杂度分析:

  • 时间复杂度:O(N),其中N是树的节点数。对每个节点访问一次。
  • 空间复杂度:O(H),其中H是树的高度。空间复杂度主要取决于递归时栈空间的开销,最坏情况下,树呈现链状,空间复杂度为O(N)。平均情况下树的高度与节点数的对数正相关,空间复杂度为O(logN)

广度优先搜索

遍历整棵二叉树,除了可以用深度优先搜索的方法,还可以使用广度优先遍历。

当找到一个叶子节点时,直接返回这个叶子节点的深度。广度优先搜索的性质保证了最先搜索到的叶子节点的深度一定最小。

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
import java.util.LinkedList;
import java.util.Queue;

class Solution {
    public int minDepth(TreeNode root) {
        if (root == null) {
            return 0;
        }
        Queue<TreeNode> queue = new LinkedList<>();
        queue.add(root);
        int depth = 0;
        while (!queue.isEmpty()) {
            int size = queue.size();
            depth++;
            for (int i = 0; i < size; i++) {
                TreeNode node = queue.poll();
                if (node.left == null && node.right == null) {
                    return depth;
                }
                if (node.left != null) {
                    queue.add(node.left);
                }
                if (node.right != null) {
                    queue.add(node.right);
                }
            }
        }
        return depth;
    }
}

代码随想录 二叉树 Java (一)_第50张图片
复杂度分析:

  • 时间复杂度:O(N),其中N是树的节点数。对每个节点访问一次。
  • 空间复杂度:O(N),其中N是树的节点数。空间复杂度主要取决于队列的开销,队列中的元素个数不会超过树的节点数。

(简单 经典)226. 翻转二叉树

代码随想录 二叉树 Java (一)_第51张图片
一看到这个题,我就觉得要用递归,逐层分解

从根节点开始,递归地对树进行遍历,并从叶子节点先开始翻转,如果当前遍历到的节点root的左右两棵子树都已经翻转,那么只需要交换两棵子树的位置,即可完成以root为根节点的整棵子树的翻转。

class Solution {
    public TreeNode invertTree(TreeNode root) {
        if (root == null) {
            return null;
        }
        if (root.left == null && root.right == null) {
            return root;
        }
        TreeNode left = invertTree(root.left);
        TreeNode right = invertTree(root.right);
        root.left = right;
        root.right = left;
        return root;
    }
}

代码随想录 二叉树 Java (一)_第52张图片
复杂度分析:

  • 时间复杂度:O(N),其中N为二叉树节点的数目。需要遍历二叉树种的每一个节点,对每个节点而言,再常数时间内就可以完成交换两棵子树的操作。
  • 空间复杂度:O(N),使用的空间由递归栈的深度决定,它等于当前节点在二叉树中的高度。在平均情况下,二叉树的高度与节点个数为对数关系,即O(logN)。而在最情况下,树形成链状,空间复杂度为O(N)

广度优先遍历

动画演示,参考链接

广度优先遍历需要额外的数据结构——队列,来存放临时遍历到的元素

首先需要将根节点放入到队列中,然后不断地迭代队列中的元素。
对当前元素调换其左右子树的位置,然后:

  • 判断其左子树是否为空,不为空就放入队列中
  • 判断其右子树是否为空,不为空就放入队列中

代码随想录 二叉树 Java (一)_第53张图片

import java.util.LinkedList;
import java.util.Queue;

class Solution {
    public TreeNode invertTree(TreeNode root) {
        if (root == null) {
            return null;
        }
        Queue<TreeNode> queue = new LinkedList<>();
        queue.add(root);
        while (!queue.isEmpty()) {
            int size = queue.size();
            for (int i = 0; i < size; i++) {
                TreeNode node = queue.poll();
                if (node.left != null) {
                    queue.add(node.left);
                }
                if (node.right != null) {
                    queue.add(node.right);
                }
                if (node.right != null || node.left != null) {
                    TreeNode tmp = node.left;
                    node.left = node.right;
                    node.right = tmp;
                }
            }
        }
        return root;
    }
}

代码随想录 二叉树 Java (一)_第54张图片

(简单)101. 对称二叉树

代码随想录 二叉树 Java (一)_第55张图片

写这一题的时候,我想的是,要判断一个二叉树是否对称,则需要判断它的左子树和右子树是否对称,如果只有左子树或者只有右子树,那么就肯定不是对称的,如果左右子树都存在,那么如果左子树的根节点的值与右子树根节点的值不一样,也不是对称的,如果值一样,则需要比较该左子树的左孩子和右子树的右孩子,以及左子树的右孩子和右子树的左孩子是否是对称的。

所以和求最大深度不同,不能只靠原有的那个方法进行递归,而是需要另外写一个方法。

class Solution {
    public boolean isSymmetric(TreeNode root) {
        return checkLeftAndRight(root.left, root.right);
    }

    public boolean checkLeftAndRight(TreeNode leftNode, TreeNode rightNode) {
        //左子树为空,右子树不为空
        if (leftNode == null && rightNode != null) {
            return false;
        }

        //左子树不为空,右子树为空
        if (leftNode != null && rightNode == null) {
            return false;
        }

        //左子树为空,右子树为空
        if (leftNode == null) {
            return true;
        }

        //左右子树都不为空
        //如果他们的值不相等
        if (leftNode.val != rightNode.val) {
            return false;
        }

        return checkLeftAndRight(leftNode.left, rightNode.right)
                && checkLeftAndRight(leftNode.right, rightNode.left);
    }
}

复杂度分析:

假设树上一共有n个节点。

  • 时间复杂度:O(n),因为遍历了整棵树
  • 空间复杂度:O(n),空间复杂度和递归使用的栈空间有关,这里递归层数不超过n。

官方对递归方法的总结:
如果一个左子树与右子树镜像对称,那么这个树是对称的。那么问题就转化为,两棵树在什么情况下互为镜像?

同时满足下面两个条件,两棵树互为镜像:

  • 它们的两个根节点具有相同的值
  • 每个树的右子树与另一个树的左子树镜像对称

另一种解题思路:迭代

引入一个队列,这是把递归程序改写成迭代程序的常用方法。初始化时我们把根节点入队两次。每次提取两个节点比较它们的值(队列中每两个连续的节点应该是相等的,而且它们的子树互为镜像),然后将两个节点的左右子节点按相反的顺序插入队列中。当队列为空时,或者检测到树不对称(即从队列中取出两个不相等的连续节点)时,该算法结束。

import java.util.LinkedList;
import java.util.Queue;

class Solution {
    public boolean isSymmetric(TreeNode root) {
        Queue<TreeNode> queue = new LinkedList<>();
        queue.add(root);
        queue.add(root);
        while (!queue.isEmpty()) {
            TreeNode node1 = queue.poll();
            TreeNode node2 = queue.poll();

            if (node1 == null && node2 == null) {
                continue;
            }

            if (node1==null || node2==null) {
                return false;
            }

            if (node1.val != node2.val) {
                return false;
            }            

            queue.add(node1.left);
            queue.add(node2.right);


            queue.add(node1.right);
            queue.add(node2.left);
        }
        return true;
    }
}

代码随想录 二叉树 Java (一)_第56张图片

复杂度分析:

  • 时间复杂度:O(n),n为二叉树中的节点,需要遍历整个二叉树
  • 空间复杂度:O(n),这里需要用一个队列来维护节点,每个节点最多进队一次,出队一次,队列中最多不会超过n个点。

(简单)100. 相同的树

代码随想录 二叉树 Java (一)_第57张图片

DFS思路

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
class Solution {
    public boolean isSameTree(TreeNode p, TreeNode q) {
        if (p == null && q == null) {
            return true;
        }
        if (p == null || q == null) {
            return false;
        }
        if (p.val != q.val) {
            return false;
        }
        return isSameTree(p.left, q.left) && isSameTree(p.right, q.right);
    }
}

代码随想录 二叉树 Java (一)_第58张图片
复杂度分析:

  • 时间复杂度:O(min(m,n)),其中m和n分别是两个二叉树的节点数。对两个二叉树同时进行深度优先搜索,只有当两个二叉树中的对应节点都不为空时才会访问到该节点,因此被访问到的节点数不会超过较小的二叉树的节点数。
  • 空间复杂度:O(min(m,n)),其中m和n是两个二叉树的节点数。空间复杂度取决于递归调用的层数,递归调用的层数不会超过较小的二叉树的最大高度,最坏情况下,二叉树的高度等于节点数。

BFS思路

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
import java.util.LinkedList;
import java.util.Queue;

class Solution {
    public boolean isSameTree(TreeNode p, TreeNode q) {

        if (p == null && q == null) {
            return true;
        }
        if (p == null || q == null) {
            return false;
        }

        Queue<TreeNode> queue = new LinkedList<>();
        queue.add(p);
        queue.add(q);
        while (!queue.isEmpty()) {
            TreeNode n1 = queue.poll();
            TreeNode n2 = queue.poll();
            if (n1 == null && n2 == null) {
                continue;
            }
            if (n1 == null || n2 == null) {
                return false;
            }
            if (n1.val != n2.val) {
                return false;
            }
            queue.add(n1.left);
            queue.add(n2.left);

            queue.add(n1.right);
            queue.add(n2.right);
        }
        return true;
    }
}

代码随想录 二叉树 Java (一)_第59张图片
复杂度分析:和上面的DFS思路一样。

(简单)572. 另一棵树的子树

代码随想录 二叉树 Java (一)_第60张图片
代码随想录 二叉树 Java (一)_第61张图片

我的思路是:使用层次遍历的方式,遍历二叉树,当遇到某一根节点的val与subRoot根节点的val相同的时候,就判断这两棵二叉树是否长得一样,这块的代码和LeetCode 第100题 相同的树的代码是一样的。

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
import java.util.LinkedList;
import java.util.Queue;

class Solution {
    public boolean isSubtree(TreeNode root, TreeNode subRoot) {
        Queue<TreeNode> queue = new LinkedList<>();
        queue.add(root);
        while (!queue.isEmpty()) {
            int size = queue.size();
            for (int i = 0; i < size; i++) {
                TreeNode node = queue.poll();
                if (node.val == subRoot.val && isSame(node, subRoot)) {
                    return true;
                }
                if (node.left != null) {
                    queue.add(node.left);
                }
                if (node.right != null) {
                    queue.add(node.right);
                }
            }
        }
        return false;
    }

    public boolean isSame(TreeNode root, TreeNode subRoot) {
        if (root == null && subRoot == null) {
            return true;
        }
        if (root == null || subRoot == null) {
            return false;
        }
        if (root.val != subRoot.val) {
            return false;
        }
        return isSame(root.left, subRoot.left) && isSame(root.right, subRoot.right);
    }
}

代码随想录 二叉树 Java (一)_第62张图片
复杂度分析:

  • 时间复杂度:O(s x t),s是二叉树中的节点的个数,t是子树中的节点的个数。对于每一个s上的点,都需要做一次深度优先搜索来和t匹配,匹配一次的时间代价是O(t),那么总时间就是O(s x t)
  • 空间复杂度:假设s深度为ds,t的深度为dt,任意时刻栈空间的最大使用代价是O(max(ds,dt))。

官方提供的另一种思路,KMP


(简单)559. N叉树的最大深度

代码随想录 二叉树 Java (一)_第63张图片
在这里插入图片描述

采用层序遍历的方式计算N叉树的最大深度,广度优先搜索

/*
// Definition for a Node.
class Node {
    public int val;
    public List children;

    public Node() {}

    public Node(int _val) {
        val = _val;
    }

    public Node(int _val, List _children) {
        val = _val;
        children = _children;
    }
};
*/

import java.util.LinkedList;
import java.util.Queue;

class Solution {
    public int maxDepth(Node root) {
        if (root == null) {
            return 0;
        }
        int depth = 0;
        Queue<Node> queue = new LinkedList<>();
        queue.add(root);
        while (!queue.isEmpty()) {
            int size = queue.size();
            depth++;
            for (int i = 0; i < size; i++) {
                Node node = queue.poll();
                for (Node n : node.children) {
                    if (n != null) {
                        queue.add(n);
                    }
                }
            }
        }
        return depth;
    }
}

代码随想录 二叉树 Java (一)_第64张图片

复杂度分析:

  • 时间复杂度:O(n),其中n是N叉树的节点个数。每个节点只会被访问一次。
  • 空间复杂度:此方法空间的消耗取决于队列存储的元素数量,其在最坏情况下会达到O(n)

另一种方法,深度优先搜索DFS

如果根节点有N个子节点,则这N个子节点对应N个子树。记N个子树的最大深度中的最大值为maxChildDepth,则该N叉树的最大深度为maxChildDepth+1。

每个子树的最大深度又可以以同样的方式进行计算。因此,可以使用【深度优先搜索】的方法计算N叉树的最大深度。具体而言,在计算当前N叉树的最大深度时,可以先递归计算出其每个子树的最大深度,然后在O(1)的时间内计算出当前N叉树的最大深度。递归在访问到空节点时退出。

class Solution {
    public int maxDepth(Node root) {
        if (root == null) {
            return 0;
        }
        int maxChildDepth = 0;
        for (Node n : root.children) {
            maxChildDepth = Math.max(maxChildDepth, maxDepth(n));
        }
        return maxChildDepth + 1;
    }
}

复杂度分析:

  • 时间复杂度:O(n),其中n为N叉树节点的个数。每个节点在递归中只被遍历一次。
  • 空间复杂度:O(height),其中height表示N叉树的高度。递归函数需要栈空间,而栈空间取决于递归的深度,因此空间复杂度等价于N叉树的高度。

你可能感兴趣的:(代码随想录,java,算法,数据结构)