Input:
The first line contains the number of vertices \$n\$. The second line contains \$n\$ integer numbers from the range \$\left[−1, n − 1\right]\$ representing the \$0\$-based index of the parent for each vertex. An index of \$-1\$ designates that vertex as a root without a parent.
It is guaranteed that there is exactly one root.
It is guaranteed that the input represents a tree.
Output:
Output the height of the tree.
Code:
console.clear();
function heightSlow(parent) {
var maxHeight = 0;
// for each vertex check its height
// and keep track of maximum height so far.
for (var i = 0; i < parent.length; i++) {
var height = 0;
// O(n*n) // n is the number of vertex
for (var vertex = i; vertex != -1; vertex = parent[vertex]) {
height++;
}
// vertex should be -1 here
maxHeight = Math.max(maxHeight, height);
}
return maxHeight;
}
function heightFast(parent) {
var depth = [];
var N = parent.length;
function recur(parent, i, depth) {
if (depth[i] !== 0) return;
if (parent[i] === -1) {
depth[i] = 1;
return;
}
// Find the depth of parent
if (depth[parent[i]] === 0) recur(parent, parent[i], depth);
// We have depth of the parent now
depth[i] = depth[parent[i]] + 1;
}
var vertex;
// fill with default depth, although I could use null
// but stick with general notion of invalid value mathematically
// Theta(N)
for (vertex = 0; vertex < N; vertex++)
depth[vertex] = 0;
// find depth of each node
// recursion would be called for each node but
// it would return immidiately hence we can ignore that call?
// if yes then recur would be called N times exactly. Hence,
// Theta(N)
for (vertex = 0; vertex < N; vertex++)
recur(parent, vertex, depth);
// get the max depth, which is height
// Theta(N)
var height = 0;
for (vertex = 0; vertex < N; vertex++) {
height = Math.max(height, depth[vertex]);
}
return height;
}
// O(N^2)
console.log(heightSlow([4, -1, 4, 1, 1])); // 3
console.log(heightSlow([-1, 0, 4, 0, 3])); // 4
// Theta(N)
console.log(heightFast([4, -1, 4, 1, 1])); // 3
console.log(heightFast([-1, 0, 4, 0, 3])); // 4
Disclaimer:
I am more interested in fail proof approaches like writing correct loops, making quick tests without any testing frameworks to check the code correctness and finally reasoning about the run time complexity.
Having said that I would like to adhere to the problem specification in past I saw people arguing about the best practices which doesn't match with the problem statement and hence becomes immaterial.