Diameter of a rooted k-ary tree - algorithm

I'm trying to find a linear-time algorithm using recursion to solve the diameter problem for a rooted k-ary tree implemented with adjacency lists. The diameter of a tree is the maximum distance between any couple of leaves. If I choose a root r (that is, a node whose degree is > 1), it can be shown that the diameter is either the maximum distance between two leaves in the same subtree or the maximum distance between two leaves of a path that go through r. My pseudocode for this problem:
Tree-Diameter(T,r)
if degree[r] = 1 then
height[r] = 0
return 0
for each v in Adj[r] do
for i = 1 to degree[r] - 1 do
d_i = Tree-Diameter(T,v)
height[r] = max_{v in Adj[r]} (height[v]
return max(d_i, max_{v in V} (height[v]) + secmax_{v in V} (height[v], 0) + 1)
To get linear time, I compute the diameter AND the height of each subtree at the same time. Then, I choose the maximum quantity between the diameters of each subtrees and the the two biggest heights of the tree + 1 (the secmax function chooses between height[v] and 0 because some subtree can have only a child: in this case, the second biggest height is 0). I ask you if this algorithm works fine and if not, what are the problems? I tried to generalize an algorithm that solve the same problem for a binary tree but I don't know if it's a good generalization.
Any help is appreciated! Thanks in advance!

In all in tree for finding diameter do as below:
Select a random node A, run BFS on this node, to find furthermost node from A. name this node as S.
Now run BFS starting from S, find the furthermost node from S, name it D.
Path between S and D is diameter of your tree. This algorithm is O(n), and just two time traverses tree. Proof is little tricky but not hard. (try yourself or if you think is not true, I'll write it later). And be careful I'm talking about Trees not general graphs. (There is no loop in tree and is connected).

This is a python implementation of what I believe you are interested in. Here, a tree is represented as a list of child trees.
def process(tree):
max_child_height=0
secmax_child_height=0
max_child_diameter=0
for child in tree:
child_height,child_diameter=process(child)
if child_height>max_child_height:
secmax_child_height=max_child_height
max_child_height=child_height
elif child_height>secmax_child_height:
secmax_child_height=child_height
if child_diameter>max_child_diameter:
max_child_diameter=child_diameter
height=max_child_height+1
if len(tree)>1:
diameter=max(max_child_diameter,max_child_height+secmax_child_height)
else:
diameter=max_child_diameter
return height,diameter
def diameter(tree):
height,diameter=process(tree)
return diameter

This is the recursive solution with Java.
import java.util.ArrayList;
import java.util.List;
public class DiameterOrNAryTree {
public int diameter(Node root) {
Result result = new Result();
getDepth(root, result);
return result.max;
}
private int getDepth(Node node, Result result) {
if (node == null) return 0;
int h1 = 0, h2 = 0;
for (Node c : node.children) {
int d = getDepth(c, result);
if (d > h1) {
h2 = h1;
h1 = d;
} else if (d > h2) h2 = d;
}
result.max = Math.max(result.max, h1 + h2);
return h1 + 1;
}
class Result {
int max;
Result() {
max = 0;
}
}
class Node {
public int val;
public List<Node> children;
public Node() {
children = new ArrayList<Node>();
}
public Node(int _val) {
val = _val;
children = new ArrayList<Node>();
}
public Node(int _val, ArrayList<Node> _children) {
val = _val;
children = _children;
}
}
}

Related

How to calculate a height of a tree

I am trying to learn DSA and got stuck on one problem.
How to calculate height of a tree. I mean normal tree, not any specific implementation of tree like BT or BST.
I have tried google but seems everyone is talking about Binary tree and nothing is available for normal tree.
Can anyone help me to redirect to some page or articles to calculate height of a tree.
Lets say a typical node in your tree is represented as Java class.
class Node{
Entry entry;
ArrayList<Node> children;
Node(Entry entry, ArrayList<Node> children){
this.entry = entry;
this.children = children;
}
ArrayList<Node> getChildren(){
return children;
}
}
Then a simple Height Function can be -
int getHeight(Node node){
if(node == null){
return 0;
}else if(node.getChildren() == null){
return 1;
} else{
int childrenMaxHeight = 0;
for(Node n : node.getChildren()){
childrenMaxHeight = Math.max(childrenMaxHeight, getHeight(n));
}
return 1 + childrenMaxHeight;
}
}
Then you just need to call this function passing the root of tree as argument. Since it traverse all the node exactly once, the run time is O(n).
1. If height of leaf node is considered as 0 / Or height is measured depending on number of edges in longest path from root to leaf :
int maxHeight(treeNode<int>* root){
if(root == NULL)
return -1; // -1 beacuse since a leaf node is 0 then NULL node should be -1
int h=0;
for(int i=0;i<root->childNodes.size();i++){
temp+=maxHeight(root->childNodes[i]);
if(temp>h){
h=temp;
}
}
return h+1;
}
2. If height of root node is considered 1:
int maxHeight(treeNode<int>* root){
if(root == NULL)
return 0;
int h=0;
for(int i=0;i<root->childNodes.size();i++){
temp+=maxHeight(root->childNodes[i]);
if(temp>h){
h=temp;
}
}
return h+1;
Above Code is based upon following class :
template <typename T>
class treeNode{
public:
T data;
vector<treeNode<T>*> childNodes; // vector for storing pointer to child treenode
creating Tree node
treeNode(T data){
this->data = data;
}
};
In case of 'normal tree' you can recursively calculate the height of tree in similar fashion to a binary tree but here you will have to consider all children at a node instead of just two.
To find a tree height a BFS iteration will work fine.
Edited form Wikipedia:
Breadth-First-Search(Graph, root):
create empty set S
create empty queues Q1, Q2
root.parent = NIL
height = -1
Q1.enqueue(root)
while Q1 is not empty:
height = height + 1
switch Q1 and Q2
while Q2 is not empty:
for each node n that is adjacent to current:
if n is not in S:
add n to S
n.parent = current
Q1.enqueue(n)
You can see that adding another queue allows me to know what level of the tree.
It iterates for each level, and for each mode in that level.
This is a discursion way to do it (opposite of recursive). So you don't have to worry about that too.
Run time is O(|V|+ |E|).

Fastest non-recursive implementation of LCA?

Here's the algorithm I came up with for non-recursively finding the lowest common ancestor of two nodes in a binary tree. Here's the basic strategy:
Use a dictionary/hashtable to store the tree. Each key-value pair represents a node and its parent.
Starting from each of the two nodes, walk up the tree by setting the variable representing each node's value to that of its parent, storing traversed values in a hashset (one for each of the two nodes).
The search is complete when any of the following conditions are reached: (a) the value of the two nodes is equal; or (b) when the two paths cross each other (i.e., the hashset of node 1's traversed values contains the current value for node 2, or vice versa); or (c) the node passed in doesn't exist in the tree (in which case the algorithm terminates and returns -1).
My understanding is that the worst-case time and space complexity of my algorithm is O(log(n)), since we never need to make more than 2 * height traversals or store more than 2 * height values in our hashsets (and since the lookup time for the hashsets and the tree dictionary are O(1)).
Following is my code (C#). Please advise if I am correct in my analysis, or if there is a more efficient (non-recursive) way to do this:
int LowestCommonAncestor(int value1, int value2, Dictionary<int, int> tree)
{
var value1Visited = new HashSet<int>();
var value2Visited = new HashSet<int>();
while (true)
{
if (value1 == value2) return value1;
if (value1Visited.Contains(value2)) return value2;
if (value2Visited.Contains(value1)) return value1;
int nextValue1;
int nextValue2;
if (tree.TryGetValue(value1, out nextValue1))
{
//Walk node 1 up the tree:
value1 = nextValue1;
value1Visited.Add(value1);
}
else
{
//Node doesn't exist in tree:
return -1;
}
if (tree.TryGetValue(value2, out nextValue2))
{
//Walk node 2 up the tree:
value2 = nextValue2;
value2Visited.Add(value2);
}
else
{
//Node doesn't exist in tree:
return -1;
}
}
}
Go up from each node to the root to measure its depth
Move up the path from the deeper node until you get to the same depth as the shallower one.
Move up the paths from both nodes (i.e., keeping the same depth on both paths) until they meet.
You don't need two hash sets.
Go up and collect in a single hash set the ancestors of one node
Go up from the second node and at each of its ancestors, check if the path collected at step 1 contains the current ancestor of the second. Stop at the first common one.
With D being the max depth of the tree, the complexity is O(D) worst-case complexity.
The worst case complexity in N - number of nodes - when the tree is degenerated in a list, one of the node being the head of this list and the other is the tail.
If the tree is balanced, D=log(N) - with log's base being the number of descendents of a node (binary - log2, ternary - log3, etc).
Here, then, is my revised algorithm:
int LCA(int value1, int value2, Dictionary<int, int> tree)
{
if (!tree.ContainsKey(value1) || !(tree.ContainsKey(value2))) return -1;
int depth1 = 0;
int depth2 = 0;
int tmpVal1 = value1;
int tmpVal2 = value2;
while (tmpVal1 != -1)
{
tmpVal1 = tree[tmpVal1];
depth1++;
}
while (tmpVal2 != -1)
{
tmpVal2 = tree[tmpVal2];
depth2++;
}
if (depth1 > depth2)
{
while (depth1 > depth2)
{
value1 = tree[value1];
depth1--;
}
}
else if (depth2 > depth1)
{
while (depth2 > depth1)
{
value2 = tree[value2];
depth2--;
}
}
while (value1 != value2)
{
value1 = tree[value1];
value2 = tree[value2];
}
return value1;
}

Print the Longest leaf-to-leaf path in a binary tree along with its length

I am solving a problem in which I have to find the longest leaf-to-leaf path in a binary tree along with its length.
for example, if the Binary tree is as follows:
a
/\
b c
/ / \
d e f
/ \ \
g h p
\
k
The longest leaf-to-leaf path would be k-h-d-b-a-c-f-p which is of length 8.
I am calculating the length by recursively finding the length of the left and right sub-tree and then return height_left + height_right + 1 . Is my concept correct?.
Also how should I print the longest leaf-to-leaf path? I just want an idea to proceed.
It seems to me that this algorithm is very close to finding a diameter of a binary tree. Diameter of the tree is the number of nodes on the longest path between two leaves in the tree.
I think you can look here for the implementation: http://www.geeksforgeeks.org/diameter-of-a-binary-tree/ and then adapt it or optimize it's time complexity if you want. But i think O(n) is good enough.
Most answers on the net gives how to find diameter of a tree, i.e
How to find the number of nodes in the longest path.
The only addition is we need to store the nodes which contribute to it.
In recursion, this can be done in two ways.
a) It should be a return type
b) It should be an input parameter which is an object. This object is populated with the result during the course of recursion.
Without the need to print the longest path, we only need to check at every node:
Max of
1) Left node max path
2) Right node max path
c) Current node max path (requires more inputs)
Now, to calculate current node max path, we need more inputs:
Current node max path needs:
1) Max left node height
2) Max right node height
This can either be stored in the node itself (as height parameter) or can be passed with the recursion.
This will only give diameter/length of the longest path.
Now, to get the path printed, we need to store more info which is:
- List<Nodes> pathList - This contains the nodes which form the longest path so far (Note this may not contain the current node).
- List<Nodes> heightList - This contains the nodes which form the longest height from the node to its leaf.
Finally the algo:
//Inputs and Outputs of the method
class Node{
int value;
Node leftchild;
Node rightchild;
}
class ReturnInfo{
ReturnInfo(){
maxpathlen = 0;
maxheight = 0;
pathList = new ArrayList<Node>();
heightList = new ArrayList<Node>();
}
int maxpathlen; //current max path
int maxheight; //current max height
List<Nodes> pathList;
List<Nodes> heightList;
}
//Signature
public ReturnInfo getMaxPath(Node n);
//Implementation
public ReturnInfo getMaxPath(Node n){
//Base case
if(n==null) return new ReturnInfo();
//This is a bottom up recursion. Info will flow from leaves to root.
//So first recurse and then do the work at this node level
//Recurse left & right
ReturnInfo leftReturnInfo = getMaxPath(n.leftchild);
ReturnInfo rightReturnInfo = getMaxPath(n.rightchild);
//Do work in this recursion or for this node
ReturnInfo retInfo = new ReturnInfo();
//Update all 4 parameters of returninfo and we are done
retInfo.maxheight = max(leftReturnInfo.maxheight, rightReturnInfo.maxheight) + 1;
//Update retInfo.heightList accordingly
retInfo.heightList = ....
retInfo.maxPathLen = max(leftReturnInfo.maxPathLen, rigthReturnInfo.maxPathLen, leftReturnInfo.maxHeight+rightReturnInfo.maxHeight+1);
//Remember from where maxPathLen came from and update accordingly
retInfo.pathList = .....
return retInfo;//We are done
}
You need a function that returns longest branch in a subtree and the longest path:
PS: I am leaving out details (Eg. Boundary conditions and so on). But this should give you an idea. This function returns two things 'branch' and 'path'. 'branch' is the longest path from this node to any of its leaves. 'path' is the longest path between any two leaves in this subtree.
def longestPath(node):
(leftBranch, leftPath) = longestPath(node.left);
(rightBranch, rightPath) = longestPath(node.right);
if len(rightBranch) > len(leftBranch):
curBranch = rightBranch+node.name
else:
curBranch = leftBranch+node.name
curPath = leftBranch + node.name + rev(rightBranch)
bestPath = curPath
if len(leftPath) > length(bestPath):
bestPath = leftPath
if len(rightPath) > length(bestPath):
bestPath = rightPath
return (curBranch, bestPath)
defintion:
node: (char content, node left , node right , node parent)
add(list , node): add node as last element in list
remove(list , index): remove and return element at index in list
length(string): length of string
insert(string , char , index): insert char at index in string
concat(string a , string OR char b): append b to a
input: node start
output: string
start
list nodes
node n
add(nodes , start)
do
n = remove(nodes , 0)
if n.parent != null
add(nodes , n.parent)
if n.left != null
add(nodes , n.left)
if n.right != null
add(nodes , n.right)
while !isEmpty(nodes)
//n now is the node with the greatest distance to start
string left = ""
string right = ""
node a = start
node b = n
while(a != b)
insert(left , a.content , length(left) - 1)
insert(right , b.content , 0)
a = a.parent
b = b.parent
string result = left
concat(result , a.content)
concat(result , right)
return result
Here is my Scala solution (Tree.scala):
/** Searches for the longest possible leaf-to-leaf path in this tree.
*
* Time - O(log^2 n)
* Space - O(log n)
*/
def diameter: List[A] = {
def build(t: Tree[A], p: List[A]): List[A] =
if (t.isEmpty) p
else if (t.left.height > t.right.height) build(t.left, t.value :: p)
else build(t.right, t.value :: p)
if (isEmpty) Nil
else {
val ld = left.diameter
val rd = right.diameter
val md = if (ld.length > rd.length) ld else rd
if (1 + left.height + right.height > md.length)
build(right, value :: build(left, Nil).reverse).reverse
else md
}
}
The idea is quite simple:
We recursively search for diameters in children (ld and rd and maximum 'md').
Check whether the longest possibe path that goes through current node is greather then diameters of its children or not (if (1 + ....)).
If its greater then we just need to build a new path with build function, which bilds a longest path from given node 't' to leaf. So, we just concatenates two resuts of this function (for left and right child) with current node.
If its not greater then the diameter is found it is md.
Longest leaf to leaf path means finding diameter of a tree. It can be done using height function.
There are many solutions available online.
Here is my Swift solution:
func diameterPath() -> [T] {
return diameterPathHelper(root).Path
}
typealias HeightAndDiameterAndPath = (Height: Int, Diameter: Int, Path: [T])
private func diameterPathHelper(node: TreeNode<T>?) -> HeightAndDiameterAndPath {
guard let node = node else {
return HeightAndDiameterAndPath(0, 0, [])
}
let left = diameterPathHelper(node.left)
let right = diameterPathHelper(node.right)
let height = max(left.Height, right.Height) + 1
if left.Height + right.Height + 1 > max(left.Diameter, right.Diameter) {
let currentDiameter = left.Height + right.Height + 1
let path = left.Path + [node.data] + right.Path
return HeightAndDiameterAndPath(height, currentDiameter, path)
} else {
if left.Diameter > right.Diameter {
return HeightAndDiameterAndPath(height, left.Diameter, left.Path)
} else {
return HeightAndDiameterAndPath(height, right.Diameter, right.Path)
}
}
}
We can use the maxdepth approach for this and initialize a variable max as 0.
public int diameterOfBinaryTree(TreeNode root) {
maxDepth(root);
return max;
}
private int maxDepth(TreeNode root) {
if (root == null) return 0;
int left = maxDepth(root.left);
int right = maxDepth(root.right);
max = Math.max(max, left + right);
return Math.max(left, right) + 1;
}
}
You have neglected one condition: What if the longest path doesn't pass through the root node?
static int findLongestPathLength(Node root){
if(root == null)
return 0;
int lh = getHeight(root.left);
int rh = getHeight(root.right);
return Math.max(1+lh+rh,
Math.max(findLongestPathLength(root.left),findLongestPathLength(root.right)));
}
static int getHeight(Node root){
if(root == null)
return 0;
return Math.max(getHeight(root.left)+1, getHeight(root.right)+1);
}
This will also make sure it find the longest path even if it doesn't pass through root.

KD TREES (3-D) Nearest Neighbour Search

I am looking at the Wikipedia page for KD trees Nearest Neighbor Search.
The pseudo code given in Wikipedia works when the points are in 2-D(x,y) .
I want to know,what changes should i make,when the points are 3-D(x,y,z).
I googled a lot and even went through similar questions link in stack overflow ,but i did n't find the 3-d implementation any where,all previous question takes 2-D points as input ,not the 3-D points that i am looking for.
The pseudo code in Wiki for building the KD Tree is::
function kdtree (list of points pointList, int depth)
{
// Select axis based on depth so that axis cycles through all valid values
var int axis := depth mod k;
// Sort point list and choose median as pivot element
select median by axis from pointList;
// Create node and construct subtrees
var tree_node node;
node.location := median;
node.leftChild := kdtree(points in pointList before median, depth+1);
node.rightChild := kdtree(points in pointList after median, depth+1);
return node;
}
How to find the Nearest neighbor now after building the KD Trees?
Thanks!
You find the nearest neighbour exactly as described on the Wikipedia page under the heading "Nearest neighbour search". The description there applies in any number of dimensions. That is:
Go down the tree recursively from the root as if you're about to insert the point you're looking for the nearest neighbour of.
When you reach a leaf, note it as best-so-far.
On the way up the tree again, for each node as you meet it:
If it's closer than the best-so-far, update the best-so-far.
If the distance from best-so-far to the target point is greater than the distance from the target point to the splitting hyperplane at this node,
process the other child of the node too (using the same recursion).
I've recently coded up a KDTree for nearest neighbor search in 3-D space and ran into the same problems understand the NNS, particularly 3.2 of the wiki. I ended up using this algorithm which seems to work in all my tests:
Here is the initial leaf search:
public Collection<T> nearestNeighbourSearch(int K, T value) {
if (value==null) return null;
//Map used for results
TreeSet<KdNode> results = new TreeSet<KdNode>(new EuclideanComparator(value));
//Find the closest leaf node
KdNode prev = null;
KdNode node = root;
while (node!=null) {
if (KdNode.compareTo(node.depth, node.k, node.id, value)<0) {
//Greater
prev = node;
node = node.greater;
} else {
//Lesser
prev = node;
node = node.lesser;
}
}
KdNode leaf = prev;
if (leaf!=null) {
//Used to not re-examine nodes
Set<KdNode> examined = new HashSet<KdNode>();
//Go up the tree, looking for better solutions
node = leaf;
while (node!=null) {
//Search node
searchNode(value,node,K,results,examined);
node = node.parent;
}
}
//Load up the collection of the results
Collection<T> collection = new ArrayList<T>(K);
for (KdNode kdNode : results) {
collection.add((T)kdNode.id);
}
return collection;
}
Here is the recursive search which starts at the closest leaf node:
private static final <T extends KdTree.XYZPoint> void searchNode(T value, KdNode node, int K, TreeSet<KdNode> results, Set<KdNode> examined) {
examined.add(node);
//Search node
KdNode lastNode = null;
Double lastDistance = Double.MAX_VALUE;
if (results.size()>0) {
lastNode = results.last();
lastDistance = lastNode.id.euclideanDistance(value);
}
Double nodeDistance = node.id.euclideanDistance(value);
if (nodeDistance.compareTo(lastDistance)<0) {
if (results.size()==K && lastNode!=null) results.remove(lastNode);
results.add(node);
} else if (nodeDistance.equals(lastDistance)) {
results.add(node);
} else if (results.size()<K) {
results.add(node);
}
lastNode = results.last();
lastDistance = lastNode.id.euclideanDistance(value);
int axis = node.depth % node.k;
KdNode lesser = node.lesser;
KdNode greater = node.greater;
//Search children branches, if axis aligned distance is less than current distance
if (lesser!=null && !examined.contains(lesser)) {
examined.add(lesser);
double nodePoint = Double.MIN_VALUE;
double valuePlusDistance = Double.MIN_VALUE;
if (axis==X_AXIS) {
nodePoint = node.id.x;
valuePlusDistance = value.x-lastDistance;
} else if (axis==Y_AXIS) {
nodePoint = node.id.y;
valuePlusDistance = value.y-lastDistance;
} else {
nodePoint = node.id.z;
valuePlusDistance = value.z-lastDistance;
}
boolean lineIntersectsCube = ((valuePlusDistance<=nodePoint)?true:false);
//Continue down lesser branch
if (lineIntersectsCube) searchNode(value,lesser,K,results,examined);
}
if (greater!=null && !examined.contains(greater)) {
examined.add(greater);
double nodePoint = Double.MIN_VALUE;
double valuePlusDistance = Double.MIN_VALUE;
if (axis==X_AXIS) {
nodePoint = node.id.x;
valuePlusDistance = value.x+lastDistance;
} else if (axis==Y_AXIS) {
nodePoint = node.id.y;
valuePlusDistance = value.y+lastDistance;
} else {
nodePoint = node.id.z;
valuePlusDistance = value.z+lastDistance;
}
boolean lineIntersectsCube = ((valuePlusDistance>=nodePoint)?true:false);
//Continue down greater branch
if (lineIntersectsCube) searchNode(value,greater,K,results,examined);
}
}
The full java source can be found here.
I want to know,what changes should i make,when the points are
3-D(x,y,z).
You get the current axis on this line
var int axis := depth mod k;
Now depending on the axis, you find the median by comparing the corresponding property. Eg. if axis = 0 you compare against the x property. One way to implement this is to pass a comparator function in the routine that does the search.

Lowest Common Ancestor implementations - what's the difference?

I've been reading about the Lowest Common Ancestor algorithm on top coder and I can't understand why the RMQ algorithm is involved - the solution listed there is insanely complicated and has the following properties:
O(sqrt(n)) time complexity for searches, O(n) precalculation time complexity
O(n) space complexity for storing parents of each node
O(n) space complexity again, for storing precalculations of each node
My solution: given 2 integer values, find the nodes through a simple preorder traversal. Take one of the nodes and go up the tree and store the path into a Set. Take the other node and go up the tree and check each node as I go up: if the node is in the Set, stop and return the LCA. Full implementation.
O(n) time complexity for finding each of the 2 nodes, given the values (because it's a regular tree, not a BST -
O(log n) space complexity for storing the path into the Set
O(log n) time complexity for going up the tree with the second node
So given these two choices, is the algorithm on Top Coder better and if yes, why? That's what I can't understand. I thought O(log n) is better than O(sqrt(n)).
public class LCA {
private class Node {
int data;
Node[] children = new Node[0];
Node parent;
public Node() {
}
public Node(int v) {
data = v;
}
#Override
public boolean equals(Object other) {
if (this.data == ((Node) other).data) {
return true;
}
return false;
}
}
private Node root;
public LCA() {
root = new Node(3);
root.children = new Node[4];
root.children[0] = new Node(15);
root.children[0].parent = root;
root.children[1] = new Node(40);
root.children[1].parent = root;
root.children[2] = new Node(100);
root.children[2].parent = root;
root.children[3] = new Node(10);
root.children[3].parent = root;
root.children[0].children = new Node[3];
root.children[0].children[0] = new Node(22);
root.children[0].children[0].parent = root.children[0];
root.children[0].children[1] = new Node(11);
root.children[0].children[1].parent = root.children[0];
root.children[0].children[2] = new Node(99);
root.children[0].children[2].parent = root.children[0];
root.children[2].children = new Node[2];
root.children[2].children[0] = new Node(120);
root.children[2].children[0].parent = root.children[2];
root.children[2].children[1] = new Node(33);
root.children[2].children[1].parent = root.children[2];
root.children[3].children = new Node[4];
root.children[3].children[0] = new Node(51);
root.children[3].children[0].parent = root.children[3];
root.children[3].children[1] = new Node(52);
root.children[3].children[1].parent = root.children[3];
root.children[3].children[2] = new Node(53);
root.children[3].children[2].parent = root.children[3];
root.children[3].children[3] = new Node(54);
root.children[3].children[3].parent = root.children[3];
root.children[3].children[0].children = new Node[2];
root.children[3].children[0].children[0] = new Node(25);
root.children[3].children[0].children[0].parent = root.children[3].children[0];
root.children[3].children[0].children[1] = new Node(26);
root.children[3].children[0].children[1].parent = root.children[3].children[0];
root.children[3].children[3].children = new Node[1];
root.children[3].children[3].children[0] = new Node(27);
root.children[3].children[3].children[0].parent = root.children[3].children[3];
}
private Node findNode(Node root, int value) {
if (root == null) {
return null;
}
if (root.data == value) {
return root;
}
for (int i = 0; i < root.children.length; i++) {
Node found = findNode(root.children[i], value);
if (found != null) {
return found;
}
}
return null;
}
public void LCA(int node1, int node2) {
Node n1 = findNode(root, node1);
Node n2 = findNode(root, node2);
Set<Node> ancestors = new HashSet<Node>();
while (n1 != null) {
ancestors.add(n1);
n1 = n1.parent;
}
while (n2 != null) {
if (ancestors.contains(n2)) {
System.out.println("Found common ancestor between " + node1 + " and " + node2 + ": node " + n2.data);
return;
}
n2 = n2.parent;
}
}
public static void main(String[] args) {
LCA tree = new LCA();
tree.LCA(33, 27);
}
}
The LCA algorithm works for any tree (not necessarily binary and not necessarily balanced). Your "simple algorithm" analysis breaks down since tracing a path to a root node is actually O(N) time and space instead of O(log N)
Just want to point out that the problem is about the rooted tree and not binary search tree. So, in you algorithm the
O(n) time complexity for finding each of the 2 nodes, given the values
O(n) space complexity for storing the path into the Set
O(sqrt(n)) time complexity for going up the tree with the second node and searching in the first n-stored elements.
Checking of each node as we go up from the second node with take O(n), so for n nodes it will take O(sqrt(n)).
The Harel and Tarjan LCA algorithm (reference in the link you gave) uses a pre-calculation with O(n) complexity, after which a lookup is O(1) (not O(sqrt(n) as you claim).

Resources