1.二叉树基本知识
LeetCode中二叉树结构体:
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
一颗二叉树的结构对称性和传递性太强了,天然的适用于递归调用的方法,例如dfs~
2.递归调用类题目-路径相关
104. 二叉树的最大深度(剑指 Offer 55 - I. 二叉树的深度)
class Solution {
public:
/*
1.dfs意义:返回以该节点为根结点的深度
2.对每个节点来说,所有可能的返回情况:
节点为正常节点:返回左右子节点的最大深度+1:会包括以下两种情况
节点为空节点:返回0
节点为叶子节点:返回1
*/
int maxDepth(TreeNode* root) {
if(!root) return 0;//节点为空的情况返回0
if(!root->left && !root->right) return 1;//叶子节点时返回1
return max(maxDepth(root->left), maxDepth(root->right)) + 1;
}
};
124. 二叉树中的最大路径和
/*
1.dfs意义:返回以该节点为根结点(最高节点)的最大单边路径(包含这个节点):如果<=0就返回0,也就是不取这个支路,否则返回上去后反倒更小
2.res存全局的变量:每次dfs一个点就算一下以当前点为最高节点的子树:left+right+root,取max
*/
class Solution {
public:
int res = INT_MIN;
int maxPathSum(TreeNode* root) {
dfs(root);
return res;
}
int dfs(TreeNode* root)
{
if(!root) return 0;
int left = dfs(root->left);
int right = dfs(root->right);
res = max(res, left + right + root->val);//记录全局max
return max(max(left, right) + root->val, 0);//
}
};
543. 二叉树的直径
class Solution {
public:
int ans = 0;
int diameterOfBinaryTree(TreeNode* root) {
//直径:最长的路径
dfs(root);
return ans;
}
//dfs需要记录答案,并且返回往下的最大深度
int dfs(TreeNode* root)
{
if(!root) return 0;
auto left = dfs(root->left);
auto right = dfs(root->right);
ans = max(ans,left+right);//当前最高点的最大值是左+右,ans记录全局最高点的max
return max(left+1,right+1);
}
};
剑指 Offer 55 - II. 平衡二叉树
class Solution {
public:
bool ans = true;
bool isBalanced(TreeNode* root) {
dfs(root);
return ans;
}
int dfs(TreeNode* root)
{
if(!root) return 0;
int left = dfs(root->left), right = dfs(root->right);
if(abs(left-right)>1) ans = false;
return (max(left,right) + 1);
}
};
剑指 Offer 34. 二叉树中和为某一值的路径
/*
带回溯的dfs
*/
class Solution {
public:
vector<vector<int>> res;
vector<int> path;
int s;
vector<vector<int>> pathSum(TreeNode* root, int sum) {
s = sum;
dfs(root,0);//当前和,也可以直接传sum,然后做减法,判断是不是==0
return res;
}
void dfs(TreeNode* root, int tmp)
{
if(!root) return ;//剪枝
path.push_back(root->val);
tmp += root->val;
if(!root->left && !root->right && tmp == s) res.push_back(path);//剪枝
dfs(root->left, tmp);//选择范围
dfs(root->right, tmp);
path.pop_back();//回溯
}
};
3.递归调用类题目-子结构相关
剑指 Offer 26. 树的子结构
class Solution {
public:
/*
其实和矩形中找单词一样,先遍历根结点
*/
bool isSubStructure(TreeNode* A, TreeNode* B) {
//遍历A树,挨个判断ai和bi为根结点的树是否相等
if(!A || !B) return false;
if(dfs(A,B)) return true;
return isSubStructure(A->left,B) || isSubStructure(A->right,B);
}
bool dfs(TreeNode* p1, TreeNode* p2)
{
if(!p2) return true;//p2为空就true
if(!p1) return false;//p2不为空但是p1为空,false
if(p1->val == p2->val)
return dfs(p1->left,p2->left) && dfs(p1->right, p2->right);
return false ;
}
};
剑指 Offer 28. 对称的二叉树
这道只有叶子都为空才返回true,上一道p2为空就表示可以了
/*
dfs意义:以p1和p2为根结点的子树是否对称
*/
class Solution {
public:
bool isSymmetric(TreeNode* root) {
if(!root) return true;
//除了根结点以外,都是左右=右左
return dfs(root->left,root->right);//这里只代表根结点的左右
}
bool dfs(TreeNode* p,TreeNode* q)//判断pq为根结点的是否对称
{
if(!p || !q) return !p && !q;//只有两个节点都为空时才返回true
if(p->val != q->val) return false;
return dfs(p->left, q->right) && dfs(p->right, q->left);
}
};
剑指 Offer 27. 二叉树的镜像(226. 翻转二叉树)
/*
dfs函数意义:把该节点为根结点的左右节点交换
*/
class Solution {
public:
TreeNode* mirrorTree(TreeNode* root) {
if(!root) return root;
auto t = mirrorTree(root->left);
auto p = mirrorTree(root->right);
swap(root->left, root->right);//叶子节点其实直接执行这一步
return root;
}
};
236. 二叉树的最近公共祖先
class Solution {
public:
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
//到达叶节点下,返回null
if(!root) return NULL;
//如果找到了p或者q返回该节点
if( root == p || root == q) return root;//无论p下是不是有q,都应该返回q
//递归左右,返回找到了的节点或者null
auto left = lowestCommonAncestor(root->left,p,q);
auto right = lowestCommonAncestor(root->right,p,q);
//pq分布在两边,返回根结点
if(left && right) return root;
//找到了其中一个节点:p或者q,返回这个节点
if(left) return left;
//返回另一个节点,顺便包含左右为没有的null的情况
return right;
}
};
129. 求根节点到叶节点数字之和
class Solution {
public:
int sum = 0;
int sumNumbers(TreeNode* root) {
int path = 0;
dfs(root, path);
return sum;
}
void dfs(TreeNode* root, int path)
{
if(!root) return;
path = path * 10 + root->val;
if(!root->left && ! root->right)
{
sum += path;
return;
}
dfs(root->left, path);
dfs(root->right, path);
//path -= root->val;
}
};
4.遍历二叉树
94. 二叉树的中序遍历
class Solution {
public:
vector<int> inorderTraversal(TreeNode* root) {
//递归其实是系统开了栈来存储,时间o(n),非递归自己手动写栈
stack<TreeNode*> stk;
vector<int> res;
auto p = root;
while(p || stk.size())
{
while(p)
{
stk.push(p);//先压入所有左子树
p = p->left;
}
p = stk.top();
stk.pop();
res.push_back(p->val);
p = p->right;//对弹出的点,压入右子树的所有左子树
}
return res;
}
};
144. 二叉树的前序遍历
class Solution {
public:
vector<int> preorderTraversal(TreeNode* root) {
vector<int> res;
stack<TreeNode*> stk;
TreeNode* p = root;
while(stk.size() || p)
{
while(p)
{
stk.push(p);
res.push_back(p->val);
p = p->left;
}
p = stk.top();
stk.pop();
p = p->right;
}
return res;
}
};
101. 对称二叉树
class Solution {
public:
bool isSymmetric(TreeNode* root) {
//迭代做法:左中右遍历左树,右中左遍历右树
if(!root) return true;
stack<TreeNode*> left,right;
auto l = root->left,r = root->right;
while(l || r || left.size() || right.size())
{
while(l && r)
{
left.push(l), right.push(r);
l = l->left, r = r->right;
}
if(l || r) return false;
l = left.top(), left.pop();
r = right.top(), right.pop();
if(l->val != r->val) return false;
l = l->right, r = r->left;//对每个弹出的节点,把它的左子树全部压入
}
return true;
}
};
剑指 Offer 32 - I. 从上到下打印二叉树
/*
队列先进先出:root入队,对每一层的节点,每遍历一个就把下一层的加入队列
*/
class Solution {
public:
vector<int> levelOrder(TreeNode* root) {
vector<int> res;
if(!root) return res;
queue<TreeNode*> q;//注意:是*
q.push(root);
while(!q.empty())
{
auto root = q.front();
if(root->left) q.push(root->left);
if(root->right) q.push(root->right);
res.push_back(q.front()->val);
q.pop();
}
return res;
}
};
剑指 Offer 32 - II. 从上到下打印二叉树 II
/*
遍历一层的时候,把下一层的节点单独存在另一个队列里;一层遍历完再交替
*/
class Solution {
public:
vector<vector<int>> levelOrder(TreeNode* root) {
vector<vector<int>> res;
vector<int> tmp;
if(!root) return res;
queue<TreeNode*> q;//注意:是*
queue<TreeNode*> nex, empty;
q.push(root);
while(!q.empty())
{
auto root = q.front();
if(root->left) nex.push(root->left);
if(root->right) nex.push(root->right);
tmp.push_back(q.front()->val);
q.pop();
if(q.empty())
{
q = nex;
nex = empty;
res.push_back(tmp);
tmp = {};
}
}
return res;
}
};
剑指 Offer 32 - III. 从上到下打印二叉树 III
/*
遍历一层的时候,把下一层的节点单独存在另一个队列里;一层遍历完再交替,交替的时候注意是否反转
*/
class Solution {
public:
vector<vector<int>> levelOrder(TreeNode* root) {
vector<vector<int>> res;
vector<int> tmp;
if(!root) return res;
queue<TreeNode*> q;//注意:是*
queue<TreeNode*> nex, empty;
q.push(root);
int t = 0;
while(!q.empty())
{
auto root = q.front();
if(root->left) nex.push(root->left);
if(root->right) nex.push(root->right);
tmp.push_back(q.front()->val);
q.pop();
if(q.empty())
{
q = nex;
nex = empty;
if(t % 2) reverse(tmp.begin(), tmp.end());//多了判断的地方
t ++;
res.push_back(tmp);
tmp = {};
}
}
return res;
}
};
114. 二叉树展开为链表
class Solution {
public:
void flatten(TreeNode* root) {
if(!root) return;
auto p = root;
while(p)
{
if(p->left)
{
auto tmp = p->right;
auto l = p->left;
p->left = nullptr;//1.注意要把left赋成空
p->right = l;//2.插入左端点
while(l->right) l = l->right;//3.找到左子树最右边的端点
l->right = tmp;//4.该端点的右端点指向以前的右端点
}
p = p->right;//继续遍历右端点
}
}
};
538. 把二叉搜索树转换为累加树
class Solution {
public:
int sum = 0;
TreeNode* convertBST(TreeNode* root) {
if(root != nullptr)
{
convertBST(root->right);
sum += root->val;
root->val = sum;//要修改每一个值
convertBST(root->left);
}
return root;
}
};
5.还原二叉树
105. 从前序与中序遍历序列构造二叉树(剑指 Offer 07. 重建二叉树)
class Solution {
public:
unordered_map<int, int> pos;//map直接在中序遍历中找到对应的下标
TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
int n = inorder.size();
for(int i = 0; i < n; i++) pos[inorder[i]] = i;
return dfs(preorder, inorder, 0, n-1, 0, n-1);
}
TreeNode* dfs(vector<int>& preorder, vector<int>& inorder, int pl, int pr, int li, int lr)
{
if(pl > pr) return NULL;
int val = preorder[pl];
auto root = new TreeNode(val);
int index = pos[val];
auto left = dfs(preorder, inorder, pl + 1,index - li + pl, li, index - 1);
auto right = dfs(preorder,inorder,index-li+pl+1,pr,index+1,lr);
root->left = left, root->right = right;
return root;
}
};
剑指 Offer 33. 二叉搜索树的后序遍历序列
class Solution {
public:
vector<int> seq;
bool verifyPostorder(vector<int>& postorder) {
//二叉搜索树:左边都小,右边都大
seq = postorder;
return dfs(0, seq.size() - 1);
}
bool dfs(int l, int r)
{
if(l >= r) return true;
int root = seq[r];//最后一个点是根结点,
int k = l;
while(k < r && seq[k] < root) k++;//找到右子树第一个点
for(int i = k; i < r; i ++) //右子树如果有比根结点小的就不对
if(seq[i] < root)
return false;
//否则当前这层合法
//cout<<l<<" "<<k<<" "<<r<<" "<<endl;
return dfs(l, k - 1) && dfs(k, r - 1);//判断左子树和右子树
}
};
6.其余二叉搜索树类
98. 验证二叉搜索树
class Solution {
public:
bool isValidBST(TreeNode* root) {
return dfs(root, INT_MIN, INT_MAX);
}
bool dfs(TreeNode* root, long long minv, long long maxv)
{
if(!root) return true;//root为空
if(root->val < minv || root->val > maxv) return false;//root越界,当前区间是[minv,maxv]
//注意递归的区间,传入的是可以相等的,所以左子树要<当前root的val,val可能是负无穷,减1会溢出,所以用long long的1
return dfs(root->left, minv, root->val-1ll) && dfs(root->right, root->val+1ll, maxv);//递归判断左右子树范围
}
};
剑指 Offer 68 - I. 二叉搜索树的最近公共祖先
class Solution {
public:
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
if(root->val < p->val && root->val < q->val)
return lowestCommonAncestor(root->right, p, q);
if(root->val > p->val && root->val > q->val)
return lowestCommonAncestor(root->left,p,q);
return root;
}
};
剑指 Offer 54. 二叉搜索树的第k大节点
class Solution {
public:
TreeNode* ans;
int kthLargest(TreeNode* root, int k) {
//按照中序遍历,传入k即可
dfs(root, k);
return ans->val;
}
void dfs(TreeNode* root,int &k)//k一定要是全局的,才能一直减所以是&!!!
{
if(!root) return;
dfs(root->right, k);
k --;//直到遍历到右下角的叶节点才第一次--
if(!k) ans = root;//减完就判断
dfs(root->left, k);
}
};
96. 不同的二叉搜索树
class Solution {
public:
int numTrees(int n) {
//o(n^2),因为n个值需要求解,每次求解需要o(n)复杂度
vector<int> f(n + 1, 0);
f[0] = 1;
f[1] = 1;
for(int i = 2; i <= n; i ++)//就是一纬的,每次是前面的组合
for(int j = 1; j <= i; j ++)
{
f[i] += f[j - 1] * f[i - j];
}
return f[n];
}
};