给定一棵有根的树,以及树中的两个节点,找到两个节点的最低共同祖先。两个节点u和v的LCA定义为从作为祖先的根到u和v的最远节点。
先决条件:LCA |设置1
上图的示例:
Input : 4 5
Output : 2
Input : 4 7
Output : 1
将LCA转换为RMQ(范围最小查询):
取一个名为E []的数组,该数组存储dfs遍历的顺序,即在dfs遍历期间覆盖节点的顺序。例如,
上面给出的树具有以下顺序的dfs遍历:1-2-4-2-5-2-1-3。
取另一个数组L [],其中L [i]是节点E [i]的级别。
还有数组H [],它存储数组E []中第i个节点的第一个匹配项的索引。
所以,对于上面的树,
E [] = {1,2,4,2,5,2,1,3}
L [] = {1、2、3、2、3、2、1、2}
H [] = {0,1,7,2,4}
请注意,数组E和L具有基于1的索引,但是数组H具有基于0的索引。
现在,要查找LCA(4,3),首先,使用数组H并找到在E中找到4和3的索引,即H [4]和H [3]。因此,索引的值为2和7。现在,查看子数组L [2:7],并在此子数组中找到最小值1(在第6个索引处),并找到数组E中的相应元素。即E [6]是LCA(4,3)。
要了解为什么这样做,请再次采用LCA(4,3)。子数组E [2:7]是一个可以从节点4到达节点3的路径。并且,如果在该路径中存在最低级别的节点,则可以简单地将其声明为LCA(4,3)。
现在,问题是要在子数组E [H [u]….H [v]]中找到最小值(假设H [u]> = H [v])。并且,可以使用段树或稀疏表来完成。下面是使用段树的代码。
C++
// CPP code to find LCA of given
// two nodes in a tree
#include
#define sz(x) x.size()
#define pb push_back
#define left 2 * i + 1
#define right 2 * i + 2
using namespace std;
const int maxn = 100005;
// the graph
vector> g(maxn);
// level of each node
int level[maxn];
vector e;
vector l;
int h[maxn];
// the segment tree
int st[5 * maxn];
// adding edges to the graph(tree)
void add_edge(int u, int v) {
g[u].pb(v);
g[v].pb(u);
}
// assigning level to nodes
void leveling(int src) {
for (int i = 0; i < sz(g[src]); i++) {
int des = g[src][i];
if (!level[des]) {
level[des] = level[src] + 1;
leveling(des);
}
}
}
bool visited[maxn];
// storing the dfs traversal
// in the array e
void dfs(int src) {
e.pb(src);
visited[src] = 1;
for (int i = 0; i < sz(g[src]); i++) {
int des = g[src][i];
if (!visited[des]) {
dfs(des);
e.pb(src);
}
}
}
// making the array l
void setting_l(int n) {
for (int i = 0; i < sz(e); i++)
l.pb(level[e[i]]);
}
// making the array h
void setting_h(int n) {
for (int i = 0; i <= n; i++)
h[i] = -1;
for (int i = 0; i < sz(e); i++) {
// if is already stored
if (h[e[i]] == -1)
h[e[i]] = i;
}
}
// Range minimum query to return the index
// of minimum in the subarray L[qs:qe]
int RMQ(int ss, int se, int qs, int qe, int i) {
if (ss > se)
return -1;
// out of range
if (se < qs || qe < ss)
return -1;
// in the range
if (qs <= ss && se <= qe)
return st[i];
int mid = (ss + se) >> 1;
int st = RMQ(ss, mid, qs, qe, left);
int en = RMQ(mid + 1, se, qs, qe, right);
if (st != -1 && en != -1) {
if (l[st] < l[en])
return st;
return en;
} else if (st != -1)
return st;
else if (en != -1)
return en;
}
// constructs the segment tree
void SegmentTreeConstruction(int ss, int se, int i) {
if (ss > se)
return;
if (ss == se) // leaf
{
st[i] = ss;
return;
}
int mid = (ss + se) >> 1;
SegmentTreeConstruction(ss, mid, left);
SegmentTreeConstruction(mid + 1, se, right);
if (l[st[left]] < l[st[right]])
st[i] = st[left];
else
st[i] = st[right];
}
// Funtion to get LCA
int LCA(int x, int y) {
if (h[x] > h[y])
swap(x, y);
return e[RMQ(0, sz(l) - 1, h[x], h[y], 0)];
}
// Driver code
int main() {
ios::sync_with_stdio(0);
// n=number of nodes in the tree
// q=number of queries to answer
int n = 15, q = 5;
// making the tree
/*
1
/ | \
2 3 4
| \
5 6
/ | \
8 7 9 (right of 5)
/ | \ | \
10 11 12 13 14
|
15
*/
add_edge(1, 2);
add_edge(1, 3);
add_edge(1, 4);
add_edge(3, 5);
add_edge(4, 6);
add_edge(5, 7);
add_edge(5, 8);
add_edge(5, 9);
add_edge(7, 10);
add_edge(7, 11);
add_edge(7, 12);
add_edge(9, 13);
add_edge(9, 14);
add_edge(12, 15);
level[1] = 1;
leveling(1);
dfs(1);
setting_l(n);
setting_h(n);
SegmentTreeConstruction(0, sz(l) - 1, 0);
cout << LCA(10, 15) << endl;
cout << LCA(11, 14) << endl;
return 0;
}
Java
// JAVA code to find LCA of given
// two nodes in a tree
import java.util.*;
public class GFG
{
static int maxn = 100005;
static int left(int i)
{
return (2 * i + 1);
}
static int right(int i) { return 2 * i + 2;}
// the graph
static Vector []g = new Vector[maxn];
// level of each node
static int []level = new int[maxn];
static Vector e = new Vector<>();
static Vector l= new Vector<>();
static int []h = new int[maxn];
// the segment tree
static int []st = new int[5 * maxn];
// adding edges to the graph(tree)
static void add_edge(int u, int v)
{
g[u].add(v);
g[v].add(u);
}
// assigning level to nodes
static void leveling(int src)
{
for (int i = 0; i < (g[src].size()); i++)
{
int des = g[src].get(i);
if (level[des] != 0)
{
level[des] = level[src] + 1;
leveling(des);
}
}
}
static boolean []visited = new boolean[maxn];
// storing the dfs traversal
// in the array e
static void dfs(int src)
{
e.add(src);
visited[src] = true;
for (int i = 0; i < (g[src]).size(); i++)
{
int des = g[src].get(i);
if (!visited[des])
{
dfs(des);
e.add(src);
}
}
}
// making the array l
static void setting_l(int n)
{
for (int i = 0; i < e.size(); i++)
l.add(level[e.get(i)]);
}
// making the array h
static void setting_h(int n)
{
for (int i = 0; i <= n; i++)
h[i] = -1;
for (int i = 0; i < e.size(); i++)
{
// if is already stored
if (h[e.get(i)] == -1)
h[e.get(i)] = i;
}
}
// Range minimum query to return the index
// of minimum in the subarray L[qs:qe]
static int RMQ(int ss, int se, int qs, int qe, int i)
{
if (ss > se)
return -1;
// out of range
if (se < qs || qe < ss)
return -1;
// in the range
if (qs <= ss && se <= qe)
return st[i];
int mid = (ss + se)/2 ;
int st = RMQ(ss, mid, qs, qe, left(i));
int en = RMQ(mid + 1, se, qs, qe, right(i));
if (st != -1 && en != -1)
{
if (l.get(st) < l.get(en))
return st;
return en;
} else if (st != -1)
return st-2;
else if (en != -1)
return en-1;
return 0;
}
// constructs the segment tree
static void SegmentTreeConstruction(int ss,
int se, int i)
{
if (ss > se)
return;
if (ss == se) // leaf
{
st[i] = ss;
return;
}
int mid = (ss + se) /2;
SegmentTreeConstruction(ss, mid, left(i));
SegmentTreeConstruction(mid + 1, se, right(i));
if (l.get(st[left(i)]) < l.get(st[right(i)]))
st[i] = st[left(i)];
else
st[i] = st[right(i)];
}
// Funtion to get LCA
static int LCA(int x, int y)
{
if (h[x] > h[y])
{
int t = x;
x = y;
y = t;
}
return e.get(RMQ(0, l.size() - 1, h[x], h[y], 0));
}
// Driver code
public static void main(String[] args)
{
// n=number of nodes in the tree
// q=number of queries to answer
int n = 15, q = 5;
for (int i = 0; i < g.length; i++)
g[i] = new Vector();
// making the tree
/*
1
/ | \
2 3 4
| \
5 6
/ | \
8 7 9 (right of 5)
/ | \ | \
10 11 12 13 14
|
15
*/
add_edge(1, 2);
add_edge(1, 3);
add_edge(1, 4);
add_edge(3, 5);
add_edge(4, 6);
add_edge(5, 7);
add_edge(5, 8);
add_edge(5, 9);
add_edge(7, 10);
add_edge(7, 11);
add_edge(7, 12);
add_edge(9, 13);
add_edge(9, 14);
add_edge(12, 15);
level[1] = 1;
leveling(1);
dfs(1);
setting_l(n);
setting_h(n);
SegmentTreeConstruction(0, l.size() - 1, 0);
System.out.print(LCA(10, 15) +"\n");
System.out.print(LCA(11, 14) +"\n");
}
}
// This code is contributed by Rajput-Ji
Python3
# Python code to find LCA of given
# two nodes in a tree
maxn = 100005
# the graph
g = [[] for i in range(maxn)]
# level of each node
level = [0] * maxn
e = []
l = []
h = [0] * maxn
# the segment tree
st = [0] * (5 * maxn)
# adding edges to the graph(tree)
def add_edge(u: int, v: int):
g[u].append(v)
g[v].append(u)
# assigning level to nodes
def leveling(src: int):
for i in range(len(g[src])):
des = g[src][i]
if not level[des]:
level[des] = level[src] + 1
leveling(des)
visited = [False] * maxn
# storing the dfs traversal
# in the array e
def dfs(src: int):
e.append(src)
visited[src] = True
for i in range(len(g[src])):
des = g[src][i]
if not visited[des]:
dfs(des)
e.append(src)
# making the array l
def setting_l(n: int):
for i in range(len(e)):
l.append(level[e[i]])
# making the array h
def setting_h(n: int):
for i in range(n + 1):
h[i] = -1
for i in range(len(e)):
# if is already stored
if h[e[i]] == -1:
h[e[i]] = i
# Range minimum query to return the index
# of minimum in the subarray L[qs:qe]
def RMQ(ss: int, se: int, qs: int, qe: int, i: int) -> int:
global st
if ss > se:
return -1
# out of range
if se < qs or qe < ss:
return -1
# in the range
if qs <= ss and se <= qe:
return st[i]
mid = (se + ss) >> 1
stt = RMQ(ss, mid, qs, qe, 2 * i + 1)
en = RMQ(mid + 1, se, qs, qe, 2 * i + 2)
if stt != -1 and en != -1:
if l[stt] < l[en]:
return stt
return en
elif stt != -1:
return stt
elif en != -1:
return en
# constructs the segment tree
def segmentTreeConstruction(ss: int, se: int, i: int):
if ss > se:
return
if ss == se: # leaf
st[i] = ss
return
mid = (ss + se) >> 1
segmentTreeConstruction(ss, mid, 2 * i + 1)
segmentTreeConstruction(mid + 1, se, 2 * i + 2)
if l[st[2 * i + 1]] < l[st[2 * i + 2]]:
st[i] = st[2 * i + 1]
else:
st[i] = st[2 * i + 2]
# Funtion to get LCA
def LCA(x: int, y: int) -> int:
if h[x] > h[y]:
x, y = y, x
return e[RMQ(0, len(l) - 1, h[x], h[y], 0)]
# Driver Code
if __name__ == "__main__":
# n=number of nodes in the tree
# q=number of queries to answer
n = 15
q = 5
# making the tree
# /*
# 1
# / | \
# 2 3 4
# | \
# 5 6
# / | \
# 8 7 9 (right of 5)
# / | \ | \
# 10 11 12 13 14
# |
# 15
# */
add_edge(1, 2)
add_edge(1, 3)
add_edge(1, 4)
add_edge(3, 5)
add_edge(4, 6)
add_edge(5, 7)
add_edge(5, 8)
add_edge(5, 9)
add_edge(7, 10)
add_edge(7, 11)
add_edge(7, 12)
add_edge(9, 13)
add_edge(9, 14)
add_edge(12, 15)
level[1] = 1
leveling(1)
dfs(1)
setting_l(n)
setting_h(n)
segmentTreeConstruction(0, len(l) - 1, 0)
print(LCA(10, 15))
print(LCA(11, 14))
# This code is contributed by
# sanjeev2552
C#
// C# code to find LCA of given
// two nodes in a tree
using System;
using System.Collections.Generic;
public class GFG
{
static int maxn = 100005;
static int left(int i)
{
return (2 * i + 1);
}
static int right(int i) { return 2 * i + 2;}
// the graph
static List []g = new List[maxn];
// level of each node
static int []level = new int[maxn];
static List e = new List();
static List l= new List();
static int []h = new int[maxn];
// the segment tree
static int []st;
// adding edges to the graph(tree)
static void add_edge(int u, int v)
{
g[u].Add(v);
g[v].Add(u);
}
// assigning level to nodes
static void leveling(int src)
{
for (int i = 0; i < (g[src].Count); i++)
{
int des = g[src][i];
if (level[des] != 0)
{
level[des] = level[src] + 1;
leveling(des);
}
}
}
static bool []visited = new bool[maxn];
// storing the dfs traversal
// in the array e
static void dfs(int src)
{
e.Add(src);
visited[src] = true;
for (int i = 0; i < (g[src]).Count; i++)
{
int des = g[src][i];
if (!visited[des])
{
dfs(des);
e.Add(src);
}
}
}
// making the array l
static void setting_l(int n)
{
for (int i = 0; i < e.Count; i++)
l.Add(level[e[i]]);
}
// making the array h
static void setting_h(int n)
{
for (int i = 0; i <= n; i++)
h[i] = -1;
for (int i = 0; i < e.Count; i++)
{
// if is already stored
if (h[e[i]] == -1)
h[e[i]] = i;
}
}
// Range minimum query to return the index
// of minimum in the subarray L[qs:qe]
static int RMQ(int ss, int se, int qs, int qe, int i)
{
if (ss > se)
return -1;
// out of range
if (se < qs || qe < ss)
return -1;
// in the range
if (qs <= ss && se <= qe)
return st[i];
int mid = (ss + se)/2 ;
int sti = RMQ(ss, mid, qs, qe, left(i));
int en = RMQ(mid + 1, se, qs, qe, right(i));
if (sti != -1 && en != -1)
{
if (l[sti] < l[en])
return sti;
return en;
} else if (sti != -1)
return sti-2;
else if (en != -1)
return en-1;
return 0;
}
// constructs the segment tree
static void SegmentTreeConstruction(int ss,
int se, int i)
{
if (ss > se)
return;
if (ss == se) // leaf
{
st[i] = ss;
return;
}
int mid = (ss + se) /2;
SegmentTreeConstruction(ss, mid, left(i));
SegmentTreeConstruction(mid + 1, se, right(i));
if (l[st[left(i)]] < l[st[right(i)]])
st[i] = st[left(i)];
else
st[i] = st[right(i)];
}
// Funtion to get LCA
static int LCA(int x, int y)
{
if (h[x] > h[y])
{
int t = x;
x = y;
y = t;
}
return e[RMQ(0, l.Count - 1, h[x], h[y], 0)];
}
// Driver code
public static void Main(String[] args)
{
st = new int[5 * maxn];
// n=number of nodes in the tree
// q=number of queries to answer
int n = 15;
for (int i = 0; i < g.Length; i++)
g[i] = new List();
// making the tree
/*
1
/ | \
2 3 4
| \
5 6
/ | \
8 7 9 (right of 5)
/ | \ | \
10 11 12 13 14
|
15
*/
add_edge(1, 2);
add_edge(1, 3);
add_edge(1, 4);
add_edge(3, 5);
add_edge(4, 6);
add_edge(5, 7);
add_edge(5, 8);
add_edge(5, 9);
add_edge(7, 10);
add_edge(7, 11);
add_edge(7, 12);
add_edge(9, 13);
add_edge(9, 14);
add_edge(12, 15);
level[1] = 1;
leveling(1);
dfs(1);
setting_l(n);
setting_h(n);
SegmentTreeConstruction(0, l.Count - 1, 0);
Console.Write(LCA(10, 15) +"\n");
Console.Write(LCA(11, 14) +"\n");
}
}
// This code is contributed by gauravrajput1
7
5
时间复杂度:
定义的数组存储在O(n)中。段树的构造也需要O(n)时间。 LCA函数调用函数RMQ,该函数每个查询占用O(logn)(因为它使用段树)。因此,总体时间复杂度为O(n + q * logn) 。