题目汇总:https://leetcode-cn.com/tag/tree/
117. 填充每个节点的下一个右侧节点指针 II中等(一次不会)
124. 二叉树中的最大路径和困难(一次不会)
129. 求根到叶子节点数字之和中等
144. 二叉树的前序遍历中等[✔]
145. 二叉树的后序遍历困难[✔]
173. 二叉搜索树迭代器中等(对迭代器很不熟悉)
199. 二叉树的右视图中等[✔]
222. 完全二叉树的节点个数中等(只会递归解法)
226. 翻转二叉树简单[✔]
117. 填充每个节点的下一个右侧节点指针 II中等
给定一个二叉树
struct Node {
int val;
Node *left;
Node *right;
Node *next;
}
填充它的每个 next 指针,让这个指针指向其下一个右侧节点。如果找不到下一个右侧节点,则将 next 指针设置为NULL
。
初始状态下,所有 next 指针都被设置为NULL
。
进阶
你只能使用常量级额外空间。
使用递归解题也符合要求,本题中递归程序占用的栈空间不算做额外的空间复杂度。
示例:
输入:root = [1,2,3,4,5,null,7]
输出:[1,#,2,3,#,4,5,7,#]
解释:给定二叉树如图 A 所示,你的函数应该填充它的每个 next 指针,以指向其下一个右侧节点,如图 B 所示。
提示:
树中的节点数小于6000
,-100 <= node.val <= 100
思路:递归
和上一题的主要区别是上题输入是完美二叉树较简单。此题是普通二叉树,考虑情况较为复杂。
看题解注意:递归时要先递归右子树,否则上级节点next关系没建好,下级无法成功getNextNoNullChild
代码来自链接https://leetcode-cn.com/problems/populating-next-right-pointers-in-each-node-ii/solution/di-gui-fa-jian-dan-yi-dong-ban-xin-shou-kan-by-lov/
class Solution {
public Node connect(Node root) {//执行用时 :0 ms, 在所有 Java 提交中击败了100.00%的用户
if(root == null)
return root;
if(root.left == null && root.right == null){//左右子树都为空
return root;
}
if(root.right == null){//只有左子树
root.left.next = getNextNoNullChild(root);
}
if(root.left == null){//只有右子树
root.right.next = getNextNoNullChild(root);
}
if(root.left != null && root.right != null) {//左右子树都不为空
root.left.next = root.right;//左子树的next就是右子树
root.right.next = getNextNoNullChild(root);//右子树的next就是下一级右手第一个
}
root.right = connect(root.right);
root.left = connect(root.left);//这里必须先右后左,否则报错
return root;
}
//根据根节点,找到下一级右手第一个
private static Node getNextNoNullChild(Node root) {
while (root.next != null) {
if (root.next.left != null) {
return root.next.left;
}
if (root.next.right != null) {
return root.next.right;
}
root = root.next;
}
return null;
}
}
124. 二叉树中的最大路径和困难
给定一个非空二叉树,返回其最大路径和。
本题中,路径被定义为一条从树中任意节点出发,达到任意节点的序列。该路径至少包含一个节点,且不一定经过根节点。
示例 1:
输入: [1,2,3]
输出: 6
示例 2:
输入: [-10,9,20,null,null,15,7]
输出: 42
思路:
代码来自https://leetcode-cn.com/problems/binary-tree-maximum-path-sum/solution/er-cha-shu-zhong-de-zui-da-lu-jing-he-by-ikaruga/评论区下方cheney
的回答。
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
int max = Integer.MIN_VALUE;
public int maxPathSum(TreeNode root) {
if(root == null)
return 0;
helper(root);
return max;
}
/* 返回经过root的单边分支最大和, 即Math.max(root, root+left, root+right)*/
private int helper(TreeNode root){
if(root == null)
return 0;
//计算左边分支最大值,左边分支如果为负数还不如不选择
//(只有左右子节点的最大贡献值大于 0 ,当前节点的最大贡献值才会变大。)
int leftMax = Math.max(0, helper(root.left));
//计算右边分支最大值,右边分支如果为负数还不如不选择
int rightMax = Math.max(0, helper(root.right));
//left->root->right 作为路径与历史最大值做比较
//(节点的最大路径和取决于该节点的值与该节点的左右子节点的最大贡献值,更新最大和记录)
max = Math.max(max, root.val + leftMax + rightMax);
// 返回经过root的单边最大分支给上游(返回当前节点的最大贡献值)
return root.val + Math.max(leftMax, rightMax);
}
}
129. 求根到叶子节点数字之和中等
给定一个二叉树,它的每个结点都存放一个
0-9
的数字,每条从根到叶子节点的路径都代表一个数字。
例如,从根到叶子节点路径1->2->3
代表数字123
。
计算从根到叶子节点生成的所有数字之和。
说明: 叶子节点是指没有子节点的节点。
思路:递归+DFS
class Solution {//执行用时 :0 ms, 在所有 Java 提交中击败了100.00%的用户
int result = 0;
public int sumNumbers(TreeNode root) {
if(root != null)
dfs(root, 0);
return result;
}
private void dfs(TreeNode root, int current){
current = current * 10 + root.val;
if(root.left == null && root.right == null){
result += current;
}
if(root.left != null){
dfs(root.left, current);
}
if(root.right != null){
dfs(root.right, current);
}
}
}
144. 二叉树的前序遍历中等
给定一个二叉树,返回它的 *前序 *遍历。
进阶: 递归算法很简单,你可以通过迭代算法完成吗?
思路一:递归
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {//执行用时:0 ms, 在所有 Java 提交中击败了100.00%的用户
public List preorderTraversal(TreeNode root) {
List res = new ArrayList<>();
if(root == null)
return res;
preorder(root, res);
return res;
}
private void preorder(TreeNode root, List res){
if(root != null){
res.add(root.val);
preorder(root.left, res);
preorder(root.right, res);
}
}
}
思路二:迭代
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {//执行用时:0 ms, 在所有 Java 提交中击败了100.00%的用户
public List preorderTraversal(TreeNode root) {
List res = new ArrayList<>();
if(root == null)
return res;
Deque stack = new ArrayDeque<>(); // 创建一个栈,官方推荐利用Deque
stack.push(root);//根节点入栈
while (!stack.isEmpty()){
// 1.节点出栈,访问节点, 加入ArrayList中
TreeNode node = stack.poll();
res.add(node.val);
//之所以先加入右孩子再加入左孩子是利用栈后入先出的原则
// 2.若右孩子节点非空, 则将右孩子入栈
if(node.right != null)
stack.push(node.right);
// 3.若左孩子节点非空, 则将左孩子入栈
if(node.left != null)
stack.push(node.left);
}
return res;
}
}
145. 二叉树的后序遍历困难
给定一个二叉树,返回它的 后序遍历。
示例:
输入: [1,null,2,3]
输出: [3,2,1]
进阶: 递归算法很简单,你可以通过迭代算法完成吗?
思路一:递归
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {//执行用时:0 ms, 在所有 Java 提交中击败了100.00%的用户
public List postorderTraversal(TreeNode root) {
List res = new ArrayList<>();
if(root == null)
return res;
postorder(root, res);
return res;
}
private void postorder(TreeNode root, List res){
if(root != null){
postorder(root.left, res);
postorder(root.right, res);
res.add(root.val);
}
}
}
思路二:迭代
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {执行用时:1 ms, 在所有 Java 提交中击败了56.90%的用户
public List postorderTraversal(TreeNode root) {
List res = new ArrayList<>();
if(root == null)
return res;
Deque stack = new ArrayDeque<>(); // 创建一个栈,官方推荐利用Deque
stack.push(root);//根节点入栈
while (!stack.isEmpty()){
// 1.节点出栈,保存节点值
TreeNode node = stack.poll();
res.add(node.val);
// 2.左孩子节点入栈
if(node.left != null)
stack.push(node.left);
// 3.右孩子节点入栈
if(node.right != null)
stack.push(node.right);
}
Collections.reverse(res);//再逆序访问节点
return res;
}
}
173. 二叉搜索树迭代器中等
实现一个二叉搜索树迭代器。你将使用二叉搜索树的根节点初始化迭代器。
调用next()
将返回二叉搜索树中的下一个最小的数。
BSTIterator iterator = new BSTIterator(root);
iterator.next(); // 返回 3
iterator.next(); // 返回 7
iterator.hasNext(); // 返回 true
iterator.next(); // 返回 9
iterator.hasNext(); // 返回 true
iterator.next(); // 返回 15
iterator.hasNext(); // 返回 true
iterator.next(); // 返回 20
iterator.hasNext(); // 返回 false
提示:
next() 和 hasNext() 操作的时间复杂度是 O(1),并使用 O(h) 内存,其中 h 是树的高度。
你可以假设 next() 调用总是有效的,也就是说,当调用 next() 时,BST 中至少存在一个下一个最小的数。
思路:队列
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class BSTIterator {//执行用时:28 ms, 在所有 Java 提交中击败了24.37%的用户
Queue queue = new LinkedList<>();
public BSTIterator(TreeNode root) {
inorder(root);
}
private void inorder(TreeNode root){
if(root != null){
inorder(root.left);
queue.add(root.val);
inorder(root.right);
}
}
/** @return the next smallest number */
public int next() {
return queue.poll();
}
/** @return whether we have a next smallest number */
public boolean hasNext() {
return !queue.isEmpty();
}
}
/**
* Your BSTIterator object will be instantiated and called as such:
* BSTIterator obj = new BSTIterator(root);
* int param_1 = obj.next();
* boolean param_2 = obj.hasNext();
*/
199. 二叉树的右视图中等
给定一棵二叉树,想象自己站在它的右侧,按照从顶部到底部的顺序,返回从右侧所能看到的节点值。
示例:
思路一:DFS+递归
我们按照 「根结点 -> 右子树 -> 左子树」 的顺序访问, 就可以保证每层都是最先访问最右边的节点的。
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {//执行用时 :0 ms, 在所有 Java 提交中击败了100.00%的用户
List res = new ArrayList<>();
public List rightSideView(TreeNode root) {
dfs(root, 0);// 从根节点开始访问,根节点深度是0
return res;
}
private void dfs(TreeNode root, int depth){
if(root == null)
return;
// 先访问当前节点,再递归地访问 右子树 和 左子树。
if(depth == res.size()){
//如果当前节点所在深度还没有出现在res里,说明在该深度下当前节点是第一个被访问的节点
//因此将当前节点加入res中。
res.add(root.val);
}
depth++;
dfs(root.right, depth);
dfs(root.left, depth);
}
}
思路二:BFS
这道题就是在 102. 二叉树的层序遍历的基础上扩展的,使用层序遍历,并只保留每层最后一个节点的值
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
//2020.06.17
class Solution {//执行用时 :1 ms, 在所有 Java 提交中击败了94.94%的用户
public List rightSideView(TreeNode root) {
List res = new ArrayList<>();
Queue queue = new ArrayDeque<>();
if(root != null){
queue.add(root);//将根节点放入队列中,然后不断遍历队列
}
while(!queue.isEmpty()){
int n = queue.size();//获取当前队列的长度,也就是当前这一层的节点个数
//遍历当前层的所有节点
for(int i=0;i
222. 完全二叉树的节点个数中等
给出一个完全二叉树,求出该树的节点个数。
说明:
完全二叉树的定义如下:在完全二叉树中,除了最底层节点可能没填满外,其余每层节点数都达到最大值,并且最下面一层的节点都集中在该层最左边的若干位置。若最底层为第 h 层,则该层包含 1~ 2h 个节点。
示例:
思路一:递归
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {//执行用时:0 ms, 在所有 Java 提交中击败了100.00%的用户
public int countNodes(TreeNode root) {
if(root == null)
return 0;
return countNodes(root.left) + countNodes(root.right) + 1;
}
}
思路二:利用完全二叉树的性质
代码来自https://leetcode-cn.com/problems/count-complete-tree-nodes/solution/chang-gui-jie-fa-he-ji-bai-100de-javajie-fa-by-xia/
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
public int countNodes(TreeNode root) {
if(root == null){
return 0;
}
int left = countLevel(root.left);
int right = countLevel(root.right);
if(left == right){
return countNodes(root.right) + (1<
226. 翻转二叉树简单
翻转一棵二叉树。
示例:
思路:递归
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {执行用时:0 ms, 在所有 Java 提交中击败了100.00%的用户
public TreeNode invertTree(TreeNode root) {// 先序遍历--从顶向下交换
if(root == null)
return null;
TreeNode rightTree = root.right;// 保存右子树
// 交换左右子树的位置
root.right = invertTree(root.left);
root.left = invertTree(rightTree);
return root;
}
}