How to find the shortest path of a weighted tree? - algorithm

I have a weighted tree which looks like (weights are in brackets)
A1
/ \
B1(3) B2(2)
/ \ / \
C1(1) C2(3) C3(4)
/ \ / \ / \
D1(8) D2(7) D3(2) D4(5)
......
So, each node has two children. And each node shares a child with a neighbour node. A depth of the tree can be very high.
3 + 1 + 8 = 12
3 + 1 + 7 = 11
3 + 3 + 7 = 13 ... and so on
What is the best way to find the shortest path? As a result I need not a sum of weights but a full path (lets say A1-B2-C3-D3).
I will be more than happy if you could reference me to the right algorithm.. Or provide java/pseudo code solution.
Thank you!
Update
I am looking for a full path from top to bottom

This may be a natural Dynamic Programming (DP) problem due to the child sharing property. I suggest using a bottom-up DP algorithm to solve this problem.
Define the state of each node as SP(n), which means shortest path from that node. We could notice that the SP(n) is only dependent on the SP(c), where c is child of n. And because of the child sharing property, the SP(n) may be reused by n's parents.
The state transformation equation is listed as below:
SP(n) = min {for every c of n's children | SP(c) + weight(c)}
As for implementation, we scan bottom-up from leaves to compute the SP(n) until we reach the root. And the time cost is O(n) since we compute it in one run.

You may want to look at Alpha-beta-pruning. This algorithm basically removes part of the search tree as soon as they are known to be obsolete, i.e. a shorter path to the same position is already known.

Related

Depth vs Level of a Tree

Are they the same thing?
In this article Height,Depth and Level of a Tree
Depth is defined as the number of edges from a node to the tree's root node while
Level is defined as
1 + the number of connections between the node and the root."
or basically depth + 1
and in this link
What is level of root node in a tree?
It is said that level can either start with 1 or 0 which makes it the same with depth if it starts with 0
So which is which? If it is 1 + depth then what is the use of adding 1?
well, it will be best explained by a image, just see below:
// I've used 1 for roots level
// though some people consider roots level as 0, so you can use either 0 or 1
// I would prefer to use 1
// but its your choice
o(depth=0, height=3, lev=1)
/ \
(depth=1, height=2, lev=2)o o(depth=1, height=1, lev=2)
/ / \
(depth=2, height=1, lev=3)o o o(depth=2, height=0, lev=3)
/
(depth=3, height=0, lev=4)o
I hope its clear to you now...
The question was asked because there were already a lot about height vs depth of a tree but not a clear distinction in level and depth of a tree and is often times used interchangeably.
So as I have read here, in different articles and in a book;
The level is depth + 1. It is not the same with depth although some choose to start the level with 0.
Depth is mostly used in relation to the root as
Depth is the number of edges from the root to a node
So it is mostly treated as a property of a node while the level is mostly used as a whole e.g.
Width is the number of nodes in a level
Or in
A perfect binary tree is where all internal nodes have two children and all leaves are at the same level
So level is like steps in a tree wherein the root node is the first step and it just so happen that it shared the same pattern with the depth of a node.
Although there is no single definition, to distinguish the two the level is mostly taken as depth + 1.

Find shortest path between two nodes in directed weighted graph

I have a directed weighted graph G = <V, E>. I need to find shortest path between s and t in O((V + E)*logV). It would be a really simple task, if I have a classical metric weight of path. But it is not.
Weight of path = two heaviest edges in this path.
Therefore, classic Dijkstra's algorithm with modified binary heap does not work. I think, that I have to modify this algorithm. I'm trying to do it, but I have no success.
Example.
Weight of path between 3 and 5 = 4 + 2 = 6
Weight of path between 3 and 7 = 4 + 4 = 8
Edited my answer based on counter example by David Eisenstat.
The example you give in your question is not a good example of when Dijkstra will not work.
I believe you can do this by modifying Dijkstra. The key is to keep track of multiple alternatives for each Vertex. Not only do you have to store the weigths that make up the shortest path, you also have to store alternatives where max < shortest.max and min > shortest.min.
Dijkstra is greedy, so what you have to figure out is: is it possible that once a shortest path is determined, another path can be found that turns out to be shorter. Because you will discover paths in increasing length, this is not possible.

Disjoint-set forests - why should the rank be increased by one when the find of two nodes are of same rank?

I am implementing the disjoint-set datastructure to do union find. I came across the following statement in Wikipedia:
... whenever two trees of the same rank r are united, the rank of the result is r+1.
Why should the rank of the joined tree be increased by only one when the trees are of the same rank? What happens if I simply add the two ranks (i.e. 2*r)?
First, what is rank? It is almost the same as the height of a tree. In fact, for now, pretend that it is the same as the height.
We want to keep trees short, so keeping track of the height of every tree helps us do that. When unioning two trees of different height, we make the root of the shorter tree a child of the root of the taller tree. Importantly, this does not change the height of the taller tree. That is, the rank of the taller tree does not change.
However, when unioning two trees of the same height, we make one root the child of the other, and this increases the height of that overall tree by one, so we increase the rank of that root by one.
Now, I said that rank was almost the same as the height of the tree. Why almost? Because of path compression, a second technique used by the union-find data structure to keep trees short. Path compression can alter an existing tree to make it shorter than indicated by its rank. In principle, it might be better to make decisions based on the actual height than using rank as a proxy for height, but in practice, it is too hard/too slow to keep track of the true height information, whereas it is very easy/fast to keep track of rank.
You also asked "What happens if I simply add the two ranks (i.e. 2*r)?" This is an interesting question. The answer is probably nothing, meaning everything will still work just fine, with the same efficiency as before. (Well, assuming that you use 1 as your starting rank rather than 0.) Why? Because the way rank is used, what matters is the relative ordering of ranks, not their absolute magnitudes. If you add them, then your ranks will be 1,2,4,8 instead of 1,2,3,4 (or more likely 0,1,2,3), but they will still have exactly the same relative ordering so all is well. Your rank is simply 2^(the old rank). The biggest danger is that you run a larger risk of overflowing the integer used to represent the rank when dealing with very large sets (or, put another way, that you will need to use more space to store your ranks).
On the other hand, notice that by adding the two ranks, you are approximating the size of the trees rather than the heights of the trees. By always adding the two ranks, whether they are equal or not, then you are exactly tracking the sizes of the trees. Again, everything works just fine, with the same caveats about the possibility of overflowing integers if your trees are very large.
In fact, union-by-size is widely recognized as a legitimate alternative to union-by-rank. For some applications, you actually want to know the sizes of the sets, and for those applications union-by-size is actually preferabe to union-by-rank.
Because in this case - you add one tree is a "sub tree" of the other - which makes the original subtree increase its size.
Have a look at the following example:
1 3
| |
2 4
In the above, the "rank" of each tree is 2.
Now, let's say 1 is going to be the new unified root, you will get the following tree:
1
/ \
/ \
3 2
|
4
after the join the rank of "1" is 3, rank_old(1) + 1 - as expected.1
As for your second question, because it will yield false height for the trees.
If we take the above example, and merge the trees to get the tree of rank 3. What would happen if we then want to merge it with this tree2:
9
/ \
10 11
|
13
|
14
We'll find out both ranks are 4, and try to merge them the same way we did before, without favoring the 'shorter' tree - which will result in trees with higher height, and ultimately - worse time complexity.
(1) Disclaimer: The first part of this answer is taken from my answer to a similar question (though not identical due to your last part of the question)
(2) Note that the above tree is syntatically made, it cannot be created in an optimized disjoint forests algorithms, but it still demonstrates the issues needed for the answer.
If you read that paragraph in a little more depth, you'll realize that rank is more like depth, not size:
Since it is the depth of the tree that affects the running time, the tree with smaller depth gets added under the root of the deeper tree, which only increases the depth if the depths were equal. In the context of this algorithm, the term "rank" is used instead of "depth" ...
and a merge of equal depth trees only increases the depth of the tree by one since the root of the one is added to the root of the other.
Consider:
A D
/ \ merged with / \
B C E F
is:
A
/|\
B C D
/ \
E F
The depth was 2 for both, and it's 3 for the merged one.
Rank represents the depth of the tree, not the number of nodes in it. When you join a tree with a smaller rank with a tree with a larger rank, the overall rank remains the same.
Consider adding a tree with rank 4 to the root of the tree of rank 6: since we added a node above the root of the depth-4 tree, that subtree now has a rank of 5. The subtree to which we've added our depth-4 tree, however, is 6, so the rank does not change.
Now consider adding a tree with rank 6 to the root of a second tree of rank 6: since the root of the first depth-6 tree now has an extra node above it, the rank of that subtree (and the tree overall) changes to 7.
Since the rank of the tree determines the processing speed, the algorithm tries to keep the rank as low as possible by always attaching a shorter tree to the taller one, keeping the overall rank unchanged. The rank changes only when the trees have identical ranks, in which case one of them gets attached to the root of the other, bumping up the rank by one.
Actually Here two important properties should be known very well to us ....
1) What is Rank ?
2) Why Rank is Used ???
Rank is nothing but the depth of a tree .U can say rank as depth (level) of a tree . When we make union nodes then these (graph nodes ) will be formed as a tree with an ultimate root node.Rank is expressed only for those root nodes .
A merged with D
Initially A has rank (level) 0 and D has rank(level) 0 . So u can merge them making anyone of them as a root . Because if u make A as root the rank(level) will be 1
and if u make D as a root then the rank will also be 1
A
`D
Here rank ( level ) is 1 when root is A .
Now think for another ,
A merge B -----> A
`D `C / \
D B
\
C
So the level will be increased by 1 , see exactly without root (A) there is at most height / depth / rank is 2 . rank[ 1] -> {D,B} and rank [2] -> {C} ................
Now our main objective is to make tree with minimum rank(depth) as possible while merging ..
Now when two differnt rank tree merge ,then
A(rank 0) merge B(rank 1)---> B Here merged tree rank is 1 same as high rank (1)
`C / \
A C
When small rank goes under over high rank . Then the merged tree's rank(height/depth) will be the same rank associated with higher rank tree .That means the rank will not increase , the merged tree rank will be same as higher rank before ...
But if we will do the reverse work means high rank tree goes under over low rank tree then see ,
A ( rank 0 ) merge B (rank 1 ) --> A ( merged tree rank 2 greater than both )
`C `B
`C
So , whatever is seen from following observation is that if we try to keep rank (height) of merged tree as minimum possible then , we have to choose the first process. i think this part is clear !!
Now u have to understand what is our objective to keep tree's height minimum as possible ..........
when we use disjoint set union then for path compression ( finding ultimate root with whom a node is connected ) when we traverse from a node to it's root node then if it's height (rank) is long then time processing will be slow .That's why when we try to merge two trees then we try to keep heigh/depth/rank as minimum as possible

My Lowest Common Ancestor Algo gives different but logically correct answer than other Algos found online

I was writing a program for finding the Lowest Common Ancestor of a pair of nodes in a Binary Search Tree(given the elements exist in the tree). My Logic was:
Start from root.
If both elems greater than the current element then return root(this is where it differs).
Else if both lesser than root, then recurse on left subtree.
else return root(one is lesser other is greater).
However, Algos found online(http://leetcode.com/2011/07/lowest-common-ancestor-of-a-binary-search-tree.html) change step 2, in this case they recurse on right subtree and therefore for the following tree:
2
/ \
1 4
/ \
3 5
and input 3 and 5,
my algo gives 2, while the other algos give 4 as the output.
So, is it me understanding the definition of LCA wrong ('cause 2 is lower than 4 and is a common ancestor) or is my Algo correct.
The LCA of v and w in T is the shared ancestor of v and w that is located farthest from the root. And hence the online version is the correct one which differs from your understanding.
Check out this LCA definition
Also you can avoid recursion if you wish. Just trace the route back to the root node from your candidates. The first common node is the LCA

Finding closest parent for two nodes

what is the best way to find closest parents for two given nodes on the tree?
So if I have:
1
/ \
2 3
/ \ / \
4 5 6 7
closest parent for 5 and 6 would be one.
Thanks
This problem is called Lowest Common Ancestor(LCA) problem. (Google it)
One query can be answered by simply climbing up along their parent links until they meet:
The first step is to let the lower node climb until they are in the same height.
The second step is to let them climb simultaneously until they meet at the same node.
Then that node is the LCA of these two node.
If you need to process multiple queries, you need to use more advanced algorithm. The most time-efficient algorithm use O(n) time to preprocess, and O(1) time for each query, where n is the total nodes in the tree.

Resources