当前位置:网站首页>最近公共祖先问题你真的学会了吗?
最近公共祖先问题你真的学会了吗?
2022-06-12 21:40:00 【一个山里的少年】
目录
搜索二叉树的最近公共祖先
1.对应letecode链接:
2.题目描述:
3.解题思路:
1.首先我们先判断根节点如果当前节点等于p或者q中的一个那么根节点就是p和q的最近公共祖先。
2.如果当前节点的值比p和q都要小那么就去左树上去找。
3.如果当前节点的值比p和q都要大那么就去右树上找
4.如果不是都大或者是都小的话说明当前节点就是p和q的最近公共祖先。
4.对应代码:
class Solution { public: TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) { TreeNode*cur=root; while(cur) { if(cur->val<p->val&&cur->val<q->val){ cur=cur->right; } else if(cur->val>p->val&&cur->val>q->val){ cur=cur->left; } else//说明cur是p和q的最近公共祖先 { return cur; } } return NULL; } };
最近公共祖先I
1.对应letecode链接:
2.题目描述:
3.解题思路:
首先我们先来看一个例子:
在上图中7和9的最近公共祖先是2.我们很直观的就看出来了,本题最简单的方法就是两个节点从自己出发一值往上走找到第一个相交的节点就是最近公共祖先。但是问题又来了本题中二叉树的节点当中没有父亲指针。是不是上面的方法就不行了呢?我们可以定义一个哈西表生key值对应的某个节点而val都应的是它的父亲,这样不就可以实现了吗?具体请看代码。
4.对应代码:
class Solution { public: TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) { unordered_map<TreeNode*,TreeNode*>parent;//key为某个节点,val为其父亲。父亲表 queue<TreeNode*>Q;//通过层序遍历生成父亲表 Q.push(root); parent[root]=nullptr; while(!Q.empty()){ auto node=Q.front(); Q.pop(); if(node->left){ Q.push(node->left); parent[node->left]=node; } if(node->right){ Q.push(node->right); parent[node->right]=node; } } // 父亲表生成完毕 set<TreeNode*>path; while(p){ path.insert(p); p=parent[p];//往上走 } while(!path.count(q)){ q=parent[q];//往上走; } return q; } };
方法二:递归后序遍历
1.从根节点开始找判断根节点是否是p和q之中的一个如果是说明最近公共祖先就是当前的根节点root.
2.如果根节点没有找到则递归去左树和右树上去找。
3.我们使用leftRet和rightRet来接收来自左子树和右子树的结果,如果leftRet和rightRet都不为空说明左边和右边各一个那么此时根节点就是最近公共祖先。否则肯定是两个节点一边一个。此时我们只需要将不为空的那个返回即可。
4.对应代码:
class Solution { public: TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) { if(!root||root==p||root==q){ return root; } TreeNode* pqInLeft=lowestCommonAncestor(root->left,p,q); TreeNode*pqInRight=lowestCommonAncestor(root->right,p,q); //左右都不为空说明左边和右边各有一个 if(pqInLeft&&pqInRight){ return root; } return pqInRight?pqInRight:pqInLeft; } };
最近公共祖先II
1.对应letecode链接:
2.题目描述
3.解题思路:
本题和上题的区别是本题p和q这两个节点有可能不在树上,就只有这一个区别。那么我们是不是只需要提前查找一个p和q这两个节点是否在树上如果不在直接返回nullptr。如果使用上题的逻辑就解决了。
4.对应代码:
class Solution { public: TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) { if(!Find(root,q)||!Find(root,p)){//只要p和q有一个没有在树上就说明没有最近公共祖先 return nullptr; } return _lowestCommonAncestor( root, p, q); } TreeNode* _lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q){ if(!root||root==p||root==q){ return root; } auto leftRet=_lowestCommonAncestor(root->left, p, q); auto rightRet=_lowestCommonAncestor(root->right, p, q); if(leftRet&&rightRet){ return root; } return leftRet?leftRet:rightRet; } bool Find(TreeNode*root,TreeNode*target){ if(root==nullptr){ return false; } if(root->val==target->val){//找到了 return true; } bool leftRet=Find(root->left, target); //左树找到了 if(leftRet){ return true; } bool rightRet=Find(root->right,target); //右树找到了 if(rightRet){ return true; } //左右都没找到 return false; } };
方法二:不检查节点是否存在
如果某个点x是p和q的祖先结点,情况1是x的左子树含有p和q或者右子树含有p和q,第二种情况是x本身就是p或q之一,而自己的左右子树中含有另外一个需要的值
dfs的过程的返回值的含义是以root为头结点的树中是否含有p或者q。dfs的是不断越来越深的遍历,ans更新的一定是祖先结点,随着深度加深最后更新的ans一定是最近公共祖先。
对应代码:
class Solution { public: TreeNode*ans=nullptr; TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) { process(root, p, q); return ans; } bool process(TreeNode*root,TreeNode*p,TreeNode*q){ if(root==nullptr){ return false; } bool leftRet=process(root->left,p,q); bool rightRet=process(root->right,p,q); if(leftRet&&rightRet){ ans=root; } //根节点的值有一个和root相等并且左子树和右子树要发现p和q其中的一个 if((root->val==p->val||root->val==q->val)&&(leftRet||rightRet)){ ans=root; } return p->val==root->val||q->val==root->val||leftRet||rightRet; } };
最近公共祖先III
1.对应letecode链接
2.题目描述:
3.解题思路:
本题和第二题介绍的第一种方法非常的相似我们可以使用它来解决这道题(在这里就不重复了),我们让两个节点都往上走找到第一个相交的节点。这不就是链表相交问题吗?
不太清楚的老铁可以看一下我的博客。
4.对应代码
class Solution { public: Node* lowestCommonAncestor(Node* p, Node * q) { Node*curP=p; Node*curQ=q; while(curP!=curQ){ curP=curP?curP->parent:q; curQ=curQ?curQ->parent:p; } return curP; } };
最近公共祖先IV
1.对应letecoede链接:
2.题目描述:
3.解题思路:
这题和上面这些题非常类似思路基本相同。依题意,nodes中的所有节点均存在于二叉树中
因而,{nodes中所有节点的最近公共祖先},可以弱化为{当前子树上的隶属于nodes中的所有节点的最近公共祖先},这样就无需考虑是否包含了nodes中的全部节点了。
1)若子树的root是nodes中的节点,则其必为该子树上隶属于nodes的所有节点的最近公共祖先。因为其本身就是该子树的根了(包含了当前子树的整个范围不能再往上了),且其自身隶属于nodes因而也不能再往下了(再往下就漏掉了root本身);
2)若root的左右子树上均存在nodes中的节点,则root就是最近公共祖先;
3)若nodes中的节点都存在于某个子树,则返回该子树上所有隶属于nodes中的节点的公共祖先即可;
4)若整个root子树上不存在nodes中的节点,则该子树上隶属于nodes中的节点的公共祖先就是nullptr。
4.对应代码:
class Solution { public: TreeNode* lowestCommonAncestor(TreeNode* root, vector<TreeNode*> &nodes) { if(root==nullptr){ return nullptr; } for(auto x:nodes) { if(x==root){ return root; } } TreeNode*leftRet=lowestCommonAncestor(root->left, nodes); TreeNode*righRet=lowestCommonAncestor(root->right, nodes); if(leftRet&&righRet){ return root; } return leftRet?leftRet:righRet; } };
边栏推荐
- How to implement a simple publish subscribe mode
- 六月集训(第12天) —— 链表
- 在同花顺开户证券安全吗,证券开户怎么开户流程
- Fill in the checklist & lt; int&gt; Have default values? [repeat] - fill list & lt; int&gt; with default values? [duplicate]
- SQL调优指南笔记10:Optimizer Statistics Concepts
- Jdbctemplate inserts and returns the primary key
- 同花顺能开户吗,在APP上可以直接开通券商安全吗
- Oracle LiveLabs实验:Introduction to Oracle Spatial Studio
- MySQL体系结构及基础管理(二)
- Ansible playbook和变量(二)
猜你喜欢
Risk control modeling X: Discussion on problems existing in traditional modeling methods and Exploration on improvement methods
Cookies and sessions
Recommended Chinese font in the code input box of Oracle SQL developer
SQL调优指南笔记16:Managing Historical Optimizer Statistics
GNS installation and configuration
makefile 的ifeq,filter,strip 简单使用
Turing prize winner: what should I pay attention to if I want to succeed in my academic career?
“Oracle数据库并行执行”技术白皮书读书笔记
JVisualVM初步使用
SQL调优指南笔记8:Optimizer Access Paths
随机推荐
jsonUtils
February 27th
drf 接收嵌套数据并创建对象, 解决:drf NOT NULL constraint failed
Icml2022 | galaxy: active learning of polarization map
Digital intelligence data depth | Bi goes down the altar? It's not that the market has declined, it's that the story has changed
PCB封装下载网站推荐及其详细使用方法
Design and practice of Hudi bucket index in byte skipping
User guide for JUC concurrency Toolkit
How do I create a daemon thread? And where to use it?
What is the difference between volatile variables and atomic variables?
Oracle SQL Developer的代码输入框中推荐使用的中文字体
Semester summary of freshman year
Oracle 19C installation documentation
Zip compression decompression
SQL tuning guide notes 10:optimizer statistics concepts
Principales étapes de la collecte des ordures à Zgc
六月集训(第11天) —— 矩阵
SQL tuning guide notes 18:analyzing statistics using optimizer statistics Advisor
zgc 并发标识和并发转移阶段的多视图地址映射
JUC并发工具包使用指南