级别祖先问题是将给定的根树T预处理为数据结构的问题,该数据结构可以确定从树的根到给定深度的给定节点的祖先。此处,树中任何节点的深度是从树的根部到节点的最短路径上的边数。
给定树表示为具有n个节点和n-1个边的无向连接图。
解决上述查询的想法是使用跳转指针算法并对O(n log n)时间中的树进行预处理,并在O(logn)时间中对应答级别祖先查询。在跳转指针中,有一个从节点N到N的第j个祖先的指针,用于
j = 1、2、4、8,…,依此类推。我们将这些指针称为Jump N [i],其中
跳u [i] = LA(N,depth(N)– 2 i )。
当要求算法处理查询时,我们使用这些跳转指针反复跳转到树上。跳转数最多为log n,因此可以在O(logn)时间内回答查询。
因此,我们存储2我的每个节点的始祖日,也发现从树的根部各节点的深度。
现在我们的任务简化为找到节点N的第(depth(N)– 2 i )个祖先。让我们将X表示为(depth(N)– 2 i ),并且让X的b位为设置的位(1),表示为s1,s2,s3,… sb。
X = 2 (s1) + 2 (s2) +…+ 2 (sb)
现在的问题是如何找到每个节点的每个节点和深度的2级焦耳的祖先从树的根?
最初,我们知道每个节点的第2 0个祖先是其父节点。我们可以递归计算第2 j个祖先。我们知道2 j个祖先是2 J-1 2 J-1个祖先的祖先第。为了计算每个节点的深度,我们使用祖先矩阵。如果我们发现第k个元素的根节点在第j个索引处存在,则该节点的深度仅为2 j,但如果第k个元素的祖先数组中不存在根节点,则该深度为第k个元素的深度是2 (第k行的最后一个非零祖先的索引) +第k行的最后一个索引处存在的祖先的深度。
下面是使用动态编程填充祖先矩阵和每个节点深度的算法。在这里,我们将根节点表示为R,并且最初假定根节点的祖先为0 。我们还使用-1初始化深度数组,这意味着未设置当前节点的深度,我们需要找到其深度。如果当前节点的深度不等于-1,则意味着我们已经计算出其深度。
we know the first ancestor of each node so we take j>=1,
For j>=1
ancstr[k][j] = 2jth ancestor of k
= 2j-1th ancestor of (2j-1th ancestor of k)
= ancstr[ancstr[i][j-1][j-1]
if ancstr[k][j] == R && depth[k] == -1
depth[k] = 2j
else if ancstr[k][j] == -1 && depth[k] == -1
depth[k] = 2(j-1) + depth[ ancstr[k][j-1] ]
让我们用下图来了解这个算法。
在给定图中,我们需要计算值为8的节点的第一级祖先。首先,我们创建存储第2个节点祖先的祖先矩阵。现在,节点8的2 0祖先是10 ,类似地,节点10的2 0祖先是9 ,对于节点9来说是1 ,对于节点1来说是5 。基于上述算法,节点8的第一级祖先是节点8的(depth(8)-1)祖先。我们已经预先计算了每个节点的深度,深度8为5,所以我们最终需要找到(5-1 )=节点8的第四祖先,其等于2 1 [2 1祖先节点8的]的第祖先和2 1节点8的第祖先是2 0 [2 0节点8的第祖先]次祖先。所以,2 0 [2 0节点8的第祖先]次祖先是与值9和2 1节点9的第祖先是具有值5节点的节点。因此,通过这种方式,我们可以以O(logn)时间复杂度来计算所有查询。
// CPP program to implement Level Ancestor Algorithm
#include
using namespace std;
int R = 0;
// n -> it represent total number of nodes
// len -> it is the maximum length of array to hold
// ancestor of each node. In worst case,
// the highest value of ancestor a node can have is n-1.
// 2 ^ len <= n-1
// len = O(log2n)
int getLen(int n)
{
return (int)(log(n) / log(2)) + 1;
}
// ancstr represents 2D matrix to hold ancestor of node.
// Here we pass reference of 2D matrix so that the change
// made occur directly to the original matrix
// depth[] stores depth of each node
// len is same as defined above
// n is total nodes in graph
// R represent root node
void setancestor(vector >& ancstr,
vector& depth, int* node, int len, int n)
{
// depth of root node is set to 0
depth[R] = 0;
// if depth of a node is -1 it means its depth
// is not set otherwise we have computed its depth
for (int j = 1; j <= len; j++) {
for (int i = 0; i < n; i++) {
ancstr[node[i]][j] = ancstr[ancstr[node[i]][j - 1]][j - 1];
// if ancestor of current node is R its height is
// previously not set, then its height is 2^j
if (ancstr[node[i]][j] == R && depth[node[i]] == -1) {
// set the depth of ith node
depth[node[i]] = pow(2, j);
}
// if ancestor of current node is 0 means it
// does not have root node at its 2th power
// on its path so its depth is 2^(index of
// last non zero ancestor means j-1) + depth
// of 2^(j-1) th ancestor
else if (ancstr[node[i]][j] == 0 &&
node[i] != R && depth[node[i]] == -1) {
depth[node[i]] = pow(2, j - 1) +
depth[ancstr[node[i]][j - 1]];
}
}
}
}
// c -> it represent child
// p -> it represent ancestor
// i -> it represent node number
// p=0 means the node is root node
// R represent root node
// here also we pass reference of 2D matrix and depth
// vector so that the change made occur directly to
// the original matrix and original vector
void constructGraph(vector >& ancstr,
int* node, vector& depth, int* isNode,
int c, int p, int i)
{
// enter the node in node array
// it stores all the nodes in the graph
node[i] = c;
// to confirm that no child node have 2 ancestors
if (isNode == 0) {
isNode = 1;
// make ancestor of x as y
ancstr[0] = p;
// ifits first ancestor is root than its depth is 1
if (R == p) {
depth = 1;
}
}
return;
}
// this function will delete leaf node
// x is node to be deleted
void removeNode(vector >& ancstr,
int* isNode, int len, int x)
{
if (isNode[x] == 0)
cout << "node does not present in graph " << endl;
else {
isNode[x] = 0;
// make all ancestor of node x as 0
for (int j = 0; j <= len; j++) {
ancstr[x][j] = 0;
}
}
return;
}
// x -> it represent new node to be inserted
// p -> it represent ancestor of new node
void addNode(vector >& ancstr,
vector& depth, int* isNode, int len,
int x, int p)
{
if (isNode[x] == 1) {
cout << " Node is already present in array " << endl;
return;
}
if (isNode[p] == 0) {
cout << " ancestor not does not present in an array " << endl;
return;
}
isNode[x] = 1;
ancstr[x][0] = p;
// depth of new node is 1 + depth of its ancestor
depth[x] = depth[p] + 1;
int j = 0;
// while we don't reach root node
while (ancstr[x][j] != 0) {
ancstr[x][j + 1] = ancstr[ancstr[x][j]][j];
j++;
}
// remaining array will fill with 0 after
// we find root of tree
while (j <= len) {
ancstr[x][j] = 0;
j++;
}
return;
}
// LA function to find Lth level ancestor of node x
void LA(vector >& ancstr, vector depth,
int* isNode, int x, int L)
{
int j = 0;
int temp = x;
// to check if node is present in graph or not
if (isNode[x] == 0) {
cout << "Node is not present in graph " << endl;
return;
}
// we change L as depth of node x -
int k = depth[x] - L;
// int q = k;
// in this loop we decrease the value of k by k/2 and
// increment j by 1 after each iteration, and check for set bit
// if we get set bit then we update x with jth ancestor of x
// as k becomes less than or equal to zero means we
// reach to kth level ancestor
while (k > 0) {
// to check if last bit is 1 or not
if (k & 1) {
x = ancstr[x][j];
}
// use of shift operator to make k = k/2
// after every iteration
k = k >> 1;
j++;
}
cout << L << "th level acestor of node "
<< temp << " is = " << x << endl;
return;
}
int main()
{
// n represent number of nodes
int n = 12;
// initialization of ancestor matrix
// suppose max range of node is up to 1000
// if there are 1000 nodes than also length
// of ancestor matrix will not exceed 10
vector > ancestor(1000, vector(10));
// this vector is used to store depth of each node.
vector depth(1000);
// fill function is used to initialize depth with -1
fill(depth.begin(), depth.end(), -1);
// node array is used to store all nodes
int* node = new int[1000];
// isNode is an array to check whether a
// node is present in graph or not
int* isNode = new int[1000];
// memset function to initialize isNode array with 0
memset(isNode, 0, 1000 * sizeof(int));
// function to calculate len
// len -> it is the maximum length of array to
// hold ancestor of each node.
int len = getLen(n);
// R stores root node
R = 2;
// construction of graph
// here 0 represent that the node is root node
constructGraph(ancestor, node, depth, isNode, 2, 0, 0);
constructGraph(ancestor, node, depth, isNode, 5, 2, 1);
constructGraph(ancestor, node, depth, isNode, 3, 5, 2);
constructGraph(ancestor, node, depth, isNode, 4, 5, 3);
constructGraph(ancestor, node, depth, isNode, 1, 5, 4);
constructGraph(ancestor, node, depth, isNode, 7, 1, 5);
constructGraph(ancestor, node, depth, isNode, 9, 1, 6);
constructGraph(ancestor, node, depth, isNode, 10, 9, 7);
constructGraph(ancestor, node, depth, isNode, 11, 10, 8);
constructGraph(ancestor, node, depth, isNode, 6, 10, 9);
constructGraph(ancestor, node, depth, isNode, 8, 10, 10);
// function to pre compute ancestor matrix
setancestor(ancestor, depth, node, len, n);
// query to get 1st level ancestor of node 8
LA(ancestor, depth, isNode, 8, 1);
// add node 12 and its ancestor is 8
addNode(ancestor, depth, isNode, len, 12, 8);
// query to get 2nd level ancestor of node 12
LA(ancestor, depth, isNode, 12, 2);
// delete node 12
removeNode(ancestor, isNode, len, 12);
// query to get 5th level ancestor of node
// 12 after deletion of node
LA(ancestor, depth, isNode, 12, 1);
return 0;
}
1th level acestor of node 8 is = 5
2th level acestor of node 12 is = 1
Node is not present in graph