LCA 最近公共祖先

首先,介绍何谓“最近公共祖先”,其实就是对于一颗二叉或者多叉树来说,每个节点都有祖先节点(根节点除外),对于任意两个点,a,b,它们可能有多个公共的祖先点c,即c为a的祖先且c为b的祖先,我们定义深度最大的那个公共祖先C为a,b的最近公共祖先,这个点是唯一的。

对于求最近公共祖先的算法有不少,著名的是LCA的在线算法和离线算法,看了这么多网上的代码,很少能找到我中意的,而且有些在线算法时间复杂度实在太高,因此我觉得有必要把自己想的一些东西拿出来给大家分享分享。

我们想想普通的方法来求a,b两个点的最近公共祖先C:

View Code
int LCA(int a,int b)//求a,b的LCA
{
    while(a!=b)//找到a,b的LCA
    {
        if(p[a].deep>p[b].deep)
        {
            a=p[a].father;
        }
        else
        {
            b=p[b].father;
        }
    }
    return a;
}

意思就是一步一步向上寻找a,b点的父节点,知道找到他们的父节点相同,那么此时的点就是点C;但是如果这棵树很大,那么这个算法实在太慢了,因此我们就优化这个地方。

我们来看看这样一棵树:

    0              -------deep=0

          /  \

        1     2          -------deep=1

      /   \

    3      4              -------deep=2

          /    \ 

        5       6          ------deep=3

                   \

                      7     -------deep=4

我们以深度为界限把数分段,比如我把深度为0和1的点作为第一段,深度2和3的点作为第二段,然后查看a和b是否在同一段,若不是同一段,则向上一段去寻找,直到a和b为同一段,然后再依照父节点去寻找最近公共祖先C:

View Code
int LCA(int a,int b)//求a,b的LCA
{
    while(p[a].sec!=p[b].sec)//找到a,b亮点所在的段
    {
        if(p[a].deep>p[b].deep)
        {
            a=p[a].sec;
        }
        else
        {
            b=p[b].sec;
        }
    }
    while(a!=b)//找到a,b的LCA
    {
        if(p[a].deep>p[b].deep)
        {
            a=p[a].father;
        }
        else
        {
            b=p[b].father;
        }
    }
    return a;
}

代码中p[u].sec为点u所归属的段,也就是归属的集合,也就是我们常说的并查集来分段。具体怎么分,方法有很多,可以以一个分叉点到下一个分叉点之间的所有点作为一个集合,而且网上大多数算法都是这么做的,这样做起来最好的时间复杂度是o(lgn),但是最复杂的情况将是o(n)。在这里我介绍一种特殊的分段方法,最好和最复杂的时间复杂度都是o(sqrt(n)),即依照节点的深度deep来分段,每一段的长度为sqrt(max(deep)),比如一棵树最大深度为100,那么深度为1~9的点为集合0,深度10~19的点为集合1,依次类推。

补充:

当然如果我们这样来分段,在下面的代码中:

p[u].deep%sec==0;时,我们应该把p[u].sec=p[p[u].father].sec+1;但是这样子的复杂度还是有点高,为了让复杂度再降低,我们把段分的更多,也就是当p[u].deep>=sqrt(max(deep))的时候,我们把段标记为它的父节点,这样深度相同的节点也可以在不同的段里面,可以让我们更快的查询最近公共祖先。(这里感谢一楼的提醒,一开始忘记说了)

 

View Code
void setsection(int u,int sec)//构建点u属于的段集合,每一段深度为sec
{
    if(p[u].deep<sec)
    {
        p[u].sec=0;
    }
    else
    {
        if(p[u].deep%sec==0)
        {
            p[u].sec=p[u].father;
        }
        else
        {
            p[u].sec=p[p[u].father].sec;
        }
    }
    for(unsigned i=0;i<p[u].next.size();i++)
    {
        int k=p[u].next[i];
        if(p[k].father==u)
        {
            setsection(k,sec);
        }
    }
}

说到这里,整个LCA的算法也就差不多讲完了,并查集分段的方法无外乎这两种,我的代码是后者,毕竟还是比较好理解的,如果不懂也可以留言,下面我把全部的代码都贴上来。

我们一开始给的是一个图,但是我们要把这个图变为一棵树,以任意节点作为根节点建树(此处以点0为root),建树过程标记父节点和深度,然后给节点分段,最后可以做任意次数的查询:

步骤:1.为图建树,标记节点的父节点和深度

   2.为树上的节点分段,使用并查集

       3.直接查询(a,b)两个节点的LCA即可。

View Code
  1 //====================================================================
  2 //Name       :LCA最近公共祖先
  3 //Author     :hxf
  4 //copyright  :http://www.cnblogs.com/Free-rein/
  5 //Description:
  6 //Data       :2012.8.20
  7 //========================================================================
  8 #include<iostream>
  9 #include<algorithm>
 10 #include<stdio.h>
 11 #include<math.h>
 12 #include<string>
 13 #include<cstring>
 14 #include<vector>
 15 #include<stack>
 16 #include<queue>
 17 #define MAXN 1040
 18 #define inf 10100
 19 #define pi 3.141592653589793239
 20 using namespace std;
 21 struct Tree{
 22     vector<int> next;//子节点
 23     int father;//父节点
 24     int deep;//深度
 25     int sec;//属于的段
 26 }p[50050];
 27 int visit[50050];
 28 int maxdeep;//最大深度
 29 void dfs(int u)//构建多叉树,找到父节点和深度
 30 {
 31     visit[u]=1;
 32     for(unsigned i=0;i<p[u].next.size();i++)
 33     {
 34         int k=p[u].next[i];
 35         if(visit[k]==0)
 36         {
 37             p[k].deep=p[u].deep+1;
 38             p[k].father=u;
 39             dfs(k);
 40         }
 41     }
 42     maxdeep=max(maxdeep,p[u].deep);
 43 }
 44 void setsection(int u,int sec)//构建点u属于的段集合,每一段深度为sec
 45 {
 46     if(p[u].deep<sec)
 47     {
 48         p[u].sec=0;
 49     }
 50     else
 51     {
 52         if(p[u].deep%sec==0)
 53         {
 54             p[u].sec=p[u].father;
 55         }
 56         else
 57         {
 58             p[u].sec=p[p[u].father].sec;
 59         }
 60     }
 61     for(unsigned i=0;i<p[u].next.size();i++)
 62     {
 63         int k=p[u].next[i];
 64         if(p[k].father==u)
 65         {
 66             setsection(k,sec);
 67         }
 68     }
 69 }
 70 void preLCA()
 71 {
 72     maxdeep=0;
 73     dfs(0);
 74     setsection(0,(int)sqrt(maxdeep));//每一段深度为sqrt(maxdeep)
 75 }
 76 int LCA(int a,int b)//求a,b的LCA
 77 {
 78     while(p[a].sec!=p[b].sec)//找到a,b亮点所在的段
 79     {
 80         if(p[a].deep>p[b].deep)
 81         {
 82             a=p[a].sec;
 83         }
 84         else
 85         {
 86             b=p[b].sec;
 87         }
 88     }
 89     while(a!=b)//找到a,b的LCA
 90     {
 91         if(p[a].deep>p[b].deep)
 92         {
 93             a=p[a].father;
 94         }
 95         else
 96         {
 97             b=p[b].father;
 98         }
 99     }
100     return a;
101 }
102 int main()
103 {
104     int n;//n个节点
105     while(scanf("%d",&n)!=EOF)
106     {
107         for(int i=0;i<n;i++)
108         {
109             p[i].next.clear();
110             p[i].father=0;
111             p[i].deep=0;
112             p[i].sec=0;
113         }
114         for(int i=0;i<n-1;i++)
115         {
116             int x,y;
117             scanf("%d %d",&x,&y);
118             p[x].next.push_back(y);
119             p[y].next.push_back(x);//建边
120         }
121         memset(visit,0,sizeof(visit));
122         preLCA();//建树
123         ///
124         int m;//做m次查询
125         scanf("%d",&m);
126         while(m--)
127         {
128             int a,b;
129             scanf("%d %d",&a,&b);
130             int lcaab=LCA(a,b);//得到a,b的LCA
131             printf("%d\n",lcaab);
132         }
133     }
134     return 0;
135 }

 

 

转载于:https://www.cnblogs.com/Free-rein/archive/2012/08/24/2654519.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
智慧校园建设方案旨在通过融合先进技术,如物联网、大数据、人工智能等,实现校园的智能化管理与服务。政策的推动和技术的成熟为智慧校园的发展提供了基础。该方案强调了数据的重要性,提出通过数据的整合、开放和共享,构建产学研资用联动的服务体系,以促进校园的精细化治理。 智慧校园的核心建设任务包括数据标准体系和应用标准体系的建设,以及信息化安全与等级保护的实施。方案提出了一站式服务大厅和移动校园的概念,通过整合校内外资源,实现资源共享平台和产教融合就业平台的建设。此外,校园大脑的构建是实现智慧校园的关键,它涉及到数据中心化、数据资产化和数据业务化,以数据驱动业务自动化和智能化。 技术应用方面,方案提出了物联网平台、5G网络、人工智能平台等新技术的融合应用,以打造多场景融合的智慧校园大脑。这包括智慧教室、智慧实验室、智慧图书馆、智慧党建等多领域的智能化应用,旨在提升教学、科研、管理和服务的效率和质量。 在实施层面,智慧校园建设需要统筹规划和分步实施,确保项目的可行性和有效性。方案提出了主题梳理、场景梳理和数据梳理的方法,以及现有技术支持和项目分级的考虑,以指导智慧校园的建设。 最后,智慧校园建设的成功依赖于开放、协同和融合的组织建设。通过战略咨询、分步实施、生态建设和短板补充,可以构建符合学校特色的生态链,实现智慧校园的长远发展。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值