Lowest Common Ancestor of Binary Tree(Not Binary Search Tree) - c

I tried working out the problem using Tarjan's Algorithm and one algorithm from the website: http://discuss.techinterview.org/default.asp?interview.11.532716.6, but none is clear. Maybe my recursion concepts are not build up properly. Please give small demonstration to explain the above two examples. I have an idea of Union Find data-structure.
It looks very interesting problem. So have to decode the problem anyhow. Preparing for the interviews.
If any other logic/algorithm exist, please share.

The LCA algorithm tries to do a simple thing: Figure out paths from the two nodes in question to the root. Now, these two paths would have a common suffix (assuming that the path ends at the root). The LCA is the first node where the suffix begins.
Consider the following tree:
r *
/ \
s * *
/ \
u * * t
/ / \
* v * *
/ \
* *
In order to find the LCA(u, v) we proceed as follows:
Path from u to root: Path(u, r) = usr
Path from v to root: Path(v, r) = vtsr
Now, we check for the common suffix:
Common suffix: 'sr'
Therefore LCA(u, v) = first node of the suffix = s
Note the actual algorithms do not go all the way up to the root. They use Disjoint-Set data structures to stop when they reach s.
An excellent set of alternative approaches are explained here.

Since you mentioned job interviews, I thought of the variation of this problem where you are limited to O(1) memory usage.
In this case, consider the following algorithm:
1) Scan the tree from node u up to the root, finding the path length L(u)
2) Scan the tree from node v up to the root, finding the path length L(v)
3) Calculate the path length difference D = |L(u)-L(v)|
4) Skip D nodes in the longer path from the root
5) Walk up the tree in parallel from the two nodes, until you hit the same node
6) Return this node as the LCA

Assuming you only need to solve the problem once (per data set) then a simple approach is to collect the set of ancestors from one node (along with itself), and then walk the list of ancestors from the other until you find a member of the above set, which is necessarily the lowest common ancestor. Pseudocode for that is given:
Let A and B begin as the nodes in question.
seen := set containing the root node
while A is not root:
add A to seen
A := A's parent
while B is not in seen:
B := B's parent
B is now the lowest common ancestor.
Another method is to compute the entire path-to-room for each node, then scan from the right looking for a common suffix. Its first element is the LCA. Which one of these is faster depends on your data.
If you will be needing to find LCAs of many pairs of nodes, then you can make various space/time trade-offs:
You could, for instance, pre-compute the depth of each node, which would allow you to avoid re-creating the sets(or paths) each time by first walking from the deeper node to the depth of the shallower node, and then walking the two nodes toward the root in lock step: when these paths meet, you have the LCA.
Another approach annotates nodes with their next ancestor at depth-mod-H, so that you first solve a similar-but-H-times-smaller problem and then an H-sized instance of the first problem. This is good on very deep trees, and H is generally chosen as the square root of the average depth of the tree.

Related

tree-decomposition of a graph

I need a start point to implement an algorithm in c to generate a tre-decomposition of a graph in input. What i'm looking for it's an algorithm to do this thing. i will like to have a pseudocode of the algorithm, i don't care about the programming language and I do not care about complexity
On the web there is a lot of theory but nothing in practice. I've tried to understand how to do an algorithm that can be implemented in c. But it's to hard
i've tried to use the following information:
Algorithm for generating a tree decomposition
https://math.mit.edu/~apost/courses/18.204-2016/18.204_Gerrod_Voigt_final_paper.pdf
and a lot of other info-material. But nothing of this link was useful.
can anyone help me?
So, here is the algorithm to find a node in the tree.
Select arbitrary node v
Start a DFS from v, and setup subtree sizes
Re-position to node v (or start at any arbitrary v that belongs to the tree)
Check mathematical condition of centroid for v
If condition passed, return current node as centroid
Else move to adjacent node with ‘greatest’ subtree size, and back to step 4
And the algorithm for tree decomposition
Make the centroid as the root of a new tree (which we will call as the ‘centroid tree’)
Recursively decompose the trees in the resulting forest
Make the centroids of these trees as children of the centroid which last split them.
And here is an example code.
https://www.geeksforgeeks.org/centroid-decomposition-of-tree/amp/

A* Graph search

Given the heuristic values h(A)=5, h(B)=1, using A* graph search, it will put A and B on the frontier with f(A)=2+5=7, f(B)=4+1=5, then select B for expansion, then put G on frontier with f(G)=4+4=8, then it will select A for expansion, but will not do anything since both S and B are already expanded and not on frontier, and therefore it will select G next and return a non-optimal solution.
Is my argument correct?
There are two heuristic concepts here:
Admissible heuristic: When for each node n in the graph, h(n) never overestimates the cost of reaching the goal.
Consistent heuristic: When for each node n in the graph and each node m of its successors, h(n) <= h(m) + c(n,m), where c(n,m) is the cost of the arc from n to m.
Your heuristic function is admissible but not consistent, since as you have shown:
h(A) > h(B) + c(A,B), 5 > 2.
If the heuristic is consistent, then the estimated final cost of a partial solution will always grow along the path, i.e. f(n) <= f(m) and as we can see again:
f(A) = g(A) + h(A) = 7 > f(B) = g(B) + h(B) = 5,
this heuristic function does not satisfy this property.
With respect to A*:
A* using an admissible heuristic guarantees to find the shortest path from the start to the goal.
A* using a consistent heuristic, in addition to find the shortest path, also guarantees that once a node is explored we have already found the shortest path to this node, and therefore no node needs to be reexplored.
So, answering your question, A* algorithm has to be implemented to reopen nodes when a shorter path to a node is found (updating also the new path cost), and this new path will be added to the open set or frontier, therefore your argument is not correct, since B has to be added again to the frontier (now with the path S->A->B and cost 3).
If you can restrict A* to be used only with consistent heuristic functions then yes, you can discard path to nodes that have been already explored.
You maintain an ordered priority queue of objects on the frontier. You then take the best candidate, expand in all available directions, and put the new nodes in the priority queue. So it's possible for A to be pushed to the back of queue even though in fact the optimal path goes through it. It's also possible for A to be hemmed in by neighbours which were reached through sub-optimal paths, in which case most algorithms won't try to expand it as you say.
A star is only an a way of finding a reasonable path, it doesn't find the globally optimal path.

Josephus Flavius on segment tree

I would like to solve Joseph Flavius problem using segment tree. I'm almost sure that simple simulation (i.e. lined list) is O(n^2). What I want to achieve is jumping on array for particular distance, taken from segment tree. In other words segment tree will keep information about number of deleted elements and taking some info from tree will allow to find next element to delete in O(1). The problem is that I dont know how to storage info in segment tree to make it working for Joseph Flavius problem.
Some kind of extra values keeped in each node? But how to make queries about next element?
The first thought I had was binary search + sement tree of sums giving O(log^2(n))
Jump from L to R has this properties:
R - L + 1 - sum(L, R) == skip_value
You can easily find R with this property using bin-search.
It gets a little more complicated when you make a full circle but I believe that you get the idea.
If anything is unclear feel free to ask.
(I will also think about log(n) solution)

Fast algorithm mapping int to monotonically increasing int subset

I have encountered variations of this problem multiple times, and most recently it became a bottleneck in my arithmetic coder implementation. Given N (<= 256) segments of known non-negative size Si laid out in order starting from the origin, and for a given x, I want to find n such that
S0 + S1 + ... + Sn-1 <= x < S0 + S1 + ... + Sn
The catch is that lookups and updates are done at about the same frequency, and almost every update is in the form of increasing the size of a segment by 1. Also, the bigger a segment, the higher the probability it will be looked up or updated again.
Obviously some sort of tree seems like the obvious approach, but I have been unable to come up with any tree implementation that satisfactorily takes advantage of the known domain specific details.
Given the relatively small size of N, I also tried linear approaches, but they turned out to be considerably slower than a naive binary tree (even after some optimization, like starting from the back of the list for numbers above half the total)
Similarly, I tested introducing an intermediate step that remaps values in such a way as to keep segments ordered by size, to make access faster for the most frequently used, but the added overhead exceeded gains.
Sorry for the unclear title -- despite it being a fairly basic problem, I am not aware of any specific names for it.
I suppose some BST would do... You may try to add a new numeric member (int or long) to each node to keep a sum of values of all left descendants. Then you'll seek for each item in approximately logarithmic time, and once an item is added, removed or modified you'll have to update just its ancestors on the returning path from the recursion. You may apply some self-organizing tree structure, for example AVL to keep the worst-case search optimal or a splay tree to optimize searches for those most often used items. Take care to update the left-subtree-sums during rebalancing or splaying.
You could use a binary tree where each node n contains two integers A_n
and U_n, where initially
A_n = S_0 + .. S_n and U_n = 0.
Let, at any fixed subsequent time, T_n = S_0 + .. + S_n.
When looking for the place of a query x, you would go along the tree, knowing that for each node m the current corresponding value of T_m is A_m + U_m + sum_{p : ancestors of m, we visited the right child of p to attain m} U_p.
This solves look up in O(log(N)).
For update of the n-th interval (increasing its size by y), you just look for it in the tree, increasing the value of U_m og y for each node m that you visit along the way. This also solves update in O(log(N)).

If one were to code a digital map such as Google Maps using Dijkstra's algorithm to find the shortest path, how will the nodes be represented/coded?

How are the nodes going to be represented? In a map, are these nodes every point on it? Need to know more about the nodes in Dijkstra Algorithm and how to implement them.
The nodes and paths are essentially ambiguous and declarative of what you want them to represent. Is a node an intersection? (scaling on a city size) Is the node a city? (Scaling on a provincial/state, country or continent scale).
The paths would need some sort of weighting between them as well. So if you're picking intersections for nodes what is the weight between node A & B vs. the weight between node B & C? I assume you'd want to use time but you don't have that data readily available do you?
There's more challenges than just nodes I think... without a database of weighting data to run short-path algorithm against you're dead in the water.
In a more abstract way of looking at it you need to determine your nodes (points probably using lat/long coords) and then determine weighting between each node that can be reached. So using the A B C idea again can node A reach node C directly? Can it reach it through B?
4 3
A ------- B--------
\ \
-----------------C
8
A -> B = 4
B -> C = 3
A -> C = 8
A -> B -> C = 7

Resources