What defines an ancestor? More specifically, would E be an ancestor to H? Or is it more simply that F,C,A are ancestors to H? Maybe even G? I would just like to clear up this simple concept.
E is not an ancestor of H. It's an uncle because it's a sibling of F, which is the parent of H.
F,C,A are ancestors of H. That's true.
G is not relevant to H at all.
Tree structure relationship notation can be found here (according to Wikipedia)
A node's "parent" is a node one step higher in the hierarchy (i.e. closer to the root node) and lying on the same branch.
"Sibling" ("brother" or "sister") nodes share the same parent node.
A node's "uncles" are siblings of that node's parent.
A node that is connected to all lower-level nodes is called an "ancestor". The connected lower-level nodes are "descendants" of the
ancestor node.
F, C, A are ancestors of H cause there exists a path from root A to node H and nodes F, C, A appear in the path. node H is called descendant of nodes F, C and A.
Related
I was doning a problem of finding a bridge in a undirected connected graph, I looked up wikipedia for Tarjan's algorithm. Here is what it writes
Tarjan's bridge-finding algorithm
The first linear time algorithm for finding the bridges in a graph was described by
Robert Tarjan in 1974. It performs the following steps:
Find a spanning forest of G
Create a rooted forest F from the spanning forest
Traverse the forest F in preorder and number the nodes. Parent nodes in the forest now have lower numbers than child nodes.
For each node v in preorder (denoting each node using its preorder number), do:
Compute the number of forest descendants ND(v) for this node, by adding one to the sum of its children's descendants.
Compute L(v), the lowest preorder label reachable from v by a path for which all but the last edge stays within
the subtree rooted at v. This is the minimum of the set
consisting of the preorder label of v, of the values of
L(w) at child nodes of v and of the preorder
labels of nodes reachable from v by edges that do not
belong to F.
Similarly, compute H(v), the highest preorder label reachable by a path for which all but the last edge stays within the
subtree rooted at v. This is the maximum of the set
consisting of the preorder label of v, of the values of
H(w) at child nodes of v and of the preorder
labels of nodes reachable from v by edges that do not
belong to F.
For each node w with parent node v, if L(w) = w and H(w) < w + ND(w) then the edge
from v to w is a bridge.
I wonder whether I understand the previous steps wrong, since in my opinion, I think that L(w) = w is never gonna happen except at the root. Where in other cases, L(w) should be at least smaller than the father of w.
Source
The English description of L and H is slightly wrong -- they should exclude paths that contain the parent edge, or else it's as if there are parallel edges between each pair of adjacent nodes, hence no bridges. The algorithm for computing L and H correctly iterates over children only.
Let's Suppose we have a connected graph G, a start vertex s, and a spanning tree T of G and G is undirected. How can I describe an algorithm to decide if T is a depth-first spanning tree rooted at s or not?
All DFS trees T for an undirected graph G have the following property:
{u, v} is an edge in G if and only if u is an ancestor of v in T or v is an ancestor of u in T.
To see why, assume without loss of generality that u is visited before v in the DFS. When building the DFS tree node for u, we will either (1) choose to visit node v as a neighbor of u, making node u a parent of node v, or (2) starting at node u we will visit some other neighbor z, and in recursively exploring z we will visit v, in which case u is a parent of z and z is an ancestor of v.
Moreover, we can make a stronger claim: any tree meeting the above criterion is a DFS tree for some DFS tree of G. Here’s how to see this. Start with the root node of T and look at its children. Given any two subtrees of the root, none of the nodes in those subtrees can be adjacent to one another in G, since otherwise by the above property one of those nodes would have to be an ancestor of the other. Therefore, each subtree consists of a set of nodes that are all reachable from one another via paths that only involve the nodes within that subtree. We can then recursively assemble one possible DFS ordering by starting at the root, recursively building DFS trees for the subgraphs represented by the subtrees in any order we’d like, and gluing those DFS orders together.
With this observation in mind, we can check very quickly with a second DFS whether T can be a DFS tree rooted at s, tracking which nodes have been visited as the DFS runs. After all children of a node v have been processed, check whether all the neighbors of v in graph G have been visited. If so, great! If not, it means that some neighbor of v is neither an ancestor nor a descendant, and the tree isn’t a DFS tree. If this process terminated without finding any violations, the process itself traces out a DFS of G using the edges of T, so T is definitely a valid DFS tree.
This algorithm runs in time O(m + n), which is as fast as possible here. After all, if you don’t look at all the nodes or edges of G, you can’t be sure whether the tree is a valid DFS tree because you can’t check the core property listed above.
Consider the following graph G and consider that at an execution of the algorithm DFS at G, the edges of the graph are characterized as tree edges(t), back edges(b) , forward edges(f) and cross edges(c) as at the following graph. For each node of the graph find the discovery time and the finishing time of the node.
In other words, for each node v of the graph, find the values d[v] and f[v] that associates the algorithm DFS with this node.
Notice that there is only one possible assignment of the values d[v] and f[v].
Could you give me a hint how we can find the initial node in order to start applying the Depth first search algorithm?
Look at node a - what could DFS do in node a? It could go either to b or e. We see that it chose b, because a->b is a tree edge and a->e is a forward edge (check the definition of tree/forward edge). In b the only choice was to visit f. In f DFS could go either to a, e or g. We can assume that it tried to visit a (f->a is marked as back edge, so everything is correct until now), than it visited e and than tried to visit b. However, we now have a problem with edge f->g. It is marked as a cross edge, which means that DFS had already visited g before. Otherwise, this edge would have been marked as a tree edge. So, we know that a was not the initial node. We need to try other options. What about c? Again, all of edges coming out of c are marked as cross, not tree, so c was not the initial node.
What about d? If DFS started in d, it could go from d to g and that's what happened because d->g is marked as tree edge. There were no nodes to go from g so it backtraced to d and visited h. From h it tried to visit g but it has already visited earlier, so h->g is marked as cross - correct. Great, so d was the initial node for this DFS execution. After visiting a connected component which contains d, g and h, DFS could start again either from a or c but we already know that it has not started from c because of those cross edges. So it started from a and after visiting b, f and e it started from c.
Tree edges should form a forest. A node at wich the DFS could have started is a node that has no incoming tree edges.
I'm wondering what the consensus is on the definition of "ancestor" in a computer science context.
I only ask because in Introduction to Algorithms, Second Edition, p. 259 there is a description of the algorithm Tree-Successor(x) that seems odd. In finding the successor of node x,
[...] if the right subtree of node x is empty and x has a successor y, then y is the lowest ancestor of x whose left child is also an ancestor of x.
In a binary search tree with a root having key 2 and children 1 and 3, the successor of 1 is its parent 2. In this case, x is the left child of x's successor, y. According to the book's definition, then, x must be its own ancestor, unless I'm missing something.
I haven't found anything in the errata about this.
It's merely a matter of definition, but in this case, yes. CLRS define an ancestor of x as any node on the unique path from the root to x, which by definition includes x.
The sentence fragment you quoted begins by mentioning exercise 12.2-6 on the next page, which specifies this:
(Recall that every node is its own ancestor.)
:-)
Is a node in a tree considered its own ancestor?
Not normally, AFAIK. For example, in the Wikipedia page on binary trees, ancestor is defined thus:
If a path exists from node p to node q, where node p is closer to the root node than q, then p is an ancestor of q and q is a descendant of p.
But apparently that text book's definition of ancestor is such that a node is its own ancestor. This definition is not exactly intuitive, but a textbook is free to introduce its own definitions for the terminology that it uses. Maybe this definition simplifies some of the related descriptions / theorems / etc.
No, a node is not ancestor of itself. According to me it should be: if the right subtree of node x is empty and x has a successor y, then y is the lowest ancestor of x whose left child is either x or an ancestor of x. but the code given in the book supposedly handling such type of cases.
Here is a tree:
There will be one root.
Each tree node has zero or more children.
It is allowed that two nodes points to the same child. Say, both node A
and node B has child C.
However, it is prohibited that,
Node A is an offspring of Node B, and
Node B is an offspring of Node A.
One prohibited case is
Node A has a child Node C and Node D,
Both Node C and D has a child node E,
Node E has a child of A.
The question is, how to determine this circle in a fastest manner?
UPDATE: I realize this is to find any cycle in a directed graph. Just now I managed to think out a solution similar to Tarjan's algorithm.
Thanks for comments.
Do a Depth First Search through the tree. If at any point you find a node that is already in your backtracking stack, there is a circle.
circles can be found using 2 pointers and advancing them at different intervals. Eventually the pointers will match, indicating a loop, or the "faster" one will reach then end. The question is usually asked of linked lists though, not trees.