文章目录
- 树
- 94. 二叉树的中序遍历
- 95. 不同的二叉搜索树 ||
- 96.不同的二叉搜索树
- 98. 验证二叉搜索树
- 100. 相同的树
- 101. 对称二叉树
- 102. 二叉树的层序遍历
- 103. 二叉树的锯齿形层次遍历
- 104. 二叉树的最大深度
- 105. 从前序与中序遍历序列构造二叉树(经典)
- 106. 从中序与后序遍历序列构造二叉树
- 107.二叉树的层次遍历 ||
- 108. 将有序数组转换成二叉搜索树
- 109.有序链表转换成二叉搜索树
- 110.平衡二叉树
- 111. 二叉树的最小深度
- 112. 路径总和
- 113. 路径总和 ||
- 114. 二叉树展开为链表
- 116. 填充每个节点的下一个右侧节点指针
- 117. 填充每个节点的下一个右侧节点指针 ||
- 124. 二叉树中的最大路径和
- 129. 求根到叶子节点数之和
- 144. 二叉树的前序遍历
- 145. 二叉树的后序遍历
- 173. 二叉搜索树迭代器
- 199. 二叉树的右视图
- 222. 完全二叉树的节点个数
- 226. 翻转二叉树
- 230. 二叉搜索数种第k小的元素
- 236 & 235. 二叉树的最近公共祖先
- 257. 二叉树的所有路径
树
Leetcode上面关于树类型的题总结
94. 二叉树的中序遍历
二叉树的遍历
二叉树的前序遍历是根左右,中序遍历是左根右,后序遍历是左右根,一般前面三种遍历都是递归写法,也可以用迭代。层序遍历是一层一层的遍历,需要利用数据结构队列。
递归写法
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
public:
vector<int> ans;
vector<int> inorderTraversal(TreeNode* root) {
dfs(root);
return ans;
}
void dfs(TreeNode* root){
if (!root) return;
dfs(root->left);
ans.push_back(root->val);
dfs(root->right);
}
};
非递归写法
- 遇到一个节点,就把它压栈,并去遍历它的左子树
- 当左子树遍历结束后,从栈顶弹出这个结点并访问它
- 然后按其右指针再去中序遍历该结点的右子树
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
public:
vector<int> inorderTraversal(TreeNode* root) {
vector<int> res;
stack<TreeNode*> stk;
//终止条件,root为空并且栈空
while (root || stk.size()){
//遇到节点,压栈并去遍历其左子树
while (root){
stk.push(root);
root = root->left;
}
//左子树遍历结束,栈顶弹出并访问
if (stk.size()){
root = stk.top();
stk.pop();
res.push_back(root->val);
//按其右指针继续去中序遍历
root = root->right;
}
}
return res;
}
};
95. 不同的二叉搜索树 ||
利用二叉搜索树中序遍历有序性
我们可以得出一个有序的序列中,如果把该序列当成二叉搜索树,那么对于如果我们把该序列的任何一个点看成根节点的话,在该点左边的数,在二叉搜索树里面就是该点的左子树,在该点左边的数,在二叉搜索树里面就是该点的右子树。再将问题细小化,左边的序列再去枚举…子问题的划分,这不是递归吗?
时间复杂度
证明略,该时间复杂度是指数级别。
代码:
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
public:
vector<TreeNode*> generateTrees(int n) {
//特判n=0
if (!n) return {
};
return dfs(1, n);
}
vector<TreeNode*> dfs(int l , int r){
if (l > r) return {
NULL};//终止条件
vector<TreeNode*> res;
//枚举[l,r]中每个点作为根节点
for (int i = l ; i <= r ; i ++){
auto left = dfs(l, i - 1) , right = dfs(i + 1, r);
//将左子树节点和右子树节点拼接
for (auto l : left){
for (auto r : right){
auto root = new TreeNode(i);
root->left = l , root->right = r;
res.push_back(root);
}
}
}
return res;
}
};
96.不同的二叉搜索树
二叉搜索树中序遍历有序性
该题和95题是一个题,该题是求方案数,95题是求所有方案
即序列个数相同组成的二叉搜索树方案数相同
和上题一样,我们可以枚举序列的每个点作为根结点,左子树和右子树能够组成多少方案,由于有了上面的第二个性质,即相同序列组成的二叉搜索树方案相同,我们可以用动态规划的思想来求。
状态表示: f[i]表示i个结点组成二叉搜索树的方案数。
状态方程 f[i] = f[i - 1 - l + 1] * f[r - i - 1 + 1]
递归公式:
时间复杂度O(N2),空间复杂度O(N)
代码:
class Solution {
public:
int numTrees(int n) {
vector<int> f(n + 1);
f[0] = 1;
for (int i = 1 ; i <= n ; i++)
for (int j = 1 ; j <= i ; j ++)
f[i] += f[j - 1] * f[i - j];
return f[n];
}
};
98. 验证二叉搜索树
二叉搜索树中序遍历有序性
求得二叉搜索树的中序遍历的结果,再去验证该结果是否是有序的。
时间复杂度O(N) , 空间复杂度O(N)
代码:
/**
* 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<int> in_order;//记录中序遍历结果
bool isValidBST(TreeNode* root) {
//特判树空
if (!root) return true;
//求树的中序遍历
dfs(root);
//验证中序遍历序列是否有序
for (int i = 0 ; i < in_order.size() ; i ++)
if (i && in_order[i - 1] >= in_order[i])
return false;
return true;
}
void dfs(TreeNode* root){
if (!root) return ;
dfs(root->left);
in_order.push_back(root->val);
dfs(root->right);
}
};
中序遍历中验证是否满足二叉搜索树的性质
- 节点的左子树只包含小于当前节点的数。
- 节点的右子树只包含大于当前节点的数。
- 所有左子树和右子树自身必须也是二叉搜索树。
在遍历过程中求一下每个子树的最小值和最大值即可。对于左子树来说,要求得最大值,对于右子树来说,要求最小值。
时间复杂度O(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:
bool isValidBST(TreeNode* root) {
//特判空树
if (!root) return true;
int maxt, mint;
return dfs(root, maxt, mint);
}
bool dfs(TreeNode* root, int &maxt, int &mint)
{
maxt = mint = root->val;
//验证左子树
if (root->left)
{
int lmax, lmin;
if (!dfs(root->left, lmax, lmin))
return false;
if (lmax >= root->val)
return false;
maxt = max(maxt, lmax);
mint = min(mint, lmin);
}
//验证右子树
if (root->right)
{
int rmax, rmin;
if (!dfs(root->right, rmax, rmin))
return false;
if (rmin <= root->val)
return false;
maxt = max(maxt, rmax);
mint = min(mint, rmin);
}
return true;
}
};
100. 相同的树
递归
对于两棵树来说,不仅要结构相同,还要各个结点相同。
使用递归求解
代码:
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
public:
bool isSameTree(TreeNode* p, TreeNode* q) {
//树都空
if (!q && !p) return true;
//树其中一个不空
if (!q || !p) return false;
//当前节点值不同
if (q->val != p->val) return false;
//左子树和右子树的判断
return isSameTree(p->left, q->left) && isSameTree(p->right , q->right);
}
};
101. 对称二叉树
解题
和100题差不多
我们需要先判断根节点,再去看左子树和右子树的情况。
- 左子树的结点和右子树结点都空,说明对称
- 左子树和右子树其中一个空,说明不对称
- 左子树和右子树结点的值不同,说明不对称
- 递归的去看左子树的左边和右子树的右边 并且 递归的去看右子树的左边和左子树的右边
时间复杂度O(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:
bool isSymmetric(TreeNode* root) {
//空树
if (!root) return true;
return dfs(root->left, root->right);
}
bool dfs(TreeNode* t1, TreeNode* t2){
//左子树结点和右子树结点都空
if (!t1 && !t2) return true;
//其中一个不空
if (!t1 || !t2) return false;
//结点值不同
if (t1->val != t2->val) return false;
//递归左子树,右子树
return dfs(t1->left, t2->right