• 最近公共祖先(朴素法、倍增法、(递归法))


    目录

    一、前言

    二、题一:二叉树的最近公共祖先

    1、上链接

    2、基本思路

    (1)朴素法

    (2)LCA倍增法。

    3、朴素法代码

    (1)C++(AC)

    (2)python(29/31,时间超限了但算法正确性是可以保证的)

    4、LCA倍增法

    (1)C++(不提交,因为不想补全力扣给的入口函数,但是能保证算法正确性)

    5、其他递归法

    三、题二:【模板】最近公共祖先(LCA)

    1、上链接

    2、基本思路

    3、倍增法代码

    (1)C++ (AC)

    (2)python(70分)


    一、前言

    又是一个复习的算法题目,下面直接看题吧。

    二、题一:二叉树的最近公共祖先

    1、上链接

    236. 二叉树的最近公共祖先 - 力扣(Leetcode)

    2、基本思路

    有两种方法可解。

    (1)朴素法

    假设给定两个节点p,q,那么我们应该找出两条路径并存起来,路径是从根节点到给定节点,怎么找呢?利用栈进行递归查找即可。

    (2)LCA倍增法。

    为什么倍增法可行,因为一个整型数永远可以化成一个二进制数(二进制转十进制)。

    3、朴素法代码

    (1)C++(AC)

    顺便也复习了一下怎么建树。

    1. #include
    2. #include
    3. #include
    4. #include
    5. using namespace std;
    6. ///**
    7. // * Definition for a binary tree node.
    8. // */
    9. struct TreeNode {
    10. int val;
    11. TreeNode *left;
    12. TreeNode *right;
    13. TreeNode(int x) : val(x), left(NULL), right(NULL) {}
    14. };
    15. class Solution {
    16. int* nums;
    17. public:
    18. Solution(){
    19. nums=new int[11];
    20. int n[]={3,5,1,6,2,0,8,-1,-1,7,4};
    21. for(int i=0;i<11;++i)
    22. nums[i]=n[i];
    23. // for(int i=0;i<11;++i)
    24. // cout<
    25. // cout<
    26. }
    27. void display(TreeNode* node){
    28. if(node){
    29. display(node->left);
    30. cout<val<
    31. display(node->right);
    32. }
    33. }
    34. TreeNode* createTree(int idx){
    35. if(idx>10)
    36. return NULL;
    37. //cout<
    38. int num=nums[idx];
    39. TreeNode* t;
    40. if(num==-1){
    41. t = NULL;
    42. }else{
    43. t=new TreeNode(num);
    44. t->left=createTree(2*idx+1);
    45. t->right=createTree(2*idx+2);
    46. }
    47. return t;
    48. }
    49. void dfs_search(TreeNode* node,TreeNode* target,vector &stack,vector &path){
    50. if(node==NULL)
    51. return;
    52. stack.push_back(node);
    53. // cout<val<
    54. if(node->val==target->val){
    55. // path=stack;
    56. path.assign(stack.begin(),stack.end());
    57. return;
    58. }
    59. // cout<<"2"<
    60. dfs_search(node->left,target,stack,path);
    61. dfs_search(node->right,target,stack,path);
    62. stack.pop_back();
    63. }
    64. //朴素法 用时击败16.68%,内存击败7.59%
    65. TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
    66. vector p_path;
    67. vector q_path;
    68. vector stack;
    69. dfs_search(root,p,stack,p_path);
    70. stack.clear();
    71. dfs_search(root,q,stack,q_path);
    72. //
    73. // for(int i=0;i
    74. // cout<val<<" ";
    75. // cout<
    76. // for(int i=0;i
    77. // cout<val<<" ";
    78. // cout<
    79. TreeNode* common;
    80. int i=0;
    81. while(isize() && isize()){
    82. if(p_path[i]==q_path[i]){
    83. common=p_path[i];
    84. }
    85. i++;
    86. }
    87. return common;
    88. }
    89. };
    90. int main(){
    91. Solution Tree;
    92. TreeNode* root=Tree.createTree(0);
    93. // Tree.display(root);
    94. int p,q;
    95. cin>>p>>q;
    96. TreeNode* pp=new TreeNode(p);
    97. TreeNode* qq=new TreeNode(q);
    98. TreeNode* ans=Tree.lowestCommonAncestor(root,pp,qq);
    99. cout<val<
    100. return 0;
    101. }

    (2)python(29/31,时间超限了但算法正确性是可以保证的)

    1. import copy
    2. class TreeNode(): # python 没有结构体,但可以用类实现相同效果
    3. def __init__(self,val:int,left=None,right=None):
    4. self.val=val
    5. self.left=left
    6. self.right=right
    7. def Create_Tree(node,vals,idx):
    8. if idx>10:
    9. return node
    10. if vals[idx]=='#':
    11. node=None
    12. else:
    13. node=TreeNode(int(vals[idx]))
    14. node.left=Create_Tree(node.left,vals,2*idx+1)
    15. node.right=Create_Tree(node.right,vals,2*idx+2)
    16. return node
    17. def display(root):
    18. if root:
    19. display(root.left)
    20. print(root.val)
    21. display(root.right)
    22. class Solution:
    23. def __init__(self):
    24. self.p_path=[]
    25. self.q_path=[]
    26. self.stack=[]
    27. def dfs_search(self,node:'TreeNode',target:'TreeNode',flg):
    28. if node==None:
    29. return
    30. self.stack.append(node)
    31. if node.val == target.val:
    32. if flg==0:
    33. self.p_path=copy.deepcopy(self.stack)
    34. else:
    35. self.q_path=copy.deepcopy(self.stack)
    36. return
    37. self.dfs_search(node.left,target,flg)
    38. self.dfs_search(node.right,target,flg)
    39. self.stack.pop()
    40. def lowestCommonAncestor(self, root: 'TreeNode', p: 'TreeNode', q: 'TreeNode') -> 'TreeNode':
    41. self.dfs_search(root,p,0)
    42. self.stack.clear()
    43. self.dfs_search(root,q,1)
    44. common=TreeNode(None);
    45. i=0
    46. while i<len(self.p_path) and i<len(self.q_path):
    47. if self.p_path[i].val==self.q_path[i].val:
    48. common=copy.deepcopy(self.p_path[i])
    49. i+=1
    50. return common
    51. if __name__=='__main__':
    52. node=None
    53. vals=list("3516208##74")
    54. root=Create_Tree(node,vals,0)
    55. ## display(root)
    56. n1,n2=map(int,input().split())
    57. p=TreeNode(n1)
    58. q=TreeNode(n2)
    59. sol=Solution()
    60. ans=sol.lowestCommonAncestor(root,p,q)
    61. print(ans.val)

    4、LCA倍增法

    (1)C++(不提交,因为不想补全力扣给的入口函数,但是能保证算法正确性)

    1. #include
    2. #include
    3. #include
    4. using namespace std;
    5. const int N=10010;
    6. int n,m,cnt=1;
    7. struct Edge{
    8. int to,w,next;
    9. }edge[N];
    10. int head[N],dep[N],fa[N][N];
    11. void Add(int u,int v){
    12. edge[cnt].to=v;
    13. edge[cnt].next=head[u];
    14. // edge[cnt].w=w;
    15. head[u]=cnt;
    16. // cout<<"对于"<
    17. cnt++;
    18. }
    19. //int k=0;
    20. void dfs(int v,int father){ //填深度表和跳步表
    21. dep[v]=dep[father]+1;
    22. fa[v][0]=father;
    23. for(int i=1;i<=19;++i)
    24. fa[v][i]=fa[fa[v][i-1]][i-1];
    25. for(int i=head[v];i;i=edge[i].next){
    26. int p=edge[i].to;
    27. // cout<
    28. // k++;
    29. if(p!=father)
    30. dfs(p,v);
    31. }
    32. }
    33. int lca(int x,int y){
    34. // cout<
    35. if(dep[x]
    36. swap(x,y);
    37. for(int i=19;i>=0;i--) //先跳到同一层
    38. if(dep[fa[x][i]]>=dep[y]){
    39. x=fa[x][i];
    40. // cout<<"* "<
    41. }
    42. // cout<<"11"<
    43. if(x==y)
    44. return y;
    45. // cout<<"22"<
    46. for(int i=19;i>=0;i--){ //再跳到lca的下一层
    47. if(fa[x][i]!=fa[y][i]){
    48. x=fa[x][i],y=fa[y][i];
    49. }
    50. }
    51. return fa[x][0];
    52. }
    53. struct TreeNode {
    54. int val;
    55. TreeNode *left;
    56. TreeNode *right;
    57. TreeNode(int x) : val(x), left(NULL), right(NULL) {}
    58. };
    59. class Tree{
    60. public:
    61. int nums[11] = {3,5,1,6,2,9,8,-1,-1,7,4}; //因为样例中有一个节点的值为0,导致dep[0]被更改了,所以调试了很久,这个样例我把0换成了9
    62. // int nums[2]={1,2};
    63. TreeNode* createTree(int idx){
    64. if(idx>10)
    65. return NULL;
    66. TreeNode* t;
    67. if(nums[idx]==-1){
    68. t=NULL;
    69. return t;
    70. }else{
    71. t=new TreeNode(nums[idx]);
    72. t->left= createTree(2*idx+1);
    73. t->right=createTree(2*idx+2);
    74. }
    75. return t;
    76. }
    77. void display(TreeNode* node){
    78. if(node){
    79. display(node->left);
    80. cout<val<
    81. display(node->right);
    82. }
    83. }
    84. void add_edge(TreeNode* node){ //链式前向星加边,当然也可以根据数组下标加边,这里建树再加边的操作略显复杂
    85. if(node->left!=NULL){
    86. Add(node->val,node->left->val);
    87. Add(node->left->val,node->val);
    88. add_edge(node->left);
    89. }
    90. if(node->right!=NULL){
    91. Add(node->val,node->right->val);
    92. Add(node->right->val,node->val);
    93. add_edge(node->right);
    94. }
    95. }
    96. };
    97. int main(){
    98. Tree* t=new Tree();
    99. TreeNode* root=t->createTree(0);
    100. // t->display(root);
    101. t->add_edge(root);
    102. // cout<val<
    103. // cout<<"dep[0]: "<
    104. dfs(root->val,0);
    105. int x,y;
    106. cin>>x>>y;
    107. int ans=lca(x,y);
    108. cout<
    109. return 0;
    110. }

    5、其他递归法

    1. class Solution {
    2. public:
    3. TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
    4. if(root == NULL)
    5. return NULL;
    6. if(root == p || root == q)
    7. return root;
    8. TreeNode* left = lowestCommonAncestor(root->left, p, q);
    9. TreeNode* right = lowestCommonAncestor(root->right, p, q);
    10. if(left == NULL)
    11. return right;
    12. if(right == NULL)
    13. return left;
    14. if(left && right) // p和q在两侧
    15. return root;
    16. return NULL; // 必须有返回值
    17. }
    18. };

    三、题二:【模板】最近公共祖先(LCA)

    1、上链接

    P3379 【模板】最近公共祖先(LCA) - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

    2、基本思路

    为倍增法量身定做的一道题,下面用链式前向星+倍增法搞定。

    3、倍增法代码

    (1)C++ (AC)

    1. #include
    2. #include
    3. #include
    4. using namespace std;
    5. int n,m,s; //树的结点个数、询问次数、树根节点的序号
    6. int x,y; //x和y之间有一条直接连接的边
    7. int a,b; //表示询问a和b的最近公共祖先
    8. const int N=5000100;
    9. int cnt=1,head[N];
    10. struct Edge{
    11. int to,next;
    12. } e[N];
    13. int dep[N],fa[N][22];
    14. void Add(int x,int y){
    15. e[cnt].to=y;
    16. e[cnt].next=head[x];
    17. head[x]=cnt++;
    18. }
    19. void dfs(int u,int father){ //填深度表和跳步表
    20. dep[u]=dep[father]+1;
    21. fa[u][0]=father;
    22. for(int i=1;i<=19;++i)
    23. fa[u][i]=fa[fa[u][i-1]][i-1];
    24. for(int i=head[u];i;i=e[i].next){
    25. int p=e[i].to;
    26. if(p!=father)
    27. dfs(p,u);
    28. }
    29. }
    30. int lca(int x,int y){
    31. if(dep[x]
    32. swap(x,y);
    33. for(int i=19;i>=0;i--) //跳到同一层
    34. if(dep[fa[x][i]]>=dep[y])
    35. x=fa[x][i];
    36. if(x==y)
    37. return y;
    38. for(int i=19;i>=0;i--)
    39. if(fa[x][i]!=fa[y][i])
    40. x=fa[x][i],y=fa[y][i];
    41. return fa[x][0];
    42. }
    43. int main(){
    44. cin>>n>>m>>s;
    45. for(int i=0;i-1;++i){
    46. cin>>x>>y;
    47. Add(x,y);
    48. Add(y,x);
    49. }
    50. dfs(s,0);
    51. // cout<
    52. for(int i=0;i
    53. cin>>a>>b;
    54. cout<<lca(a,b)<
    55. }
    56. return 0;
    57. }

    (2)python(70分)

    1. N=500010
    2. # 没办法,N=5000010会全部时间超限
    3. cnt=1
    4. head=[0]*N
    5. next=[0]*N
    6. to=[0]*N
    7. dep=[0]*N
    8. fa=[[0]*20 for _ in range(N)]
    9. def Add(a,b):
    10. global cnt
    11. to[cnt]=b
    12. next[cnt]=head[a]
    13. head[a]=cnt
    14. cnt+=1
    15. def dfs(u:int,father:int): # 填深度表和跳步表
    16. dep[u]=dep[father]+1
    17. fa[u][0]=father
    18. for i in range(1,20):
    19. fa[u][i]=fa[fa[u][i-1]][i-1]
    20. i=head[u]
    21. while i:
    22. p=to[i]
    23. if p!=father:
    24. dfs(p,u)
    25. i=next[i]
    26. def lca(x,y)->int:
    27. if dep[x]
    28. x,y=y,x
    29. for i in range(19,-1,-1): # 先跳到同一层
    30. if dep[fa[x][i]]>=dep[y]:
    31. x=fa[x][i]
    32. if x==y:
    33. return y
    34. for i in range(19,-1,-1):
    35. if fa[x][i]!=fa[y][i]:
    36. x,y=fa[x][i],fa[y][i]
    37. return fa[x][0]
    38. n,m,s=map(int,input().split())
    39. for i in range(n-1):
    40. x,y=map(int,input().split())
    41. Add(x,y)
    42. Add(y,x)
    43. dfs(s,0)
    44. for i in range(m):
    45. a,b=map(int,input().split())
    46. print(lca(a,b))

    以上,最近公共祖先

    祝好

  • 相关阅读:
    SveletJs学习——事件
    (STM32)从零开始的RT-Thread之旅--SPI驱动ST7735(2)
    Vue脚手架④
    第七章 树与森林
    spring aop 初探
    8月25日计算机视觉理论学习笔记——FCN、DeepLab
    Java实现Excel数据导入数据库
    C++核心编程:P19->STL----常用算法(下)
    利用Proxifier配置多级代理
    springboot教室实验室预约系统在线视频点播系统毕业设计毕设作品开题报告开题答辩PPT
  • 原文地址:https://blog.csdn.net/m0_52711790/article/details/127928596