1

Tree is given. Edges information is given as a adjacency list format.

Nodes numbers are in 1, 2, 3, ....N.

Root of the tree is 1 always.

Now, two indices are given. Question is wheather any index of the above two lies on the ways from root (1) to other index.

Constraints

  1. Numbers of nodes is 10^5.
  2. Maximum Queries that can fire, 10^5.
  3. Parent Id is always smaller than child.

Example :-

Edges given :-

1 2
2 5
1 3
3 7
1 4
4 8
8 9

Questions -

2 3  ( Answer - Not possible, from 1 to 2, 3 never comes in between and from 1 to 3, 2 never comes in between.)
8 9  ( Answer - Possible, 8 lies in between 1 and 9)

Next example :-

1 2
2 4
2 5
1 3
3 6
3 7
3 8
devsda
  • 3,746
  • 9
  • 47
  • 82
  • Do you have a special relation for the edge up to the parent node, like `node.parent`, or is the ID of the parent always smaller than that of the child, or are the edges always in the form `(parent, child)`? In these cases you could just go 'up' until you find either the root or the other node. – tobias_k Jun 21 '14 at 11:08
  • No such relation is mentioned. but if I iteraot up, as you said, then it will take O(TOtal Number of nodes) in worst case for each query. – devsda Jun 21 '14 at 11:11
  • In the worst case that the tree is a chain. In the average case, it should be O(logn). – tobias_k Jun 21 '14 at 11:11
  • Do you have any other approach, by storing something so that query response time will reduce ? – devsda Jun 21 '14 at 11:13
  • Well, you could of course always cache something, lika create a hashmap (nodeid -> set of parent nodes), Then lookup is O(1), but this requires about O(nlogn) space, unless I'm mistaken. – tobias_k Jun 21 '14 at 11:16
  • [This other question](http://stackoverflow.com/q/1484473/1639625) is about binary trees, but I think it applies to your problem, too: if the lowest common ancestor is one of the nodes, then it lies in between the other and the root. – tobias_k Jun 21 '14 at 11:22
  • @tobias_k If Id of the parent is smaller than child, how can we proceed ? – devsda Jun 21 '14 at 11:28
  • If parent's ID is smaller that that of the child, then for node `x` and edge `(x,y)`, it is clear whether it leads towards the root (yx). So you just have to follow the edged pointing towards root until you find either root or the other node (repeat for both nodes). – tobias_k Jun 21 '14 at 11:31
  • @tobias_k But again in worst case, that is , a single chain. It takes linear time. Problem is again same. – devsda Jun 21 '14 at 11:36
  • As I said, you either have that time complexity (average case O(logn), worst case O(n)), or you can create some supporting data structures, get time complexity of O(1), but a space complexity of at least O(nlogn). Your choice. – tobias_k Jun 21 '14 at 11:38
  • Duplicate is not my answer. – devsda Jun 21 '14 at 11:53
  • Then please clarify your question: What exactly is the data that you have and the assumptions you can make, what data structures can you make use of, what is your desired complexity in both time and space, etc. – tobias_k Jun 21 '14 at 11:56
  • @tobias_k Now please see my question, I added my constraints. – devsda Jun 21 '14 at 12:08
  • Again my question: Is the ID of the parent of a node always smaller than the ID of the child node? This is the case in your examples, but it is not clear whether that's always the case. – tobias_k Jun 21 '14 at 12:51
  • @tobias_k It is always the case. – devsda Jun 21 '14 at 13:50
  • @tobias_k Please help me in above problem. – devsda Jun 21 '14 at 14:41

3 Answers3

1

Assuming that the ID of the parent node (the one closer to the root) is always greater than the ID of the child node, as it seems to be the case in your examples, you can easily see from any edge whether it leads towards the root or away from it. Thus the basic algorithms would be:

given nodes n and m
find edge (x, n) or (n, x) such that x < n
repeat with node x until x is m or x is root

I don't think there is a faster way to find out whether one node is 'between' root and the other. You can, of course, improve performance by using appropriate data structures, e.g by first mapping each node to its parent in a hash set. Here's an example in Python:

ROOT = 1
edges = ((1, 2), (2, 4), (2, 5), (1, 3), (3, 6), (3, 7), (3, 8))

parents = {}
for edge in edges:
    parent, child = min(edge), max(edge)
    parents[child] = parent

def is_anchestor_of(p, c):
    while c > p:
        if parents[c] == p:
            return True
        c = parents[c]
    return False

Complexity in both, time and space, for creating the hash map is O(n) in the number of nodes or edges (which is pretty much the same in a tree), and average case complexity for is_anchestor_of is O(logn), but can deteriorate to O(n) in the worst case (extremely unbalanced tree, or chain).

You can improve the lookup complexity by mapping each node to all of it's anchestors. Complexity for creating this hash map would be O(n log n), unless I'm mistaken, both time and space, but could probably go up to O(n^2) in the worst case. In any case, using this structure, the complexity of is_anchestor_of is O(1), since it is just a lookup in a hash map followed by a lookup in a hash set.

anchestors = {}
for node in parents:
    anchestors[node] = set([ ROOT ])
    p = parents[node]
    while p != ROOT:
        anchestors[node].add(p)
        p = parents[p]

def is_anchestor_of(p, c):
    return p in anchestors[c]

In both cases, just check whether one is an anchestor of the other.

def on_one_path(x, y):
    return is_anchestor_of(x, y) if x < y else is_anchestor_of(y, x)
print on_one_path(3, 8)

Update: Seems there is a more efficient approach; see @Loha's answer.

tobias_k
  • 74,298
  • 11
  • 102
  • 155
  • But again in the worst case, it takes polynomial complexity. It will fail in my case. – devsda Jun 21 '14 at 19:20
  • @devnull I have to ask this: Do you have reason to believe that there is a faster algorithm? If so, what would it look like? And is the worst case really that relevant for you? Did you try to implement that algorithm and see how long it takes on _your_ trees? – tobias_k Jun 21 '14 at 19:35
  • @devnull Care to explain why the downvote? Receiving another answer that is (in your opinion) better than the others does not mean that you need to downvote those, unless they contain wrong information, which is not the case in neither answer. If you must, remove the upvote, and I'd consider even that rude, but downvoting is just plain wrong. – tobias_k Jun 26 '14 at 08:33
  • The answer you gave is a naive approach. I can say this thing that your answer is correct but not useful for me. I was trying to remove my upvote, but by mistakenly it downvoted. can you please edit something, so that I can remove my downvote. Really Really sorry for that. – devsda Jun 26 '14 at 09:20
  • 1
    @devnull Done editing. Also, if the answer is so 'naive', why didn't you write something like "I've already tried this and this naive approach, but they do not suffice"? Would have saved me _lots_ of work. – tobias_k Jun 26 '14 at 09:36
1

Algorithm is as follows :-

  1. Start from root, and note IN TIME and OUT TIME for each and every node. As you said Adjacency list is given. That you can easily done by using DFS in Time complexity O(Total number of nodes ).
  2. One node "B" is descendent to other node "A", only in one condition.

    IN_TIME(A) < IN_TIME(B) and OUT_TIME(B) < OUT_TIME(A)

This way, Query will handle in O(1) time.

Loha
  • 50
  • 6
  • Thanks for your efficient solution. – devsda Jun 26 '14 at 07:41
  • Nice idea, and actually seems to make sense. However, your answer is a bit hard to understand IMHO. +1 if you add some more explanation (e.g. what exactly you mean by in and out time) or some pseudocode, so it is more helpful for future visitors – tobias_k Jun 26 '14 at 08:40
0

For each node, construct a sorted list of the nodes above it in the tree:

1 {}
2 {1}
3 {1}
4 {1}
5 {1, 2}
7 {1, 3}
8 {1, 4}
9 {1, 4, 8}

Then, when when given a query, search for each node in the list belonging to the other (O(logN)):

2 3: 2 is not in {1}, and 3 is not in {1}, so the answer is no.
8 9: 9 is not in {1, 4}, but 8 is in {1, 4, 8}, so the answer is yes.

EDIT:

As tobias_k points out, there are other structures such as a hash table which would make the search O(1). And constructing the table is O(n), trivially-- just iterate over the list of edges and put 'em in.

Beta
  • 86,746
  • 10
  • 132
  • 141
  • 1
    Or a hash set, then lookup is O(1). – tobias_k Jun 21 '14 at 11:17
  • @Beta But how can you make list of all parent indexesfor each node index ? – devsda Jun 21 '14 at 11:21
  • @Beta How can you construct `For each node, construct a sorted list of the nodes above it in the tree:` ? – devsda Jun 21 '14 at 13:53
  • @Beta Sorry for downvote. I was trying to remove my upvote, but by mistakenly it goes to downvote. Can you please do some edit, so that I can remove my downvote. Sorry again for that. – devsda Jun 26 '14 at 09:21