【模板】最近公共祖先(LCA)

树的简介

树常见用来表示家族谱的家族关系,他像一个倒着的树。他的构成为:

  • 根节点( r o o t root root ): 所有节点都由一个 根节点( r o o t root root ) ,且只有一个 根节点( r o o t root root )
  • 父亲节点( f a t h e r − n o d e father-node father−node ): 每个 父亲节点( f a t h e r − n o d e father-node father−node ) 都有一个或者多个 孩子节点( c h i l d − n o d e child-node child−node )
  • 孩子节点( c h i l d − n o d e child-node child−node ): 每个 孩子节点( c h i l d − n o d e child-node child−node ) 都有一个 父亲节点( f a t h e r − n o d e father-node father−node ) 或者 根节点( r o o t root root )

二叉树 - 特例

二叉树和树基本一样,但是他也和树有一点区别:

  • 每一个 父亲节点( f a t h e r − n o d e father-node father−node ) 最多只能有两个或者没有 孩子节点( c h i l d − n o d e child-node child−node )
  • 两个 孩子节点( c h i l d − n o d e child-node child−node ) 分别为 左孩子节点( l e f t − c h i l d − n o d e left-child-node left−child−node )右孩子节点( r i g h t − c h i l d − n o d e right-child-node right−child−node ) ,但是一个二叉树最多只能有两个孩子节点,也可以没有孩子节点

树的存储

树的存储经常使用 邻接表 进行存储,他的存储如下表格:

如果 1 1 12 2 2 的父亲节点,那么 2 2 2 就是 1 1 1 的孩子节点:

1 2 3
1 f a l s e false false t r u e true true f a l s e false false
2 t r u e true true f a l s e false false f a l s e false false
3 f a l s e false false f a l s e false false f a l s e false false

我们在代码里可以用 v e c t o r vector vector 来存储:

cpp 复制代码
#include <bits/stdc++.h>
using namespace std;

const int N = 1e5+10;
vector<int> G[N];
struct node {
    int v,id;
}
vector<node> Q[N];

int main() {
    int n,m;
    scanf("%d%d",&n,&m);
    int u,v;
    for(int i=1;i<n;i++) {
        scanf("%d%d",&u,&v);
        G[u].push_back(v);
        G[v].push_back(u);
    }
    for(int i=1;i<=m;i++) {
        scanf("%d%d",&u,&v);
        Q[u].push_back(node{v,i});
        Q[v].push_back(node{u,i});
    }
    return 0;
}

这样可以用 v e c t o r vector vector 进行存储,我们也可以用 数组 进行存储:

cpp 复制代码
#include <bits/stdc++.h>
using namespace std;

const int N = 1e5+10;
bool a[N][N];
int b[N][N];

int main() {
    int n,m;
    scanf("%d%d",&n,&m);
    int u,v;
    for(int i=1;i<n;i++) {
        scanf("%d%d",&u,&v);
        a[u][v] = true;
        a[v][u] = true;
    }
    for(int i=1;i<=m;i++) {
        scanf("%d%d",&u,&v);
        a[u][i] = v;
        a[v][i] = u;
    }
    return 0;
}

我们也有可以用 t a r j a n tarjan tarjan 算法进行查找:

cpp 复制代码
int find(int x) {	//截枝查找(折半查找)
    if(fa[x] == x) {
        return x;
    }
    return fa[x] == find(fa[x]);
}

void tarjan(int u) {
    vis[u] = 1;
    for(auto v : G[u]) {
        if(!vis[v]) {
            tarjan(v);
            fa[v] = u;
        }
    }
    for(auto e : Q[u]) {
        int v = e.v;
        int id = e.id;
        if(vis[v]) {
            ans[id] = find(v);
        }
    }
}

所以我们可以写出 洛谷的【模板】最近公共祖先(LCA) 代码:

cpp 复制代码
#include <bits/stdc++.h>
using namespace std;

const int N = 5e5+5;
vector<int> G[N];
struct node {
	int v,id;
};
vector<node> Q[N];
int fa[N];
bool vis[N];
int ans[N];

int find(int x) {
	if(x == fa[x]) {
		return x;
	}
	return fa[x] = find(fa[x]);
}

void tarjan(int u) {
	vis[u] = 1;
	for(auto v : G[u]) {
		if(!vis[v]) {
			tarjan(v);
			fa[v] = u;
		}
	}
	for(auto e : Q[u]) {
		int v = e.v;
		int id = e.id;
		if(vis[v]) {
			ans[id] = find(v);
		}
	}
}

int main()
{
	int n,m,s;
	scanf("%d%d%d",&n,&m,&s);
	int u,v;
    // 使用vector进行存储
	for(int i=1;i<n;i++) {
		scanf("%d%d",&u,&v);
		G[u].push_back(v);
		G[v].push_back(u);
	}
	for(int i=1;i<=m;i++) {
		scanf("%d%d",&u,&v);
		Q[u].push_back(node{v,i});
		Q[v].push_back(node{u,i});
	}
	for(int i=1;i<=n;i++) {
		fa[i] = i;
	}
    // 使用 tarjan算法 进行查找
	tarjan(s);
    // 输出
	for(int i=1;i<=m;i++) {
		printf("%d\n",ans[i]);
	}
	return 0;
}

这样我们就可以完成洛谷的 【模板】最近公共祖先(LCA)

相关推荐
能工智人小辰11 分钟前
Codeforces Round 509 (Div. 2) C. Coffee Break
c语言·c++·算法
kingmax5421200812 分钟前
CCF GESP202503 Grade4-B4263 [GESP202503 四级] 荒地开垦
数据结构·算法
岁忧17 分钟前
LeetCode 高频 SQL 50 题(基础版)之 【高级字符串函数 / 正则表达式 / 子句】· 上
sql·算法·leetcode
梦星辰.19 分钟前
VSCode CUDA C++进行Linux远程开发
linux·c++·vscode
whoarethenext1 小时前
C++ OpenCV 学习路线图
c++·opencv·学习
eachin_z1 小时前
力扣刷题(第四十九天)
算法·leetcode·职场和发展
闻缺陷则喜何志丹1 小时前
【强连通分量 缩点 拓扑排序】P3387 【模板】缩点|普及+
c++·算法·拓扑排序·洛谷·强连通分量·缩点
恰薯条的屑海鸥1 小时前
零基础在实践中学习网络安全-皮卡丘靶场(第十四期-XXE模块)
网络·学习·安全·web安全·渗透测试
Lester_11011 小时前
嵌入式学习笔记 - freeRTOS vTaskPlaceOnEventList()函数解析
笔记·学习
hutaotaotao2 小时前
c++中的输入输出流(标准IO,文件IO,字符串IO)
c++·io·fstream·sstream·iostream