二叉树类题目专题训练 -- LeetCode上30道与二叉树相关的题

这个专题中的题目是我跟随代码随想录的刷题计划,在LeetCode上做的与二叉树相关的题目,用于加深对二叉树的理解!

下面的内容将会有每一道题目的题意、在代码随想录中对应的参考文章、我的思路以及我所写的Java代码,希望对你有帮助!


1 - LeetCode 144 145 94 二叉树的三种遍历 – 递归法

参考文章

思路:

本次使用递归法来进行二叉树的三种遍历,迭代法在下一篇文章。注意在LeetCode上写法是并没有直接使用LeetCode给的方法来递归,而是自己再写一个没有返回值的方法来递归,并通过传递列表来更新答案,这样写起来更方便一点。使用递归方法时,要注意确定递归方法的参数和返回值,以及要确定终止条件。

中间节点的顺序就是所谓的遍历方式,可以看以下代码中递归方法的调用部分。
前序遍历:中左右
中序遍历:左中右
后序遍历:左右中

Java代码:

《LeetCode 144 二叉树的前序遍历》

class Solution {
    public List<Integer> preorderTraversal(TreeNode root) {
        List<Integer> ans = new ArrayList<>();
        preorder(root, ans);
        return ans;
    }
    private void preorder(TreeNode node, List<Integer> list){
        if(node == null)return;
        list.add(node.val);
        preorder(node.left, list);
        preorder(node.right, list);
    }
}

《LeetCode 145 二叉树的后序遍历》

class Solution {
    public List<Integer> postorderTraversal(TreeNode root) {
        List<Integer> ans = new ArrayList<>();
        postorder(root, ans);
        return ans;
    }
    private void postorder(TreeNode node, List<Integer> list){
        if(node == null)return;
        postorder(node.left, list);
        postorder(node.right, list);
        list.add(node.val);
    }
}

《LeetCode 94 二叉树的中序遍历》

class Solution {
    public List<Integer> inorderTraversal(TreeNode root) {
        List<Integer> ans = new ArrayList<>();
        inorder(root, ans);
        return ans;
    }
    private void inorder(TreeNode node, List<Integer> list){
        if(node == null)return;
        inorder(node.left, list);
        list.add(node.val);
        inorder(node.right, list);
    }
}


 
 

2 - LeetCode 144 145 94 二叉树的三种遍历 – 迭代法

参考文章1
参考文章2

思路:

之前我们学习了使用递归法来进行二叉树的三种遍历,这次我们考虑使用迭代法来进行遍历,使用栈来实现。推荐将两篇参考文章都看一遍。在两篇参考文章中,第一篇讲了如何使用迭代法来遍历,但是三种遍历方式的实现并不是特别统一,没法像递归法一样仅修改中间元素的访问时机来非常简单地从一种遍历顺序改造为另一种遍历顺序,而是需要较大幅度修改代码。第二篇则讲了如何将这种迭代法改造为可以统一实现的方式。

本篇文章中所附代码使用的是第二篇参考文章中提及的统一实现的方式。

注意点:

1、推入栈的顺序需要注意,不论是哪种遍历顺序,左儿子总是比右儿子先遍历,但是在将他们推入栈的时候,注意是先将右儿子推入栈中,再把左儿子推入栈中,这样才能在把它们弹出栈进行访问时才是顺序正确的,在参考文章1中有细讲这里。

2、关于在栈中推入null节点,这是起到标记的作用,每当遇到null节点时,意味着我们已经将null节点的下一个节点的左右儿子推入到栈过了,这次我们可以直接将下一个节点的值输出了,在参考文章2中有细讲这里。

Java代码:

《LeetCode 144 二叉树的前序遍历》

class Solution {
    public List<Integer> preorderTraversal(TreeNode root) {
        List<Integer> ans = new ArrayList<>();
        if (root == null) return ans;
        Deque<TreeNode> deque = new LinkedList<>();
        deque.push(root);
        while (!deque.isEmpty()) {
            TreeNode rnNode = deque.pop();
            if (rnNode == null) {
                rnNode = deque.pop();
                ans.add(rnNode.val);
            } else {
                if (rnNode.right != null) deque.push(rnNode.right);
                if (rnNode.left != null) deque.push(rnNode.left);
                deque.push(rnNode);
                deque.push(null);
            }
        }
        return ans;
    }
}

《LeetCode 145 二叉树的后序遍历》

class Solution {
    public List<Integer> postorderTraversal(TreeNode root) {
        List<Integer> ans = new ArrayList<>();
        if (root == null) return ans;
        Deque<TreeNode> deque = new LinkedList<>();
        deque.push(root);
        while (!deque.isEmpty()) {
            TreeNode rnNode = deque.pop();
            if (rnNode == null) {
                rnNode = deque.pop();
                ans.add(rnNode.val);
            } else {
                deque.push(rnNode);
                deque.push(null);
                if (rnNode.right != null) deque.push(rnNode.right);
                if (rnNode.left != null) deque.push(rnNode.left);
            }
        }
        return ans;
    }
}

《LeetCode 94 二叉树的中序遍历》

class Solution {
    public List<Integer> inorderTraversal(TreeNode root) {
        List<Integer> ans = new ArrayList<>();
        if (root == null) return ans;
        Deque<TreeNode> deque = new LinkedList<>();
        deque.push(root);
        while (!deque.isEmpty()) {
            TreeNode rnNode = deque.pop();
            if (rnNode == null) {
                rnNode = deque.pop();
                ans.add(rnNode.val);
            } else {
                if (rnNode.right != null) deque.push(rnNode.right);
                deque.push(rnNode);
                deque.push(null);
                if (rnNode.left != null) deque.push(rnNode.left);
            }
        }
        return ans;
    }
}


 
 

3 - LeetCode上10道关于二叉树层序遍历的题目 – 熟练二叉树层序遍历

参考文章

LeetCode 102 二叉树的层序遍历

二叉树层序遍历的模板,使用队列来完成。由于需要将每一层的节点放在一起,所以我们每次遍历新的一层的时候,记录下当前队列的大小,因为当前队列的大小就代表当前层的节点数!要用一个变量来记录当前队列大小,而不能直接使用deque.size(),因为deque.size()是变化的!随着你在遍历这一层并不断将它们的左右儿子推入队列中,deque.size()是一直在变大的,并不能代表当前正在遍历的层的节点个数!

class Solution {
	public List<List<Integer>> levelOrder(TreeNode root) {
		Deque<TreeNode> deque = new LinkedList<>();
		deque.offer(root);
		List<List<Integer>> ans = new ArrayList<>();
		if (root == null)
			return ans;
		while (!deque.isEmpty()) {
			int size = deque.size();
			List<Integer> tmpList = new ArrayList<>();
			for (int i = 0; i < size; i++) {
				TreeNode node = deque.poll();
				tmpList.add(node.val);
				if (node.left != null)
					deque.offer(node.left);
				if (node.right != null)
					deque.offer(node.right);
			}
			ans.add(tmpList);
		}
		return ans;
	}
}

LeetCode 107 二叉树的层序遍历 II

跟上道题是差不多的,差别在于是自底向上的层序遍历,只要在最后用Collections.reverse() 方法将结果反转一下就行了

class Solution {
    public List<List<Integer>> levelOrderBottom(TreeNode root) {
        Deque<TreeNode> deque = new LinkedList<>();
		deque.offer(root);
		List<List<Integer>> ans = new ArrayList<>();
		if (root == null)
			return ans;
		while (!deque.isEmpty()) {
			int size = deque.size();
			List<Integer> tmpList = new ArrayList<>();
			for (int i = 0; i < size; i++) {
				TreeNode node = deque.poll();
				tmpList.add(node.val);
				if (node.left != null)
					deque.offer(node.left);
				if (node.right != null)
					deque.offer(node.right);
			}
			ans.add(tmpList);
		}
        Collections.reverse(ans);//关键代码在这里!!!
		return ans;
    }
}

LeetCode 199 二叉树的右视图

这道题是要返回从右侧所能看到的节点值
在这里插入图片描述
做这道题的时候思路注意还是得往二叉树的层序遍历上靠,仍然是使用二叉树层序遍历模板!
那么如何判断一个节点是否是在当前层的最右边?如果它是在最右边,那么在遍历它上一层的时候,它会是最后一个被推入队列中的!
我们在代码中有使用一个size变量来记录一个层的节点数,并用一个for循环来遍历这一层。那么当在for循环遍历中发现当前节点是当前层最后一个节点时,将它记录下来就可以了!在代码中已注明!

class Solution {
    public List<Integer> rightSideView(TreeNode root) {
        Deque<TreeNode> deque = new LinkedList<>();
		deque.offer(root);
		List<Integer> ans = new ArrayList<>();
		if (root == null)
			return ans;
		while (!deque.isEmpty()) {
			int size = deque.size();
			for (int i = 0; i < size; i++) {
				TreeNode node = deque.poll();
                if(i == size - 1)ans.add(node.val);//关键代码在这里!!!
				if (node.left != null)deque.offer(node.left);
				if (node.right != null)deque.offer(node.right);
			}
		}
		return ans;
    }
}

LeetCode 637 二叉树的层平均值

仍然使用二叉树的层序遍历模板,遍历每一层的时候记录当前层的节点值的和,最后再将和除以当前层的节点个数都得到当前层的平均值了

class Solution {
	public List<Double> averageOfLevels(TreeNode root) {
		List<Double> ans = new ArrayList<>();
		if (root == null)
			return ans;
		Deque<TreeNode> deque = new LinkedList<>();
		deque.offer(root);
		while (!deque.isEmpty()) {
			int size = deque.size();
			double sum = 0;
			for (int i = 0; i < size; i++) {
				TreeNode node = deque.poll();
				if (node.left != null)
					deque.offer(node.left);
				if (node.right != null)
					deque.offer(node.right);
				sum += node.val;//关键代码在这里!!!
			}
			ans.add(sum / size);//关键代码在这里!!!
		}
		return ans;
	}
}

LeetCode 429 N叉树的层序遍历

跟二叉树层序遍历其实大同小异,区别仅仅只是二叉树在对每一个节点进行处理时是将它的左右儿子推入队列,而N叉树就把节点的所有儿子都推入队列就行了

class Solution {
	public List<List<Integer>> levelOrder(Node root) {
		List<List<Integer>> ans = new ArrayList<>();
		if (root == null)
			return ans;
		Deque<Node> deque = new LinkedList<>();
		deque.offer(root);
		while (!deque.isEmpty()) {
			int size = deque.size();
			List<Integer> tmpList = new ArrayList<>();
			for (int i = 0; i < size; i++) {
				Node node = deque.poll();
				tmpList.add(node.val);
				if (node.children != null)//关键代码在这里!!!
					for (Node tmp : node.children)
						deque.offer(tmp);
			}
			ans.add(tmpList);
		}
		return ans;
	}
}

LeetCode 515 在每个树行中找最大值

仍然是二叉树的层序遍历,遍历每一层的时候取当前层节点中的最大值即可

class Solution {
	public List<Integer> largestValues(TreeNode root) {
		List<Integer> ans = new ArrayList<>();
		if (root == null)
			return ans;
		Deque<TreeNode> deque = new LinkedList<>();
		deque.offer(root);
		while (!deque.isEmpty()) {
			int size = deque.size();
			int rnBiggest = 0;
			for (int i = 0; i < size; i++) {
				TreeNode rnNode = deque.poll();
				if (i == 0)
					rnBiggest = rnNode.val;//关键代码在这里!!!
				else
					rnBiggest = rnBiggest >= rnNode.val ? rnBiggest : //关键代码在这里!!!rnNode.val;
				if (rnNode.left != null)
					deque.offer(rnNode.left);
				if (rnNode.right != null)
					deque.offer(rnNode.right);
			}
			ans.add(rnBiggest);
		}
		return ans;
	}
}

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

依然是二叉树的层序遍历。在每一层的遍历时记录上一个节点,然后将当前节点赋给上一个节点的next指针。

class Solution {
	public Node connect(Node root) {
		if (root == null)
			return root;
		Deque<Node> deque = new LinkedList<>();
		deque.offer(root);
		while (!deque.isEmpty()) {
			int size = deque.size();
			Node rnParent = null;
			for (int i = 0; i < size; i++) {
				Node rnNode = deque.poll();
				if (i != 0)
					rnParent.next = rnNode;//关键代码在这里!!!
				rnParent = rnNode;//关键代码在这里!!!
				if (rnNode.left != null)
					deque.offer(rnNode.left);
				if (rnNode.right != null)
					deque.offer(rnNode.right);
			}
		}
		return root;
	}
}

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

这题跟上一道题的区别就在于上一道题题目所给的树是满二叉树,这道题没有要求需要是满二叉树。但是上一道题的解决方法其实跟一棵树是不是满二叉树没有关系,所以直接用上一道题的代码也能过这道题

 
LeetCode 104 二叉树的最大深度

求二叉树的最大深度,每遍历到新的一层的时候深度+1即可。

class Solution {
    public int maxDepth(TreeNode root) {
        if (root == null)
			return 0;
		Deque<TreeNode> deque = new LinkedList<>();
		deque.offer(root);
        int depth = 0;
		while (!deque.isEmpty()) {
            depth++;//关键代码在这里!!!
			int size = deque.size();
			for (int i = 0; i < size; i++) {
				TreeNode rnNode = deque.poll();
				if (rnNode.left != null)
					deque.offer(rnNode.left);
				if (rnNode.right != null)
					deque.offer(rnNode.right);
			}
		}
		return depth;
    }
}

LeetCode 111 二叉树的最小深度

这道题跟上一道题是类似的,只不过它求的是最小深度。当我们在遍历过程中遇到第一个没有儿子的节点,那么这个节点所处的层次数就是这棵树的最小深度。

class Solution {
    public int minDepth(TreeNode root) {
        if (root == null)
			return 0;
		Deque<TreeNode> deque = new LinkedList<>();
		deque.offer(root);
        int depth = 0;
		while (!deque.isEmpty()) {
            depth++;
			int size = deque.size();
			for (int i = 0; i < size; i++) {
				TreeNode rnNode = deque.poll();
                if(rnNode.left==null&&rnNode.right==null)//关键代码在这里!!!
                    return depth;
				if (rnNode.left != null)
					deque.offer(rnNode.left);
				if (rnNode.right != null)
					deque.offer(rnNode.right);
			}
		}
		return depth;
    }
}


 
 

4 - LeetCode 226 翻转二叉树 – 二叉树遍历

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/invert-binary-tree


题意:

给你一棵二叉树的根节点 root ,翻转这棵二叉树,并返回其根节点。

示例 1:

输入:root = [4,2,7,1,3,6,9]
输出:[4,7,2,9,6,3,1]

示例 2:

输入:root = [2,1,3]
输出:[2,3,1]

示例 3:
输入:root = []
输出:[]

提示:
树中节点数目范围在 [0, 100] 内
-100 <= Node.val <= 100


参考文章

思路:

其实就是把二叉树遍历一下,然后在遍历每一个节点的时候将它的左右儿子交换一下就行了。前面学到的几种遍历方式几乎都可以用,但是中序遍历是不可以用的,因为使用中序遍历的话会导致部分节点左右儿子重复交换两次了,不理解的话用纸画画看就能知道了。

我这里使用到的是广度优先遍历(其实就是层序遍历)

本题Java代码:

/**
 * 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 TreeNode invertTree(TreeNode root) {
        if(root==null)return root;
        Deque<TreeNode> deque = new LinkedList<>();
        deque.offer(root);
        while(!deque.isEmpty()){
            TreeNode rnNode = deque.poll();
            TreeNode tmpNode = rnNode.left;
            rnNode.left = rnNode.right;
            rnNode.right = tmpNode;
            if(rnNode.left != null)deque.offer(rnNode.left);
            if(rnNode.right != null)deque.offer(rnNode.right);
        }
        return root;
    }
}


 
 

5 - LeetCode 101 对称二叉树 – 递归法和迭代法

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/symmetric-tree


题意:

给你一个二叉树的根节点 root , 检查它是否轴对称。

示例 1:

输入:root = [1,2,2,3,4,4,3]
输出:true

示例 2:

输入:root = [1,2,2,null,3,null,3]
输出:false

提示:

树中节点数目在范围 [1, 1000] 内
-100 <= Node.val <= 100

进阶:你可以运用递归和迭代两种方法解决这个问题吗?


参考文章

思路:

这道题分递归法和迭代法(迭代法这里用双端队列来做)来做,原理都是一样的,主要是比较根节点的左右儿子所形成的左右子树是否是对称的,这时候是要看内侧和外侧之分,详情看参考文章吧

递归法 Java代码:

class Solution {
	private boolean compare(TreeNode left, TreeNode right) {
		if (left == null && right == null)
			return true;
		else if (left == null || right == null || left.val != right.val)
			return false;
		return compare(left.left, right.right) && compare(left.right, right.left);
	}

	public boolean isSymmetric(TreeNode root) {
		return compare(root.left, root.right);
	}
}

迭代法 Java代码:

class Solution {
	public boolean isSymmetric(TreeNode root) {
		Deque<TreeNode> deque = new LinkedList<>();
		deque.offerFirst(root.left);
		deque.offerLast(root.right);
		while (!deque.isEmpty()) {
			TreeNode left = deque.pollFirst();
			TreeNode right = deque.pollLast();
			if (left == null && right == null)
				continue;// 注意这里是continue,不是return true,要跟递归法区分
			else if (left == null || right == null || left.val != right.val)
				return false;
			deque.offerFirst(left.left);
			deque.offerLast(right.right);
			deque.offerFirst(left.right);
			deque.offerLast(right.left);
		}
		return true;
	}
}


 
 

6 - LeetCode 104和559 二叉树和N叉树的最大深度 – 递归法和迭代法

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/maximum-depth-of-binary-tree


题意:

给定一个二叉树,找出其最大深度。

二叉树的深度为根节点到最远叶子节点的最长路径上的节点数。

说明: 叶子节点是指没有子节点的节点。

示例:
给定二叉树 [3,9,20,null,null,15,7],

     3
    /   \
  9    20
        /   \
     15     7

返回它的最大深度 3 。


参考文章

思路:

递归法:

递归法还是相当简单的,每次递归获取左右儿子较深的深度,然后+1并返回即可。注意递归终止条件是当前节点为null,那么当前节点是不存在的,所以深度应该是返回0而不是1,叶子节点不是null,递归到叶子节点时候深度应该返回1,而不是0。

递归法的Java代码:

class Solution {
    public int maxDepth(TreeNode root) {
        if (root == null) return 0;
        int leftDepth = maxDepth(root.left);
        int rightDepth = maxDepth(root.right);
        return Math.max(leftDepth, rightDepth) + 1;
    }
}

迭代法:

迭代法在当时二叉树层序遍历里面就做到了,参考文章《LeetCode上10道关于二叉树层序遍历的题目 – 熟练二叉树层序遍历》

迭代法的Java代码:

class Solution {
    public int maxDepth(TreeNode root) {
        if (root == null)
			return 0;
		Deque<TreeNode> deque = new LinkedList<>();
		deque.offer(root);
        int depth = 0;
		while (!deque.isEmpty()) {
            depth++;//关键代码在这里!!!
			int size = deque.size();
			for (int i = 0; i < size; i++) {
				TreeNode rnNode = deque.poll();
				if (rnNode.left != null)
					deque.offer(rnNode.left);
				if (rnNode.right != null)
					deque.offer(rnNode.right);
			}
		}
		return depth;
    }
}

这里再做一下 LeetCode 559 N叉树的最大深度 ,道理其实大同小异,就只是二叉树每次获取每个节点的左右儿子为根节点的子树的最大深度,而N叉树就每次获取每个节点的所有儿子为根节点的子树的最大深度就行。也分递归法和迭代法两种做法来做。

递归法Java代码:

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

迭代法Java代码:

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


 
 

7 - LeetCode 111 二叉树的最小深度 – 递归法和迭代法

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/minimum-depth-of-binary-tree


思路:

给定一个二叉树,找出其最小深度。

最小深度是从根节点到最近叶子节点的最短路径上的节点数量。

说明:叶子节点是指没有子节点的节点。

示例 1:


输入:root = [3,9,20,null,null,15,7]
输出:2

示例 2:
输入:root = [2,null,3,null,4,null,5,null,6]
输出:5

提示:
树中节点数的范围在 [0, 105] 内
-1000 <= Node.val <= 1000


参考文章

思路:

递归法:

这道题使用递归法的时候需要注意,求最小深度,并不能像求最大深度一样每次递归直接取较大的左右儿子为根结点的子树的深度,因为这个时候当前节点可能不存在左右儿子。如果当前节点同时不存在左右节点,那么它就是叶子节点,应该返回深度为1;如果当前节点仅存在一个儿子,那么就应该返回这个儿子为根节点的子树的最小深度;如果当前节点存在两个儿子,就直接取较小的左右儿子为根结点的子树的深度。

Java代码:

class Solution {
	public int minDepth(TreeNode root) {
		if (root == null)
			return 0;
		int leftDepth = minDepth(root.left);
		int rightDepth = minDepth(root.right);
		if (root.left == null)
			//只有一个儿子,返回这个儿子为根节点的子树的最小深度
			return rightDepth + 1;
		if (root.right == null)
			//只有一个儿子,返回这个儿子为根节点的子树的最小深度
			return leftDepth + 1;
		//到这里就说明存在两个儿子
		return Math.min(leftDepth, rightDepth) + 1;
	}
}

迭代法:

迭代法与求最大深度非常相似,只不过当我们遇到第一个叶节点就说明当前层数其实就是最小深度,直接返回当前层数就行了。

class Solution {
	public int minDepth(TreeNode root) {
		if (root == null)
			return 0;
		Deque<TreeNode> deque = new LinkedList<>();
		deque.offer(root);
		int depth = 0;
		while (!deque.isEmpty()) {
			depth++;
			int size = deque.size();
			for (int i = 0; i < size; i++) {
				TreeNode rnNode = deque.poll();
				if (rnNode.left == null && rnNode.right == null)// 关键代码在这里!!!
					return depth;
				if (rnNode.left != null)
					deque.offer(rnNode.left);
				if (rnNode.right != null)
					deque.offer(rnNode.right);
			}
		}
		return depth;
	}
}


 
 

8 - LeetCode 222 完全二叉树的节点个数

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/count-complete-tree-nodes


题意:

给你一棵 完全二叉树 的根节点 root ,求出该树的节点个数。

完全二叉树 的定义如下:在完全二叉树中,除了最底层节点可能没填满外,其余每层节点数都达到最大值,并且最下面一层的节点都集中在该层最左边的若干位置。若最底层为第 h 层,则该层包含 1~ 2h 个节点。

示例 1:

输入:root = [1,2,3,4,5,6]
输出:6

示例 2:
输入:root = []
输出:0

示例 3:
输入:root = [1]
输出:1

提示:

树中节点的数目范围是[0, 5*104]
0 <= Node.val <= 5 * 104
题目数据保证输入的树是 完全二叉树

进阶:遍历树来统计节点是一种时间复杂度为 O(n) 的简单解决方案。你可以设计一个更快的算法吗?


参考文章

思路:

这道题我们先尝试用二叉树的遍历方法遍历每一个节点然后计数,分别使用递归法和迭代法。这种做法对所有二叉树都适用,并没有用到完全二叉树的特性,时间复杂度为O(N),比较慢

递归法:

递归法的代码真的好简洁

Java代码:

class Solution {
    public int countNodes(TreeNode root) {
        if (root == null) return 0;
        return countNodes(root.left) + countNodes(root.right) + 1;
    }
}

迭代法:

迭代法这里仍然是借用二叉树层序遍历的模板,用一个变量计数节点数即可。二叉树层序遍历看我的这个文章 LeetCode上10道关于二叉树层序遍历的题目

Java代码:

class Solution {
	public int countNodes(TreeNode root) {
		if (root == null)
			return 0;
		int ans = 0;
		Deque<TreeNode> deque = new LinkedList<>();
		deque.offer(root);
		while (!deque.isEmpty()) {
			int size = deque.size();
			for (int i = 0; i < size; i++) {
				TreeNode rnNode = deque.poll();
				ans++;
				if (rnNode.left != null)
					deque.offer(rnNode.left);
				if (rnNode.right != null)
					deque.offer(rnNode.right);
			}
		}
		return ans;
	}
}

利用完全二叉树的特性解题思路:

一棵完全二叉树要么是满二叉树,要么是最后一层叶子节点没有满。同时,完全二叉树根节点的左右孩子一定有一棵是满二叉树,另一颗也是完全二叉树或者空节点。

如果一棵完全二叉树是满二叉树,那么它的节点数是 2树的深度 + 1。
如果一棵完全二叉树不是满二叉树,那么我们分别递归计算它的左孩子和右孩子的结点,它的节点数是 左孩子节点数 + 右孩子节点数 + 1。在这递归的过程中,递归到某一深度,一定会有左右孩子是满二叉树,就可以按照满二叉树的节点数计算公式来计算。

时间复杂度O(logN * logN),比上面的递归法和迭代法更快

示例图(图片转自代码随想录):

Java代码:

class Solution {
	public int countNodes(TreeNode root) {
		if (root == null)
			return 0;
		TreeNode left = root.left;
		TreeNode right = root.right;
		int leftDepth = 0, rightDepth = 0;
		while (left != null) {
			left = left.left;
			leftDepth++;
		}
		while (right != null) {
			right = right.right;
			rightDepth++;
		}
		if (leftDepth == rightDepth)
			return (2 << leftDepth) - 1;
		else
			return countNodes(root.left) + countNodes(root.right) + 1;
	}
}


 
 

9 - LeetCode 110 平衡二叉树 – 递归法

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/balanced-binary-tree


题意:

给定一个二叉树,判断它是否是高度平衡的二叉树。

本题中,一棵高度平衡二叉树定义为:
一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过 1 。

示例 1:

输入:root = [3,9,20,null,null,15,7]
输出:true

示例 2:

输入:root = [1,2,2,3,3,null,null,4,4]
输出:false

示例 3:
输入:root = []
输出:true

提示:
树中的节点数在范围 [0, 5000] 内
-104 <= Node.val <= 104


参考文章

思路:

首先要理解好平衡二叉树的概念,这里在题意中就已经说明了。
这里我们使用递归法的做法。
当getHeight方法返回-1时,意味着子树不平衡的,那么就说明这整棵树都是不平衡的,直接接着返回-1即可。
如果getHeight方法返回的是非负数,则返回的是当前子树的高度,将两个儿子的子树高度相减,差大于1即当前树是不平衡的,返回-1。
如果当前树是平衡的,返回它的高度。
结果只需判断这棵树是否是平衡的就行,即看返回值是不是-1即可。

本题Java代码:

class Solution {
	public boolean isBalanced(TreeNode root) {
		return getHeight(root) != -1;
	}

	private int getHeight(TreeNode node) {
		if (node == null)
			return 0;
		int leftHeight = getHeight(node.left);
		int rightHeight = getHeight(node.right);
		if (leftHeight == -1 || rightHeight == -1 || Math.abs(leftHeight - rightHeight) > 1)
			return -1;
		return Math.max(leftHeight, rightHeight) + 1;
	}
}


 
 

10 - LeetCode 257 二叉树的所有路径 – 递归法

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/binary-tree-paths


题意:

给你一个二叉树的根节点 root ,按 任意顺序 ,返回所有从根节点到叶子节点的路径。

叶子节点 是指没有子节点的节点。

示例 1:

输入:root = [1,2,3,null,5]
输出:[“1->2->5”,“1->3”]

示例 2:
输入:root = [1]
输出:[“1”]

提示:
树中节点的数目在范围 [1, 100] 内
-100 <= Node.val <= 100


参考文章

思路:

这道题使用了递归法,总体思路还是简单的,把所有路径遍历一下,每次递归时候将遍历到当前节点的字符串传入即可。主要是要注意结果中根节点前面没有"->"。其他已在代码中注明。

本题Java代码:

class Solution {
	public List<String> binaryTreePaths(TreeNode root) {
		return search(root, "");
	}

	private List<String> search(TreeNode node, String curString) {
		//先在上一次传入的curString后面加上当前节点的值
		curString = curString + node.val;
		List<String> list = new ArrayList<>();
		if (node.left == null && node.right == null) {
			//叶子节点,是遍历字符串结尾,直接返回curString
			list.add(curString);
			return list;
		}
		//不是叶子节点,那么会有儿子节点,在curString后面再加"->"
		if (node.left != null)
			list.addAll(search(node.left, curString + "->"));
		if (node.right != null)
			list.addAll(search(node.right, curString + "->"));
		return list;
	}
}


 
 

11 - LeetCode 404 左叶子之和 – 迭代法

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/sum-of-left-leaves


题意:

计算给定二叉树的所有左叶子之和。

示例:

    3
    /  \
  9   20
       /  \
    15   7

在这个二叉树中,有两个左叶子,分别是 9 和 15,所以返回 24


参考文章

思路:

其实需要注意的是,之前我们判断一个节点是否具有某种特性,都是依据它本身以及它两个孩子的特点来判断的。但是这道题我们是要判断一个节点是不是一个左叶子节点,借助节点本身我们没法判断他是不是一个左叶子,所以我们需要从它的父节点来判断它是不是左叶子节点。

判断一个节点是不是一个左叶子节点,首先它需要是父节点的左儿子,其次它没有儿子。那么我们从它的父节点来对它进行判断,判断条件是 node.left != null && node.left.left == null && node.left.right == null ,其中node是当前我们判断节点的父节点。

所以这道题,我们对二叉树进行遍历,然后在每一个节点处判断它是否具有是叶子的左儿子即可。遍历方式都是可以使用的。在这里我使用的是层序遍历法。

本题Java代码:

class Solution {
	public int sumOfLeftLeaves(TreeNode root) {
		if (root == null)
			return 0;
		Deque<TreeNode> deque = new LinkedList<>();
		deque.offer(root);
		int ans = 0;
		while (!deque.isEmpty()) {
			TreeNode node = deque.poll();
			//以下是关键代码
			if (node.left != null && node.left.left == null && node.left.right == null)
				ans += node.left.val;
			if (node.left != null)
				deque.offer(node.left);
			if (node.right != null)
				deque.offer(node.right);
		}
		return ans;
	}
}


 
 

12 - LeetCode 513 找树左下角的值

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/find-bottom-left-tree-value


题意:

给定一个二叉树的 根节点 root,请找出该二叉树的 最底层 最左边 节点的值。

假设二叉树中至少有一个节点。

示例 1:

输入: root = [2,1,3]
输出: 1

示例 2:

输入: [1,2,3,4,null,5,6,null,null,7]
输出: 7

提示:

二叉树的节点个数的范围是 [1,104]
-231 <= Node.val <= 231 - 1


参考文章

思路:

这道题用层序遍历法很容易就解决了。做了很多二叉树的题,只能说,层序遍历法真的很好用。二叉树的层序遍历可以看这篇文章LeetCode上10道关于二叉树层序遍历的题目,做完基本上就对二叉树的层序遍历写法了然于胸。

这道题在每遍历新的一层的时候把当前层的第一个数记录下来就行了,直到遍历完所有的节点后,当前记录的值就是最后一层的最左边节点的值。

本题Java代码:

class Solution {
	public int findBottomLeftValue(TreeNode root) {
		Deque<TreeNode> deque = new LinkedList<>();
		deque.offer(root);
		int ans = 0;
		while (!deque.isEmpty()) {
			int size = deque.size();
			for (int i = 0; i < size; i++) {
				TreeNode node = deque.poll();
				if (i == 0)//关键代码在这里!!!
					ans = node.val;
				if (node.left != null)
					deque.offer(node.left);
				if (node.right != null)
					deque.offer(node.right);
			}
		}
		return ans;
	}
}


 
 

13 - LeetCode 112和113 路径总和 路径总和II – 递归法

参考文章

LeetCode 112 路径总和

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/path-sum


题意:

给你二叉树的根节点 root 和一个表示目标和的整数 targetSum 。判断该树中是否存在 根节点到叶子节点 的路径,这条路径上所有节点值相加等于目标和 targetSum 。如果存在,返回 true ;否则,返回 false 。

叶子节点 是指没有子节点的节点。

示例 1:
在这里插入图片描述
输入:root = [5,4,8,11,null,13,4,7,2,null,null,null,1], targetSum = 22
输出:true
解释:等于目标和的根节点到叶节点路径如上图所示。

示例 2:

输入:root = [1,2,3], targetSum = 5
输出:false
解释:树中存在两条根节点到叶子节点的路径:
(1 --> 2): 和为 3
(1 --> 3): 和为 4
不存在 sum = 5 的根节点到叶子节点的路径。

示例 3:
输入:root = [], targetSum = 0
输出:false
解释:由于树是空的,所以不存在根节点到叶子节点的路径。

提示:
树中节点的数目在范围 [0, 5000] 内
-1000 <= Node.val <= 1000
-1000 <= targetSum <= 1000


思路:

递归法:

递归法写到最后代码可以说是非常的简洁,也很容易理解。如果不理解可以看参考文章中写的,说明的很详细

本题Java代码:

class Solution {
	public boolean hasPathSum(TreeNode root, int targetSum) {
		if (root == null)
			return false;
		if (root.left == null && root.right == null && targetSum == root.val)
			return true;
		return hasPathSum(root.left, targetSum - root.val) || hasPathSum(root.right, targetSum - root.val);
	}
}

 

LeetCode 113 路径总和II

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/path-sum-ii


题意:

给你二叉树的根节点 root 和一个整数目标和 targetSum ,找出所有 从根节点到叶子节点 路径总和等于给定目标和的路径。

叶子节点 是指没有子节点的节点。

示例 1:
在这里插入图片描述
输入:root = [5,4,8,11,null,13,4,7,2,null,null,5,1], targetSum = 22
输出:[[5,4,11,2],[5,8,4,5]]

示例 2:

输入:root = [1,2,3], targetSum = 5
输出:[]

示例 3:
输入:root = [1,2], targetSum = 0
输出:[]

提示:
树中节点总数在范围 [0, 5000] 内
-1000 <= Node.val <= 1000
-1000 <= targetSum <= 1000


思路:

这道题其实跟上一道题是很相似的,只是他需要将这些符合要求的路径输出出来。我在这里采用的跟之前的《LeetCode 257 二叉树的所有路径》的方法是类似的,都是在递归方法的参数中加入当前遍历的序列,只不过在这里将String类型换成了List类型。

只要将上一道题的递归法理解之后,这道题仅仅只是在递归方法中多加个参数而已,而且由于这道题需要遍历二叉树所有的路径且并不仅仅只是返回一个boolean变量,因此在这道题我们的递归方法返回值设为了void,并用一个全局变量ans来记录答案。

需要注意的是: 记住在List变量ans中加入List变量path,加入的是path的引用,即地址值,那么如果这个path变量在其他地方变动了,也会导致ans中的元素对应改变。这样是为什么以下代码中在每次调用递归方法时都重新new一个ArrayList的原因,这样子进行一个List变量的拷贝,才能在每个递归方法中都是一个新的path变量,切记在这里调用递归方法时不是直接把当前递归方法中的实参path变量传入!

如果说要直接传入实参path变量的话也是可以的,记得每次回溯要把path中最后一个元素删掉!

以下提供这两种做法的代码:

每次调用递归方法都重新new一个ArrayList变量的方法 Java代码:

class Solution {
	private List<List<Integer>> ans;

	private void search(TreeNode node, int targetSum, List<Integer> path) {
		if (node == null)
			return;
		path.add(node.val);
		if (node.left == null && node.right == null && targetSum == node.val) {
			ans.add(path);
			return;
		}
		search(node.left, targetSum - node.val, new ArrayList(path));
		search(node.right, targetSum - node.val, new ArrayList(path));
	}

	public List<List<Integer>> pathSum(TreeNode root, int targetSum) {
		ans = new ArrayList<>();
		search(root, targetSum, new ArrayList<>());
		return ans;
	}
}

每次调用递归方法直接传入当前递归方法的path实参 Java代码:

class Solution {
	private List<List<Integer>> ans;

	private void search(TreeNode node, int targetSum, List<Integer> path) {
		if (node == null)
			return;
		path.add(node.val);
		if (node.left == null && node.right == null && targetSum == node.val) {
			List<Integer> tmp = new ArrayList(path);
			ans.add(tmp);
			path.remove(path.size() - 1);//记得删掉最后一个元素
			return;
		}
		search(node.left, targetSum - node.val, path);
		search(node.right, targetSum - node.val, path);
		path.remove(path.size() - 1);//记得删掉最后一个元素
	}

	public List<List<Integer>> pathSum(TreeNode root, int targetSum) {
		ans = new ArrayList<>();
		search(root, targetSum, new ArrayList<>());
		return ans;
	}
}


 
 

14 - LeetCode 106 / 105 从中序与后序 / 前序与中序遍历序列构造二叉树

参考文章

LeetCode 106 从中序与后序遍历序列构造二叉树

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/construct-binary-tree-from-inorder-and-postorder-traversal


题意:

给定两个整数数组 inorder 和 postorder ,其中 inorder 是二叉树的中序遍历, postorder 是同一棵树的后序遍历,请你构造并返回这颗 二叉树 。

示例 1:

输入:inorder = [9,3,15,20,7], postorder = [9,15,7,20,3]
输出:[3,9,20,null,null,15,7]

示例 2:
输入:inorder = [-1], postorder = [-1]
输出:[-1]

提示:
1 <= inorder.length <= 3000
postorder.length == inorder.length
-3000 <= inorder[i], postorder[i] <= 3000
inorder 和 postorder 都由 不同 的值组成
postorder 中每一个值都在 inorder 中
inorder 保证是树的中序遍历
postorder 保证是树的后序遍历


思路:

由中序与后序遍历序列构造二叉树其实在数据结构课程中已经学过了,主要的思路由下图可以看出(图片转自代码随想录):
在这里插入图片描述
每次将当前后序遍历序列中的最后一个元素作为当前的节点的值,然后在中序遍历序列中寻找这个值,中序遍历中在这个值左边的值就是在当前节点的左子树中,在这个值右边的值就是在当前节点的右子树中。

思路是不难的,难点主要是代码实现。我们使用递归法来做这道题,在每层递归中将当前的中序遍历序列数组和后序遍历序列数组进行切割。注意:将数组进行切割需要先对中序遍历序列数组进行切割,再切割后序遍历序列数组! 这是只有获取当前节点的值后在中序遍历序列数组找到这个值,再切割中序遍历数组,我们才能知道在当前节点的左子树和右子树中的节点数各有多少个,然后来对后序遍历数组进行切割。另外,数组切割过程仍然需要注意遵循左闭右开的原则。 更多细节在代码中感受吧!

本题Java代码:

class Solution {
	private TreeNode build(int[] inorder, int inLeft, int inRight, int[] postorder, int postLeft, int postRight) {
		if (inRight - inLeft < 1) return null;
		if (inRight - inLeft == 1) return new TreeNode(inorder[inLeft]);
		int rootVal = postorder[postRight - 1];
		TreeNode root = new TreeNode(rootVal);
		int rootIndex = 0;
		for (int i = inLeft; i < inRight; i++) {
			if (inorder[i] == rootVal) {
				rootIndex = i;
				break;
			}
		}
		root.left = build(inorder, inLeft, rootIndex, postorder, postLeft, postLeft + (rootIndex - inLeft));
		root.right = build(inorder, rootIndex + 1, inRight, postorder, postLeft + (rootIndex - inLeft), postRight - 1);
		return root;
	}

	public TreeNode buildTree(int[] inorder, int[] postorder) {
		return build(inorder, 0, inorder.length, postorder, 0, postorder.length);
	}
}

 
LeetCode 105 从前序与中序遍历序列构造二叉树

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/construct-binary-tree-from-preorder-and-inorder-traversal


题意:

给定两个整数数组 preorder 和 inorder ,其中 preorder 是二叉树的先序遍历, inorder 是同一棵树的中序遍历,请构造二叉树并返回其根节点。

示例 1:

输入: preorder = [3,9,20,15,7], inorder = [9,3,15,20,7]
输出: [3,9,20,null,null,15,7]

示例 2:
输入: preorder = [-1], inorder = [-1]
输出: [-1]

提示:
1 <= preorder.length <= 3000
inorder.length == preorder.length
-3000 <= preorder[i], inorder[i] <= 3000
preorder 和 inorder 均 无重复 元素
inorder 均出现在 preorder
preorder 保证 为二叉树的前序遍历序列
inorder 保证 为二叉树的中序遍历序列


思路:

这道题其实跟上一道题是一样的,只不过我们改为每次由前序遍历序列数组的第一个值来获取当前节点的值。

这里我们注意一下,能由前序与中序遍历序列或由中序与后序遍历序列来构造二叉树,那么能不能由前序与后序遍历序列来构造二叉树?前序和后序不能唯一确定一颗二叉树! 答案是不能的!没有中序遍历无法确定左右部分,无法进行分割。

本题Java代码:

class Solution {
	private TreeNode build(int[] preorder, int preLeft, int preRight, int[] inorder, int inLeft, int inRight) {
		if (preRight - preLeft < 1) return null;
		if (preRight - preLeft == 1) return new TreeNode(preorder[preLeft]);
		int rootVal = preorder[preLeft];
		TreeNode root = new TreeNode(rootVal);
		int rootIndex = 0;
		for (int i = inLeft; i < inRight; i++) {
			if (inorder[i] == rootVal) {
				rootIndex = i;
				break;
			}
		}
		root.left = build(preorder, preLeft + 1, preLeft + 1 + (rootIndex - inLeft), inorder, inLeft, rootIndex);
		root.right = build(preorder, preLeft + 1 + (rootIndex - inLeft), preRight, inorder, rootIndex + 1, inRight);
		return root;
	}

	public TreeNode buildTree(int[] preorder, int[] inorder) {
		return build(preorder, 0, preorder.length, inorder, 0, inorder.length);
	}
}


 
 

15 - LeetCode 654 最大二叉树 – 递归法构造二叉树

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/maximum-binary-tree


题意:

给定一个不重复的整数数组 nums 。 最大二叉树 可以用下面的算法从 nums 递归地构建:

  1. 创建一个根节点,其值为 nums 中的最大值。
  2. 递归地在最大值 左边 的 子数组前缀上 构建左子树。
  3. 递归地在最大值 右边 的 子数组后缀上 构建右子树。

返回 nums 构建的 最大二叉树 。

示例 1:

输入:nums = [3,2,1,6,0,5]
输出:[6,3,5,null,2,0,null,null,1]
解释:递归调用如下所示:

  • [3,2,1,6,0,5] 中的最大值是 6 ,左边部分是 [3,2,1] ,右边部分是 [0,5] 。
    • [3,2,1] 中的最大值是 3 ,左边部分是 [] ,右边部分是 [2,1] 。
      • 空数组,无子节点。
      • [2,1] 中的最大值是 2 ,左边部分是 [] ,右边部分是 [1] 。
        • 空数组,无子节点。
        • 只有一个元素,所以子节点是一个值为 1 的节点。
    • [0,5] 中的最大值是 5 ,左边部分是 [0] ,右边部分是 [] 。
      • 只有一个元素,所以子节点是一个值为 0 的节点。
      • 空数组,无子节点。

示例 2:

输入:nums = [3,2,1]
输出:[3,null,2,null,1]

提示:

1 <= nums.length <= 1000
0 <= nums[i] <= 1000
nums 中的所有整数 互不相同


参考文章

思路:

这道题其实代码上的实现方法与《LeetCode 106 / 105 从中序与后序 / 前序与中序遍历序列构造二叉树》非常相似,都是使用递归法,不断切割数组,且切割数组并不是直接返回一个新的数组对象,而是直接使用下标索引的方法来对数组进行分隔,即每次递归都是直接使用下标索引来在原数组上进行操作。同时这道题的代码实现仍然是使用左闭右开的做法。

构造树一般先构造中间节点,然后递归构造左子树和右子树!在这里的代码实现中,都是直接在每一次的递归中对当前的数组进行遍历找出最大值。

本题Java代码:

class Solution {
	private TreeNode construct(int[] nums, int left, int right) {
		if (right - left < 1)
			return null;
		if (right - left == 1)
			return new TreeNode(nums[left]);
		int maxIndex = left;
		for (int i = left + 1; i < right; i++) {
			if (nums[i] > nums[maxIndex])
				maxIndex = i;
		}
		TreeNode root = new TreeNode(nums[maxIndex]);
		root.left = construct(nums, left, maxIndex);
		root.right = construct(nums, maxIndex + 1, right);
		return root;
	}

	public TreeNode constructMaximumBinaryTree(int[] nums) {
		return construct(nums, 0, nums.length);
	}
}


 
 

16 - LeetCode 617 合并二叉树 – 递归法

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/merge-two-binary-trees


题意:

给你两棵二叉树: root1 和 root2 。

想象一下,当你将其中一棵覆盖到另一棵之上时,两棵树上的一些节点将会重叠(而另一些不会)。你需要将这两棵树合并成一棵新二叉树。合并的规则是:如果两个节点重叠,那么将这两个节点的值相加作为合并后节点的新值;否则,不为 null 的节点将直接作为新二叉树的节点。

返回合并后的二叉树。

注意: 合并过程必须从两个树的根节点开始。

示例 1:

输入:root1 = [1,3,2,5], root2 = [2,1,3,null,4,null,7]
输出:[3,4,5,5,4,null,7]

示例 2:
输入:root1 = [1], root2 = [1,2]
输出:[2,2]

提示:
两棵树中的节点数目在范围 [0, 2000] 内
-104 <= Node.val <= 104


参考文章

思路:

本题使用的是递归法,代码写完还是很简洁的。

这道题与构造二叉树有点类似,主要是同时要遍历两棵树,同时与构造二叉树相似在于都是在当前递归方法中构建一个节点,递归赋值左右子树,然后返回当前节点。

关键是要注意当前递归方法中如果我们传入的两个节点如果有一个是空节点,那我们直接返回另一个节点就可以了,这样代码写起来简洁又方便。

本题Java代码:

class Solution {
	public TreeNode mergeTrees(TreeNode root1, TreeNode root2) {
		if (root1 == null) return root2;
		if (root2 == null) return root1;
		TreeNode root = new TreeNode(root1.val + root2.val);
		root.left = mergeTrees(root1.left, root2.left);
		root.right = mergeTrees(root1.right, root2.right);
		return root;
	}
}


 
 

17 - LeetCode 236 二叉树的最近公共祖先 – 递归法

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/lowest-common-ancestor-of-a-binary-tree


题意:

给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。

百度百科中最近公共祖先的定义为:“对于有根树 T 的两个节点 p、q,最近公共祖先表示为一个节点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。”

示例 1:

输入:root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 1
输出:3
解释:节点 5 和节点 1 的最近公共祖先是节点 3 。

示例 2:

输入:root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 4
输出:5
解释:节点 5 和节点 4 的最近公共祖先是节点 5 。因为根据定义最近公共祖先节点可以为节点本身。

示例 3:
输入:root = [1,2], p = 1, q = 2
输出:1

提示:
树中节点数目在范围 [2, 105] 内。
-109 <= Node.val <= 109
所有 Node.val 互不相同 。
p != q
p 和 q 均存在于给定的二叉树中。


参考文章

思路:

这道题涉及到二叉树的自底向上查找,而二叉树的自底向上查找我们可以借助回溯来实现,同时二叉树的后序遍历也即是天然的回溯过程,因此我们先处理叶子节点,再处理中间节点。

这道题在二叉树自底向上的传递过程中通过递归方法的返回值来判断最近公共祖先,使用null来做标记:

  • 当递归方法返回null时,意味着当前遍历的子树中不含有与目标节点相关的节点;
  • 相反,如果递归方法返回的值不会null,意味着当前遍历的子树中含有与目标节点相关的节点。

当一个节点的左右子树递归遍历的结果返回值都不为null时,意味着这个节点的左右子树都含有与目标节点相关节点,则当前节点就是最近公共祖先。

下面两个图很形象地说明了这个过程(图片转自代码随想录):
在这里插入图片描述
在这里插入图片描述
更多细节可以参阅参考文章,讲的很详细!

本题Java代码:

class Solution {
	public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
		if (root == p || root == q || root == null) return root;
		TreeNode left = lowestCommonAncestor(root.left, p, q);
		TreeNode right = lowestCommonAncestor(root.right, p, q);
		if (left != null && right != null) return root;
		if (left == null && right != null) return right;
		return left;
	}
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值