# DFS
原来前/中/后序遍历都属于dfs啊 😃
对于二叉树的题目,其要么在从上往下的 "递" 过程进行判断,要么在从下往上的 "归" 过程操作
dfs模板
dfs(node){
fun(node);
dfs(node->left);
dfs(node->right);
ans=xx;
return ;
}
//二叉树dfs前序遍历的迭代实现
void dfs(root){
stk.push(root);
while(!stk.empty()){
cur=stk.top();
stk.pop();
fun(cur);
stk.push(cur->right);
stk.push(cur->left);
}
}
//中序
//优先向左访问,一直到空节点时,才从栈中取出一个节点并操作
//然后尝试向右访问,在向右访问时,访问一个节点,就再把它当作根节点再次操作
void dfs(root){
while(!stk.empty()&&cur){
while(cur){
stk.push(cur);
cur=cur->left;
}
cur=stk.top();
skt.pop();
cur=cur->right;
}
}
-
问题转化为寻找大于根节点的最大节点 ,主要用来学习lamda函数
考虑剪枝操作 ,因为下面的值是越来越大的,所以用当前值更新过后后面就没必要遍历了
//1.递归 ,PathSum(TreeNode* root, int targetSum) 表示从root出发是否有和为root的路径
只需判断是否有从left/right出发和为sum-node->val的路线
故递推式: PathSum(root,Sum)=Path(root->left,Sum-root->val)||Path(root->right,Sum-root->val)
//2.BFS
BFS 使用 队列 保存遍历到每个节点时的路径和,如果该节点恰好是叶子节点,并且 路径和 正好等于 sum,说明找到了解
//同理,考虑递归遍历与bfs
1.前序遍历
若当前节点为叶子节点, 将其和加到sum
否则 将当前值继续传递给叶子节点
2.bfs
//使用后序遍历 ,由后序遍历的那个图可知 ,每层最后一次访问的为最右侧结点
function<void(TreeNode*, int)> fun = [&](TreeNode* node, int depth) {
//利用后序遍历的那个图可知,每层最后一次遍历到的为最右边的节点
if (!node) return;
if (depth >= ans.size()) { // 动态扩展 ans
ans.resize(depth + 1);
}
fun(node->left, depth + 1);
fun(node->right, depth + 1);
ans[depth] = node->val;
};
//也可以使用 根右左的方法,每层第一个遍历到的是最右侧节点
function<void(TreeNode*, int)> fun = [&](TreeNode* node, int depth) {
//利用后序遍历的那个图可知,每层最后一次遍历到的为最右边的节点
if (!node) return;
if (depth == ans.size()) { // 动态扩展 ans
ans.push_back(node->val);
fun(node->right, depth + 1);
fun(node->left, depth + 1);
};
//BFS 记录路径上的最大值,比较即可
function<void(TreeNode*,int)> fun=[&](TreeNode* node,int lmax){
if(!node) return ;
if(node->val>=lmax) ans++;
lmax=max(lmax,node->val);
fun(node->left,lmax);
fun(node->right,lmax);
};
//很深刻的一题 ,对回溯的应用
1.对于伪回文的判断,要求元素的出现次数满足 0/1奇 多偶,因此只需要记录每条路上元素的出现情况即可
2.在记录路上的情况时,要注意回溯
dfs=[&](TreeNode* node){
if(!node) return ;
count[node->val]++;
if(!node->left&&!node->right) check();
dfs(node->left);
dfs(node->right);
count[node->val]--;//在遍历完左右子树后撤销对当前节点的遍历,使其在递归返回父节点时保持原样
};
非常无脑,dfs即可
string smallestFromLeaf(TreeNode* root){
//暴力写法,自顶向下遍历,记录当前节点到根节点的路径字符串,当遍历到叶子节点时,和答案比较即可
string ans="~";
function<void(TreeNode*,string)> dfs;
dfs=[&](TreeNode* node,string path){
//path:当前节点到根节点的路径
char ch=node->val+'a';
path=ch+path;
if(!node->left&&!node->right) ans=min(ans,path);
if(node->left) dfs(node->left,path);
if(node->right) dfs(node->right,path);
};
dfs(root,"");
return ans;
}
int maxAncestorDiff(TreeNode* root) {
//祖先:在同一条路径上 问题转化为找到同一条路径上的最大/小值,计算差值即可
int ans=0;
function<void(TreeNode*,int,int)> dfs;
dfs=[&](TreeNode* node,int mx,int mn){
if(!node) ans=max(ans,mx-mn);
mx=max(mx,node->val);
mn=min(mn,node->val);
dfs(node->left,mx,mn);
dfs(node->right,mx,mn);
};
dfs(root,0,root->val);
return ans;
}
bool isSameTree(TreeNode* p, TreeNode* q) {
//自上往下(dfs)和自下往上(递归)都可以
bool flag=true;
function<void(TreeNode*,TreeNode*)> dfs;
dfs=[&](TreeNode* node1,TreeNode* node2){
if(node1&&node2){//两节点值都存在时,比较节点值
if(node1->val!=node2->val) {flag=false;return ;}
dfs(node1->left,node2->left);
dfs(node1->right,node2->right);
}
if(!node1&&!node2) return ;//二者都为空节点时,不操作
flag=false; //只有一个节点存在,返回false
};
dfs(p,q);
return flag;
}
bool isSameTree(TreeNode* p, TreeNode* q) {//判断以p,q为根节点的两棵树是否相同
//采用递归实现
if(!p&&!q) return true;//均为空节点,true
if(!p&&q||!q&&p) return false;
if(p->val!=q->val) return false;//终止条件,当只有一个节点或节点值不同时,退出
return isSameTree(p->left,q->left)&&isSameTree(p->right,q->right);
}
bool isSymmetric(TreeNode* root) {
//自上向下遍历dfs(迭代)
bool flag=true;
function<void(TreeNode*,TreeNode*)> dfs;
dfs=[&](TreeNode* t1,TreeNode* t2){
//比较t1和t2是否轴对称
if(!t1||!t2) flag=(t1==t2);
if(t1->val!=t2->val) flag=false;//两个节点,判断是否相等
dfs(t1->left,t2->right);
dfs(t1->right,t2->left);
};
dfs(root->left,root->right);
return flag;
}
//递归,判断t1和t2两棵树是否对称
bool isSymmetric(TreeNode* t1,TreeNode* t2) {
if(!t1||!t2) return t1==t2;//当一个空节点时false,2个空节点时true,0个时不会进入该判断
if(t1->val!=t2->val) return false;//两个节点,判断是否相等
return isSymmetric(t1->left,t2->right)&&isSymmetric(t1->right,t2->left);
}
bool flipEquiv(TreeNode* root1, TreeNode* root2) {
//递归, 如果两棵树的左右子树等价,且这两棵树根节点相同,则这两棵树等价
if(!root1||!root2) return root1==root2;
if(root1->val!=root2->val) return false;//判断根节点是否相同
return flipEquiv(root1->left,root2->left)&&flipEquiv(root1->right,root2->right)||
flipEquiv(root1->left,root2->right)&&flipEquiv(root1->right,root2->left);
}
TreeNode* getTargetCopy(TreeNode* original, TreeNode* cloned, TreeNode* target) {
//两棵树同时遍历,比较original节点与target,二者相同时返回cloned,因为二者同步所以进行一次前序遍历即可
if(!original) return NULL;
if(original==target) return cloned;
TreeNode* lnode=getTargetCopy(original->left,cloned->left,target);
TreeNode* rnode=getTargetCopy(original->right,cloned->right,target);
return lnode==NULL?rnode:lnode;
}
/*一道非常有意思的题,巧妙地用返回的node判断root树的子树是否有1 同样巧妙的还有判断平衡二叉树,当不平衡时返回-1;*/
是否需要删除子树?--子树中没有1即可
跟上一题一样,最后的返回条件改一下即可,返回null表示该树需要删除,否则不需要删除
TreeNode* removeLeafNodes(TreeNode* root, int target) {
if(!root) return nullptr;
TreeNode* lnode=removeLeafNodes(root->left,target);
TreeNode* rnode=removeLeafNodes(root->right,target);
if(!lnode) root->left=nullptr;
if(!rnode) root->right=nullptr;
if(!root->left&&!root->right&&root->val==target) return nullptr;
//该节点为叶子且值为target时,返回null,表示该树需要删除
else return root;//否则返回root
}
如果当前节点被删除,那么就检查左/右孩子 是否被删除,如果没被删除,就加入答案
如果当前节点被删除,返回空节点,否则返回当前节点
# 回溯
每次执行完一次函数后,都会恢复到进入函数之前的状态,此乃回溯之法 ⏲️
//1.使用临时变量,表示当前路径0.上的字符串
vector<string> binaryTreePaths(TreeNode* root) {
vector<string> ans;
function<void(TreeNode*,string)> dfs;
dfs=[&](TreeNode* node,string path){
if(!node) return ;
path+=to_string(node->val);
if(!node->left&&!node->right){
ans.push_back(path);
return ;
}
path+="->";
dfs(node->left,path);
dfs(node->right,path);
};
dfs(root,"");
return ans;
}
//2.回溯,使用外部变量,记录当前的路径
dfs = [&](TreeNode* node) {
if (!node) return;
int old_len = path.size();
path += to_string(node->val);
if (!node->left && !node->right) {
ans.push_back(path);
path.resize(old_len);
return;
}
path += "->";
dfs(node->left);
dfs(node->right);
// 回溯
path.resize(old_len);//因为函数每执行一次就回溯一次,故回溯完的结果是上一层刚执行完的结果,即认为当前字符并未添加
};
-
vector<vector<int>> pathSum(TreeNode* root, int targetSum) { vector<int> path; vector<vector<int>> ans; int sum=0; function<void(TreeNode*)> dfs; dfs=[&](TreeNode* node){ if(!node) return ; sum+=node->val; path.push_back(node->val); if(!node->left&&!node->right&&sum==target) ans.push_back(path); dfs(node->left);//执行完不会影响path的值,因为其结束后会恢复到进入之前的状态 dfs(node->right); sum-=node->val;//每次执行完一次函数后,都会恢复到进入函数之前的状态,此乃回溯之法 path.pop_back(); }; dfs(root); return ans; }
//对于一般的二叉树的回溯, 其关键词在于 同一条路径
//利用前缀和记录这条路径上的根节点路径和
# 最近公共祖先
//关键在于找到 p,q,ans 三个点的关系 ,最终ans点一定满足 左右各含一个p/q;
//从下往上在回溯的过程中判断,故只能使用后序遍历,从子树中传递
TreeNode* fun(TreeNode* root,p,q) {
//判断root树中是否含有p/q/ans ,若有,则返回 p/q/ans
if(!root||root==p||root==q) return root;
lnode=fun(root->left,p,q); //在左子树中寻找
rnode=fun(root->right,p,q); //在右子树中寻找
if(lnode&&rnode) return root;//左右各一个,一定是答案点
if(lnode) return rnode;
return lnode;
}
//对于 搜索树
更改最终ans的判定条件即可 ans一定满足 p<root<q 且ans
TreeNode* fun(TreeNode* root,p,q){
int cur=root->val;
if(cur>p->val&&cur>q->val) return fun(root->left,p,q);
if(cur<p->val&&cur<q->val) return fun(root->right,p,q);
return root;
}
bool isValidBST(TreeNode* root){
//"递"的思想,按照中序遍历,判断是否为递增即可
if(!isbst(root->left)) return false;
if(root->val<=pre) return false;
pre=root->val;//更新pre的值
return isbst(root->right);
}
bool isBST(TreeNode* root,int mn,int mx){
//前序遍历,根据bst的定义来
//按照前序遍历构建bst的过程来看,在添加每个节点时,其只需满足一定范围即可,故思路就是将这个范围逐渐往下传递,判断每个节点是否满足条件 若当前为左,其必须小于父节点,**且受到祖先的限制** 若为右,其必须大于父节点,**且受到祖先的限制**
if(!root) return true;
int cur=root->val;
return cur>mn&&cur<mx
&&isbst(root->left,mn,cur)
&&isbst(root->right,cur,mx);
}
# BFS
//每一层XXX ,此时que中存储一整行的元素,每次都处理一行
while(!que.empty()){
int n=que.siz();
for(int i=.;i<n;i++){
node=que.front();
que.pop();
if(node->left) que.push(node->left);
if(node->right) que.push(node->right);
}
}
//que每次存储一行元素,每次操作都处理一行而不是一个
如何判断下一层??---for循环执行完就表示当前层遍历完
/*方法1 bfs
记录当前访问的层数,若为奇数层就记录当前层的所有数值,反序赋值(利用栈与队列)
方法2 dfs
两颗 对称二叉树的思想,根左右和根右左 同时遍历恰好能得到对称位置,若当前层为奇数,就交换二者数值*/
dfs_swap=[&](TreeNode* node1,TreeNode* node2,int is_odd){
//对于对称节点node1和node2,若当前层为奇数就交换二者
if(!node1) return ;
if(is_odd){
int temp=node1->val;
node1->val=node2->val;
node2->val=temp;
}
dfs_swap(node1->left,node2->right,is_odd^1);
dfs_swap(node1->right,node2->left,is_odd^1);
};
# 二叉搜索树
bro觉得最
# 递归
判断当前节点是不是左叶子是无法判断的,必须要通过节点的父节点来判断其左孩子是不是左叶子
/*最小不平衡树的左右高度差>1,故可以

对于所有的不平衡二叉树,其一定会有这样的最小不平衡部分
/*对于一个节点,是无法通过它自身来判断其是否为左叶子的,所以只能通过父节点来判断其左孩子是否为叶子.
