检查两个节点是否在树中的同一路径上
给定一棵树(不一定是二叉树)和许多查询,这样每个查询都将树的两个节点作为参数。对于每个查询对,查找两个节点是否在从根到底部的同一路径上。
例如,考虑下面的树,如果给定的查询是 (1, 5)、(1, 6) 和 (2, 6),那么答案应该分别是 true、true 和 false。
请注意,1 和 5 位于同一根到叶路径上,1 和 6 也是如此,但 2 和 6 不在同一根到叶路径上。
很明显,深度优先搜索技术是用来解决上述问题的,主要问题是如何快速响应多个查询。这里我们的图是一棵树,它可能有任意数量的孩子。现在一棵树中的 DFS,如果从根节点开始,以深度搜索的方式进行,即假设根有三个孩子,而这些孩子只有一个孩子,所以如果 DFS 启动,那么它首先访问根节点的第一个孩子,然后将深入到该节点的子节点。一棵小树的情况可以表示如下:
访问节点的顺序将是 - 1 2 5 3 6 4 7 。
因此,稍后访问其他子节点,直到完全成功访问一个子节点直到深度。为了简化这一点,如果我们假设我们手中有一块手表,并且我们以 DFS 方式从根开始行走。
银泰——当我们第一次访问节点时
超时 - 如果我们稍后再次访问节点但没有未访问的子节点,我们称之为超时,
注意:其子树中的任何节点将始终具有 intime < 其子节点(或子节点的子节点),因为它总是在子节点之前首先被访问(由于 DFS),并且将具有 outtime > 其子树中的所有节点,因为在注意之前它等待所有子节点被标记为已访问的超时时间。
对于任意两个节点 u, v 如果它们在同一条路径上,那么,
Intime[v] < Intime[u] and Outtime[v] > Outtime[u]
OR
Intime[u] < Intime[v] and Outtime[u ]> Outtime[v]
- 如果给定的一对节点遵循这两个条件中的任何一个,那么它们就在叶子路径的同一个根上。
- 否则不在同一路径上(如果两个节点在不同的路径上,则意味着没有人在彼此的子树中)。
伪代码
我们使用一个全局变量 time ,它将随着节点的 dfs 开始而递增,并且在之后也会递增
DFS(v)
increment timer
Intime[v] = timer
mark v as visited
for all u that are children of v
DFS(u)
increment timer
Outtime[v] = timer
end
时间复杂度——预处理 O(n),每个查询 O(1)。
执行:
下面是上述伪代码的实现。
C++
// C++ program to check if given pairs lie on same
// path or not.
#include
using namespace std;
const int MAX = 100001;
// To keep track of visited vertices in DFS
bool visit[MAX] = {0};
// To store start and end time of all vertices
// during DFS.
int intime[MAX];
int outtime[MAX];
// initially timer is zero
int timer = 0;
// Does DFS of given graph and fills arrays
// intime[] and outtime[]. These arrays are used
// to answer given queries.
void dfs(vector graph[], int v)
{
visit[v] = true;
// Increment the timer as you enter
// the recursion for v
++timer;
// Upgrade the in time for the vertex
intime[v] = timer;
vector::iterator it = graph[v].begin();
while (it != graph[v].end())
{
if (visit[*it]==false)
dfs(graph, *it);
it++;
}
// increment the timer as you exit the
// recursion for v
++timer;
// upgrade the outtime for that node
outtime[v] = timer;
}
// Returns true if 'u' and 'v' lie on same root to leaf path
// else false.
bool query(int u, int v)
{
return ( (intime[u]outtime[v]) ||
(intime[v]outtime[u]) );
}
// Driver code
int main()
{
// Let us create above shown tree
int n = 9; // total number of nodes
vector graph[n+1];
graph[1].push_back(2);
graph[1].push_back(3);
graph[3].push_back(6);
graph[2].push_back(4);
graph[2].push_back(5);
graph[5].push_back(7);
graph[5].push_back(8);
graph[5].push_back(9);
// Start dfs (here root node is 1)
dfs(graph, 1);
// below are calls for few pairs of nodes
query(1, 5)? cout << "Yes\n" : cout << "No\n";
query(2, 9)? cout << "Yes\n" : cout << "No\n";
query(2, 6)? cout << "Yes\n" : cout << "No\n";
return 0;
}
Java
// Java program to check if given
// pairs lie on same path or not.
import java.util.*;
class GFG{
static int MAX = 100001;
// To keep track of visited vertices in DFS
static boolean []visit = new boolean[MAX];
// To store start and end time of all vertices
// during DFS.
static int []intime = new int[MAX];
static int []outtime = new int[MAX];
// Initially timer is zero
static int timer = 0;
// Does DFS of given graph and fills arrays
// intime[] and outtime[]. These arrays are used
// to answer given queries.
static void dfs(Vector graph[], int v)
{
visit[v] = true;
// Increment the timer as you enter
// the recursion for v
++timer;
// Upgrade the in time for the vertex
intime[v] = timer;
for(int it : graph[v])
{
if (visit[it] == false)
dfs(graph, it);
it++;
}
// Increment the timer as you exit the
// recursion for v
++timer;
// Upgrade the outtime for that node
outtime[v] = timer;
}
// Returns true if 'u' and 'v' lie on
// same root to leaf path else false.
static boolean query(int u, int v)
{
return ((intime[u] < intime[v] &&
outtime[u] > outtime[v]) ||
(intime[v] < intime[u] &&
outtime[v] > outtime[u]));
}
// Driver code
public static void main(String[] args)
{
// Let us create above shown tree
int n = 9; // total number of nodes
@SuppressWarnings("unchecked")
Vector []graph = new Vector[n + 1];
for(int i = 0; i < graph.length; i++)
graph[i] = new Vector();
graph[1].add(2);
graph[1].add(3);
graph[3].add(6);
graph[2].add(4);
graph[2].add(5);
graph[5].add(7);
graph[5].add(8);
graph[5].add(9);
// Start dfs (here root node is 1)
dfs(graph, 1);
// Below are calls for few pairs of nodes
if (query(1, 5))
System.out.print("Yes\n" );
else
System.out.print("No\n");
if (query(2, 9))
System.out.print("Yes\n");
else
System.out.print("No\n");
if (query(2, 6))
System.out.print("Yes\n" );
else
System.out.print("No\n");
}
}
// This code is contributed by Princi Singh
Python3
# contributed by saurabh_jain861
# Python3 program to check if given
# pairs lie on same path or not.
# Does DFS of given graph and fills
# arrays intime[] and outtime[].
# These arrays are used to answer
# given queries.
def dfs(graph, v):
global intime, outtime, visit, MAX, timer
visit.add(v)
# Increment the timer as you enter
# the recursion for v
timer += 1
# Upgrade the in time for the vertex
intime[v] = timer
it = 0
while it < len(graph[v]):
if (graph[v][it] not in visit):
dfs(graph, graph[v][it])
it += 1
# increment the timer as you
# exit the recursion for v
timer += 1
# upgrade the outtime for that node
outtime[v] = timer
# Returns true if 'u' and 'v' lie on
# same root to leaf path else false.
def query(u, v):
global intime, outtime, visit, MAX, timer
return ((intime[u] < intime[v] and
outtime[u] > outtime[v]) or
(intime[v] < intime[u] and
outtime[v] > outtime[u]) )
# Driver code
MAX = 100001
# To keep track of visited vertices in DFS
visit =set()
# To store start and end time of
# all vertices during DFS.
intime = [0] * MAX
outtime = [0] * MAX
# initially timer is zero
timer = 0
# Let us create above shown tree
n = 9 # total number of nodes
graph = [[] for i in range(n+1)]
graph[1].append(2)
graph[1].append(3)
graph[3].append(6)
graph[2].append(4)
graph[2].append(5)
graph[5].append(7)
graph[5].append(8)
graph[5].append(9)
# Start dfs (here root node is 1)
dfs(graph, 1)
# below are calls for few pairs of nodes
print("Yes") if query(1, 5) else print("No")
print("Yes") if query(2, 9) else print("No")
print("Yes") if query(2, 6) else print("No")
# This code is contributed by PranchalK
C#
// C# program to check if given
// pairs lie on same path or not.
using System;
using System.Collections.Generic;
class GFG{
static int MAX = 100001;
// To keep track of visited
// vertices in DFS
static bool []visit =
new bool[MAX];
// To store start and end
// time of all vertices
// during DFS.
static int []intime =
new int[MAX];
static int []outtime =
new int[MAX];
// Initially timer is zero
static int timer = 0;
// Does DFS of given graph
// and fills arrays intime[]
// and outtime[]. These arrays
// are used to answer given queries.
static void dfs(List []graph,
int v)
{
visit[v] = true;
// Increment the timer as
// you enter the recursion
// for v
++timer;
// Upgrade the in time
// for the vertex
intime[v] = timer;
foreach(int it in graph[v])
{
if (visit[it] == false)
dfs(graph, it);
}
// Increment the timer as
// you exit the recursion for v
++timer;
// Upgrade the outtime for
// that node
outtime[v] = timer;
}
// Returns true if 'u' and
// 'v' lie on same root to
// leaf path else false.
static bool query(int u,
int v)
{
return ((intime[u] < intime[v] &&
outtime[u] > outtime[v]) ||
(intime[v] < intime[u] &&
outtime[v] > outtime[u]));
}
// Driver code
public static void Main(String[] args)
{
// Let us create above shown tree
// total number of nodes
int n = 9;
List []graph =
new List[n + 1];
for(int i = 0;
i < graph.Length; i++)
graph[i] = new List();
graph[1].Add(2);
graph[1].Add(3);
graph[3].Add(6);
graph[2].Add(4);
graph[2].Add(5);
graph[5].Add(7);
graph[5].Add(8);
graph[5].Add(9);
// Start dfs (here root
// node is 1)
dfs(graph, 1);
// Below are calls for few
// pairs of nodes
if (query(1, 5))
Console.Write("Yes\n" );
else
Console.Write("No\n");
if (query(2, 9))
Console.Write("Yes\n");
else
Console.Write("No\n");
if (query(2, 6))
Console.Write("Yes\n" );
else
Console.Write("No\n");
}
}
// This code is contributed by Amit Katiyar
Yes
Yes
No
插图:
从下图了解更多,我们可以举一些例子。上面修改的 DFS 算法将导致树的顶点在此处标记的以下时间和时间。现在我们将考虑所有情况。
案例 1:节点 2 和 4:节点 2 的 intime 小于节点 4,但由于 4 在其子树中,因此它的退出时间将大于 4 。因此,条件是有效的,并且两者都在同一条路径上。
案例 2:节点 7 和 6:节点 7 的时间小于节点 6,但由于两个节点都不在彼此的子树中,因此它们的退出时间不符合要求的条件。