一. 二叉树天然的递归结构
给定一个二叉树,找出其最大深度。
二叉树的深度为根节点到最远叶子节点的最长路径上的节点数。
说明: 叶子节点是指没有子节点的节点。
示例:
给定二叉树 [3,9,20,null,null,15,7],
3
/ \
9 20
/ \
15 7
返回它的最大深度 3 。
解题
#include <iostream>
using namespace std;
// 104. Maximum Depth of Binary Tree
// https://leetcode.com/problems/maximum-depth-of-binary-tree/description/
// 时间复杂度: O(n), n是树中的节点个数
// 空间复杂度: O(h), h是树的高度
/// Definition for a binary tree node.
struct TreeNode {
int val;
TreeNode *left;
TreeNode *right;
TreeNode(int x) : val(x), left(NULL), right(NULL) {}
};
class Solution {
public:
int maxDepth(TreeNode* root) {
if(root == NULL)
return 0;
return 1 + max(maxDepth(root->left), maxDepth(root->right));
}
};
二. 一个简单的二叉树问题引发的血案 Invert Binary Tree
翻转一棵二叉树。
示例:
输入:
4
/ \
2 7
/ \ / \
1 3 6 9
输出:
4
/ \
7 2
/ \ / \
9 6 3 1
备注:
这个问题是受到 Max Howell 的 原问题 启发的 :
谷歌:我们90%的工程师使用您编写的软件(Homebrew),但是您却无法在面试时在白板上写出翻转二叉树这道题,这太糟糕了。
解题
#include <iostream>
using namespace std;
/// 226. Invert Binary Tree
/// https://leetcode.com/problems/invert-binary-tree/description/
/// 时间复杂度: O(n), n为树中节点个数
/// 空间复杂度: O(h), h为树的高度
/// Definition for a binary tree node.
struct TreeNode {
int val;
TreeNode *left;
TreeNode *right;
TreeNode(int x) : val(x), left(NULL), right(NULL) {}
};
class Solution {
public:
TreeNode* invertTree(TreeNode* root) {
if(root == NULL)
return NULL;
invertTree(root->left);
invertTree(root->right);
swap(root->left, root->right);
return root;
}
};
三. 注意递归终止条件 Path Sum
给定一个二叉树和一个目标和,判断该树中是否存在根节点到叶子节点的路径,这条路径上所有节点值相加等于目标和。
说明: 叶子节点是指没有子节点的节点。
示例:
给定如下二叉树,以及目标和 sum = 22,
5
/ \
4 8
/ / \
11 13 4
/ \ \
7 2 1
返回 true, 因为存在目标和为 22 的根节点到叶子节点的路径 5->4->11->2。
解题
#include <iostream>
using namespace std;
/// Definition for a binary tree node.
struct TreeNode {
int val;
TreeNode *left;
TreeNode *right;
TreeNode(int x) : val(x), left(NULL), right(NULL) {}
};
/// 112. Path Sum
/// https://leetcode.com/problems/path-sum/description/
/// 时间复杂度: O(n), n为树的节点个数
/// 空间复杂度: O(h), h为树的高度
class Solution {
public:
bool hasPathSum(TreeNode* root, int sum) {
if(root == NULL)
return sum == 0;
if(hasPathSum(root->left, sum - root->val))
return true;
if(hasPathSum(root->right, sum - root->val))
return true;
return false;
}
};
错误原因
这样的例子:
sum=5
5
\
8
/ \
13 4
带入上面的代码中, 会得到true 即 5 这条路径
但问题是, 5不是叶子节点, 不符合题意
正确的结果应该是 如果得到的路径是 5 单一个根节点, 返回false
递归终止条件 位置不对, 必须把它放在叶子结点中 判断
正确解法
#include <iostream>
using namespace std;
/// Definition for a binary tree node.
struct TreeNode {
int val;
TreeNode *left;
TreeNode *right;
TreeNode(int x) : val(x), left(NULL), right(NULL) {}
};
/// 112. Path Sum
/// https://leetcode.com/problems/path-sum/description/
/// 时间复杂度: O(n), n为树的节点个数
/// 空间复杂度: O(h), h为树的高度
class Solution {
public:
bool hasPathSum(TreeNode* root, int sum) {
if(root == NULL)
return false;
if(root->left == NULL && root->right == NULL)
return sum == root->val; // 递归终止条件 放在叶子结点中
return hasPathSum(root->left, sum - root->val)
|| hasPathSum(root->right, sum - root->val);
}
};
四. 定义递归问题 Binary Tree Path
给定一个二叉树,返回所有从根节点到叶子节点的路径。
说明: 叶子节点是指没有子节点的节点。
示例:
输入:
1
/ \
2 3
\
5
输出: ["1->2->5", "1->3"]
解释: 所有根节点到叶子节点的路径为: 1->2->5, 1->3
解题
- 叶子节点 和 非叶子结点 的逻辑是不一样的
- res 对与叶子结点是 存放 单个字符串, 而对于非叶子结点, res存放多个字符串
#include <iostream>
#include <string>
#include <vector>
using namespace std;
/// 257. Binary Tree Paths
/// https://leetcode.com/problems/binary-tree-paths/description/
/// 时间复杂度: O(n), n为树中的节点个数
/// 空间复杂度: O(h), h为树的高度
/// Definition for a binary tree node.
struct TreeNode {
int val;
TreeNode *left;
TreeNode *right;
TreeNode(int x) : val(x), left(NULL), right(NULL) {}
};
class Solution {
public:
vector<string> binaryTreePaths(TreeNode* root) {
vector<string> res;
if(root == NULL)
return res;
if(root->left == NULL && root->right == NULL){ // 是否叶子结点
res.push_back(to_string(root->val));
return res;
}
// 非叶子结点
vector<string> leftPaths = binaryTreePaths(root->left);
for(int i = 0 ; i < leftPaths.size() ; i ++)
res.push_back(to_string(root->val) + "->" + leftPaths[i]);
vector<string> rightPaths = binaryTreePaths(root->right);
for(int i = 0 ; i < rightPaths.size() ; i ++)
res.push_back(to_string(root->val) + "->" + rightPaths[i]);
return res;
}
};
五. 稍复杂的递归逻辑 Path Sum III
给定一个二叉树,它的每个结点都存放着一个整数值。
找出路径和等于给定数值的路径总数。
路径不需要从根节点开始,也不需要在叶子节点结束,但是路径方向必须是向下的(只能从父节点到子节点)。
二叉树不超过1000个节点,且节点数值范围是 [-1000000,1000000] 的整数。
示例:
root = [10,5,-3,3,2,null,11,3,-2,null,1], sum = 8
10
/ \
5 -3
/ \ \
3 2 11
/ \ \
3 -2 1
返回 3。和等于 8 的路径有:
1. 5 -> 3
2. 5 -> 2 -> 1
3. -3 -> 11
解题思路
- 关键在于 路径不需要从根节点开始
- 把每一个子树, 也当成独立的树, 去寻找路径
#include <iostream>
using namespace std;
/// 437. Path Sum III
/// https://leetcode.com/problems/path-sum-iii/description/
/// 时间复杂度: O(n), n为树的节点个数
/// 空间复杂度: O(h), h为树的高度
/// Definition for a binary tree node.
struct TreeNode {
int val;
TreeNode *left;
TreeNode *right;
TreeNode(int x) : val(x), left(NULL), right(NULL) {}
};
class Solution {
public:
// 在以root为根节点的二叉树中,寻找和为sum的路径,返回这样的路径个数
int pathSum(TreeNode* root, int sum) {
if(root == NULL)
return 0;
return findPath(root, sum)
+ pathSum(root->left , sum)
+ pathSum(root->right , sum);
}
private:
// 在以node为根节点的二叉树中,寻找包含node的路径,和为sum
// 返回这样的路径个数
int findPath(TreeNode* node, int num){
if(node == NULL)
return 0;
int res = 0;
if(node->val == num)
res += 1;
res += findPath(node->left , num - node->val);
res += findPath(node->right , num - node->val);
return res;
}
};
六. 二分搜索树中的问题 Lowest Common Ancestor of a Binary Search Tree
给定一个二叉搜索树, 找到该树中两个指定节点的最近公共祖先。
百度百科中最近公共祖先的定义为:“对于有根树 T 的两个结点 p、q,最近公共祖先表示为一个结点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。”
例如,给定如下二叉搜索树: root = [6,2,8,0,4,7,9,null,null,3,5]
_______6______
/ \
___2__ ___8__
/ \ / \
0 _4 7 9
/ \
3 5
示例 1:
输入: root = [6,2,8,0,4,7,9,null,null,3,5], p = 2, q = 8
输出: 6
解释: 节点 2 和节点 8 的最近公共祖先是 6。
示例 2:
输入: root = [6,2,8,0,4,7,9,null,null,3,5], p = 2, q = 4
输出: 2
解释: 节点 2 和节点 4 的最近公共祖先是 2, 因为根据定义最近公共祖先节点可以为节点本身。
说明:
所有节点的值都是唯一的。
p、q 为不同节点且均存在于给定的二叉搜索树中。
解题思路
- 假设 p <= q
- 从root根节点开始向下, 找到 一个节点, 满足 p <= 该节点 <= q
- 这样的节点就是 p 和 q 的最近公共祖先
#include <iostream>
#include <cassert>
using namespace std;
/// 235. Lowest Common Ancestor of a Binary Search Tree
/// https://leetcode.com/problems/lowest-common-ancestor-of-a-binary-search-tree/description/
/// 时间复杂度: O(lgn), 其中n为树的节点个数
/// 空间复杂度: O(h), 其中h为树的高度
/// Definition for a binary tree node.
struct TreeNode {
int val;
TreeNode *left;
TreeNode *right;
TreeNode(int x) : val(x), left(NULL), right(NULL) {}
};
class Solution {
public:
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
assert(p != NULL && q != NULL);
if(root == NULL)
return NULL;
if(p->val < root->val && q->val < root->val)
return lowestCommonAncestor(root->left, p, q);
if(p->val > root->val && q->val > root->val)
return lowestCommonAncestor(root->right, p, q);
assert(p->val == root->val || q->val == root->val
|| (root->val - p->val) * (root->val - q->val) < 0);
return root;
}
};