leetcode 经典二叉树算法题目(思路、方法、code)
二叉树相关题目,主要就是要搞定二叉树的多种遍历方式,包括先序遍历,中序遍历,后序遍历,层次遍历,DFS遍历。掌握了所有的遍历方法,二叉树的题目基本就化简为一些简单问题去解决。
二叉树由于其良好的结构,很多题目都可以化为递归去解决。
文章目录
-
-
- leetcode 经典二叉树算法题目(思路、方法、code)
-
- [226. 翻转二叉树](https://leetcode-cn.com/problems/invert-binary-tree/)
- [112. 路径总和](https://leetcode-cn.com/problems/path-sum/)
- [113. 路径总和 II](https://leetcode-cn.com/problems/path-sum-ii/)
- [236. 二叉树的最近公共祖先](https://leetcode-cn.com/problems/lowest-common-ancestor-of-a-binary-tree/)
- [114. 二叉树展开为链表](https://leetcode-cn.com/problems/flatten-binary-tree-to-linked-list/)
- [102. 二叉树的层序遍历](https://leetcode-cn.com/problems/binary-tree-level-order-traversal/)
- [144. 二叉树的前序遍历](https://leetcode-cn.com/problems/binary-tree-preorder-traversal/)
- [199. 二叉树的右视图](https://leetcode-cn.com/problems/binary-tree-right-side-view/)
- [105. 从前序与中序遍历序列构造二叉树](https://leetcode-cn.com/problems/construct-binary-tree-from-preorder-and-inorder-traversal/)
- [101. 对称二叉树](https://leetcode-cn.com/problems/symmetric-tree/)
- [104. 二叉树的最大深度](https://leetcode-cn.com/problems/maximum-depth-of-binary-tree/)
- [111. 二叉树的最小深度](https://leetcode-cn.com/problems/minimum-depth-of-binary-tree/)
-
二叉树的常用结构体定义:
struct TreeNode {
int val;
TreeNode *left;
TreeNode *right;
TreeNode(int x) : val(x), left(NULL), right(NULL) {
}
};
226. 翻转二叉树
翻转一棵二叉树。
示例:
输入:
4
/ \
2 7
/ \ / \
1 3 6 9
输出:
4
/ \
7 2
/ \ / \
9 6 3 1
分析:翻转二叉树是非常经典的一个问题。
方法一:递归
- 如果一个树是空的,则翻转的结果仍为空
- 如果一个树非空,则翻转的结果应该为,翻转后的树的左子树应该是原来右子树经过翻转得到的,翻转后的树的右子树应该是右原来的左子树翻转得到的
- 根据该递归规则进行递归即可
class Solution {
public:
TreeNode* invertTree(TreeNode* root) {
if(root==NULL) return NULL;
TreeNode* temp=root->left; //注意需要一个中间变量
root->left=invertTree(root->right);//这里左子树将发生变化,故之前需要中间变量保存下来原来的左子树
root->right=invertTree(temp);
return root;
}
};
方法二:迭代
迭代法的主要思想是,我们需要遍历树中所有节点,然后将每个节点的两个子树进行调换。可以用BFS的方法遍历树中节点,并将未访问的节点存放在queue中,也可以用先序遍历等(但非递归地遍历还是BFS比较容易写)
class Solution {
public:
TreeNode* invertTree(TreeNode* root) {
if(root==NULL) return NULL;
queue<TreeNode*> queue;
queue.push(root);
while(!queue.empty()) //队列非空则一直进行
{
TreeNode* first=queue.front();
TreeNode* temp=first->left; //交换左右子树
first->left=first->right;
first->right=temp;
if(first->left!=NULL) queue.push(first->left); //子树非空则添加进队列
if(first->right!=NULL) queue.push(first->right);
queue.pop(); //将队列头pop出去
}
return root;
}
};
112. 路径总和
给定一个二叉树和一个目标和,判断该树中是否存在根节点到叶子节点的路径,这条路径上所有节点值相加等于目标和。
说明: 叶子节点是指没有子节点的节点。
示例:
给定如下二叉树,以及目标和 sum = 22,
5
/ \
4 8
/ / \
11 13 4
/ \ \
7 2 1
返回 true, 因为存在目标和为 22 的根节点到叶子节点的路径 5->4->11->2
分析:解决该问题,我们需要知道,如何将问题化为子问题,如何判断一个节点是叶节点,当遍历到叶节点时如何判断。
两个子树都为空,则为叶结点。 如果是递归处理该问题,则主要判断当前的值是否与sum一致即可。
- 如果该节点为空,则返回false
- 否则判断该节点的是否是叶子节点,如果是叶子节点并且该值等于sum,则返回true
- 否则向下遍历,等于两个子节点对该问题的或,当然子问题处理时 s u m − v a l sum-val sum−val,
class Solution {
public:
bool is_leaf(TreeNode* root)
{
if(root->left==NULL&&root->right==NULL)
return true;
else
return false;
}
bool hasPathSum(TreeNode* root, int sum)
{
if(root==NULL) return false;
if(root->left==NULL&&root->right==NULL&&root->val==sum) //是叶节点并且值为sum,则返回true
return true;
return hasPathSum(root->left,sum-root->val)||hasPathSum(root->right,sum-root->val);
}
};
113. 路径总和 II
给定一个二叉树和一个目标和,找到所有从根节点到叶子节点路径总和等于给定目标和的路径。
说明: 叶子节点是指没有子节点的节点。
示例:
给定如下二叉树,以及目标和 sum = 22,
5
/ \
4 8
/ / \
11 13 4
/ \ / \
7 2 5 1
返回:
[
[5,4,11,2],
[5,8,4,5]
]
分析:该问题需要给出路径,因此回溯法是非常可行的一种方法。
用回溯法经典套路:
- 每次保存已经走过的路径
- 如果到达根节点则判断是否符合,如果不符合则回溯过去,如果符合则将答案存取起来
- 每次调用递归后,需要将状态恢复
class Solution {
public:
vector<vector<int>> result;
bool is_leaf(TreeNode* root)
{
return (root->left==NULL&&root->right==NULL);
}
vector<vector<int>> pathSum(TreeNode* root, int sum)
{
if(root==NULL) return result;
vector<int> road;
backtrack(root,sum,road);
return result;
}
void backtrack(TreeNode* node,int sum,vector<int