I have a binary tree and I'm trying to add elements
the problem is if I have and input like 5 5 6 6 9 0 0 2 -1 in the tree are added only 5 6 9 0 2 (I use -1 to stop adding elements)
I tried changing the condition if(nodeToAdd.data < node.data) in if(nodeToAdd.data <= node.data) but this didn't helped, I tried different conditions, but nothing, every time I got a stack overflow.
What I need to change so I could add two, three or n-times the same number? Any ideas?
Solved the problem, now everything work perfectly)
private Node traverseAndAddNode (Node node, int data){
if(node==null)return new Node(data);
if(data <= node.data){
node.leftChild=traverseAndAddNode(node.leftChild, data);
}else if(data > node.data){
node.rightChild=traverseAndAddNode(node.rightChild, data);
}
return node;
}
I just tried your code on my tree. Looks like it works.
private Node put(Node i, Key key, Value value) {
if (i == null) return new Node(key, value, 1);
// int compare = key.compareTo(i.key);
// if (compare < 0) i.left = put(i.left, key, value);
// else if(compare > 0) i.right = put(i.right, key, value);
// else i.value = value;
//
// //update N while unwinding stack
// i.N = size(i.left) + size(i.right) + 1;
int compare = key.compareTo(i.key);
if (compare < 0) {
if (i.left == null) i.left = new Node(key, value, 1);
else put(i.left, key, value);
}
if (compare >= 0) {
if (i.right == null) i.right = new Node(key, value, 1);
else put(i.right, key, value);
}
return i;
}
//main
BST<String, Integer> bst = new BST<>();
String[] s = {"S", "S", "E", "E", "X", "A", "R", "C", "H", "M"};
for (int i = 0; i < s.length; i++) {
bst.put(s[i], i);
}
for(String key: bst.keys()) System.out.print(key + " "); // levelordered
ouput: S E S A E X C R H M
Currently your method does nothing if nodeToAdd.data == node.data because both if conditions fail. You likely want:
if (nodeToAdd.data < node.data) {
...
else {
...
}
In this case equal nodes will be inserted to the right. If you want them inserted to the left then make the if condition <=.
None of that explains the stack overflow you mentioned but that is possibly an unrelated bug. I note that your code relies on the node being added having null children. I suggest that you explicitly assert this to ensure no loops are added to your tree in this method. They could well be added elsewhere.
assert nodeToAdd.leftChild == null;
assert nodeToAdd.rightChild == null;
Related
I am trying this problem on Practice-It, but have been having trouble with it for quite a while.
Write a method matches that returns a count of the number of nodes in one tree that match nodes in another tree. A match is defined as a pair of nodes that are in the same position in the two trees relative to their overall root and that store the same data.
So far, I've tried the following below, but I don't quite get the count I want, and I'm not quite sure why.
public int matches(IntTree t2)
{
return match(overallRoot, t2.overallRoot);
}
public int match(IntTreeNode tree1, IntTreeNode tree2)
{
if(tree1 == null && tree2 == null)
return 1;
if(tree1 == null || tree2 == null)
return 0;
if(tree1.data == tree2.data)
return 1;
int left = match(tree1.left, tree2.left);
int right = match(tree1.right, tree2.right);
return left + right;
}
Any help would really be appreciated!
You're stopping your search if the current node matches. If it's different, you check left and right, but on a match you return one.
You are very close to the solution, you have to consider:
if one of the nodes is null you can stop the visit for the subtrees and return 0
if the data for the two roots are different the count is 0 otherwise is 1 and after you can calculate the count for the two subtrees adding to the count for the two roots.
Below my suggestions as code:
public int match(IntTreeNode tree1, IntTreeNode tree2) {
if(tree1 == null || tree2 == null) { return 0; }
int count = tree1.data == tree2.data ? 1 : 0;
int left = match(tree1.left, tree2.left);
int right = match(tree1.right, tree2.right);
return count + left + right;
}
Full answer for the practice it one:
int matches(IntTree tree2) {
return matches(tree2.overallRoot, this.overallRoot);
}
int matches(IntTreeNode tree1, IntTreeNode node2)
{
int left=0, right=0, count =0;
if(tree1 == null && this != null || this == null && tree1 != null) { return 0; }
count = tree1.data == node2.data ? 1 : 0;
if(tree1.left != null && node2.left !=null){
left = matches(tree1.left, node2.left);}
if(tree1.right != null && node2.right !=null){
right = matches(tree1.right, node2.right);}
return count + left + right;
}
As far as my logic goes I'm using two different arrays to store all the leafs and then compare those arrays to see if the leaves are indeed the same, but my test cases are failing (for eg. [3,5,1,6,2,9,8,null,null,7,4]
[3,5,1,6,7,4,2,null,null,null,null,null,null,9,8]). Thanks in advance!
'''
class Solution {
static int array1[] = new int[50];
static int array2[] = new int[50];
static int q = 0;
static int r = 0;
public boolean compareLeaves(int arr1[], int arr2[])
{
for(int i = 0; i <array1.length ;i ++)
{
if(array1[i] != array2[i])
{
return false;
}
}
return true;
}
public boolean leafSimilar(TreeNode root1, TreeNode root2) {
if(root1 == null || root2 == null)
{
return false;
}
if(root1.left == null && root1.right == null)
{
array1[q] =root1.val ;
q++;
}
if(root2.left == null && root2.right == null)
{
array2[r] =root2.val ;
r++;
}
leafSimilar(root1.left,root2.left);
leafSimilar(root1.right,root2.right);
return compareLeaves(array1,array2);
}
}
'''
If the arrays have different lengths but agree on the first array1.length elements, I believe you deem them equal (return true). You probably need to use q and r for determining whether the element counts are the same and how many elements to compare.
If both roots are null, I would expect that the trees should be considered equal, but you return false.
Even if root1 == null, you should still pick up the leaves from root2 and vice versa.
I think you should do in-order traversal, that is, call leafSimilar(root1.left,root2.left) before you look at root1.val and root2.val. It may be that it doesn’t matter since the val is only considered for leaves, but I find it hard to be 100 % sure.
I may have missed something.
Using two different arrays to store all the leaves should be a sound strategy. I think it will be easier if you process each tree separately, not both trees at once.
your program will fail for test case:
tree1 : 1
/ \
null null
tree2: 2
/ \
1 null
clearly both trees have only one leaf node 1 but your code will fail because you are iterating on both of them identically.
you should iterate on them separately and store leaf node in the array's. And in the end check if both array have same elements no matter the order.
tree1 : 1
/ \
2 3
tree2: 1
/ \
3 2
Above two trees have same leaves, I have Updated functions to implement it correctly.
public int leafSimilar(TreeNode root, int arr[], int l) {
if(root == null)
{
return l;
}
if(root.left == null && root.right == null)
{
arr[l] =root.val ;
l+=1;
return l;
}
l = leafSimilar(root.left, l);
l = leafSimilar(root.right, l);
return l;
}
public boolean compareLeaves(int arr1[], int arr2[], int l, int r)
{
if( l != r ) return false;
for(int i = 0; i <l ;i ++)
{
boolean flag = true;
for(int j = 0; j <r ;j ++) {
if(arr1[i] == arr2[j])
{
flag = false;
break;
}
}
if( flag) return false;
}
return true;
}
int l = leafSimilar(root1, arr1, 0);
int r = leafSimilar(root2, arr2, 0);
compareLeaves(arr1, arr2, l, r);
Also above function will fail if tree can have duplicate node. Update the compare function to count frequency of all nodes in array one and then match it with frequency of nodes in array2. It will handle duplicate nodes.
The below line of code is suggesting both the tree to follow the same path thereby, ignoring the leaves in one of the tree1 or tree2.
if(root1 == null || root2 == null)
{
return false;
}
It's better to traverse both the tree one by one. And keep on storing the leaves.
public static boolean compare()
{
for(int i = 0; i <array1.length ;i ++)
{
if(array1[i] != array2[i])
{
return false;
}
}
return true;
}
public void isSimilar(Node root, int flag)
{
if(root==null)
return;
if(root.left == null && root.right == null)
{
if(flag==1)
{
array1[q] =root.val ;
q++;
}
else
{
array2[r] =root.val ;
r++;
}
}
isSimilar(root.left,flag);
isSimilar(root.right,flag);
}
You have to pass a flag variable to point which array to populate.
For ex, here 0 refer to tree1 and populate array1, and 1 refer to tree2 and populate array2 :
isSimilar(root1, 0);
isSimilar(root2, 1);
In fact this is a interview question asked a few days ago.
The interviewer wants me to express the difference between ArrayList and LinkedList, and asked to optimize the insertion operation on ArrayList, in other words, to re-implement add(int index, E element) and of course the complexity of get(int index) operation can be sacrificed.
My answer was to separate the array into k sub-arrays and update a counting array representing the number of elements already in the corresponding sub-array. And the memory of every sub-array is allocated dynamically with an expected initial size. When I need to insert a data into the ArrayList, I can locate a sub-array first, and do the operation within a small array.
And if insertions are not too frequent or the indexes are uniform distributed, the time complexity of inserting can be O(log(k) + n/k + k) in average, where log(k) means we should locate the sub-array first with binary searching on the counting array's sum array, n/k is for data movement or even memory re-allocation, and k stands for the updating of the sum array.
I'm sure there are better solutions. I do need some suggestions, thanks!
One of the solutions could be:
add(int index, E element) always add element to the end of array (you have to also store the index where this element should be added) - complexity O(1)
get(int index) has to restore correct order of array (if some elements were added after the last invocation) - knowing the positions in which each element should be, you can restore correct order in O(n)
You can implement it in a balanced binary tree, so that both add() and get() cost O(logn)
An example implementation will look like (hand-crafted here, will not compile, corner cases not covered):
class Node {
int subTreeSize;
Node left,right;
Element e;
// all i 0-indexed
Node get(int i) {
if (i >= subTreeSize) {
return null;
}
if (left != null) {
if(left.subTreeSize > i) {
return left.get(i);
} else {
i -= left.subTreeSize;
}
}
if (i == 0) {
return this;
}
return right.get(i-1);
}
// add e to the last of the subtree
void append(Element e) {
if(right == null){
right = new Node(e);
} else {
right.append(e);
right = right.balance();
}
subTreeSize += 1;
}
// add e to i-th position
void add(int i, Element e) {
if (left != null) {
if(left.subTreeSize > i) {
add(i,left);
left=left.balance();
} else {
i -= left.subTreeSize;
}
}
if (i == 0) {
if (left == null){
left = new Node(e);
} else {
left.append(e);
left = left.balance();
}
} else {
if (right == null) {
// also in this case i == 1
right = new Node(e);
} else {
right.add(i-1, e);
right = right.balance();
}
}
subTreeSize += 1;
}
// the common balance operation used in balance tree like AVL or RB
// usually just left or right rotation
Node balance() {
...
}
}
public class Tree {
Node root;
public Element get(int i) {
return root.get(i).e;
}
public void add(int i, Element e) {
if (root == null) {
root = new Node(e);
} else {
root.add(i,e);
root = root.balance();
}
}
}
A variant of an order statistic tree would allow you to add and get by index in O(log n).
The basic idea is as follows:
Have each node store the size of the subtree rooted at that node.
The index of a node will correspond to its position in the in-order traversal of the tree.
This means that the ordering of the nodes is determined based on where in the tree they appear - this is not the way a binary search tree typically works, where the nodes' elements have some ordering that's not dependent on where in the tree it appears (e.g. f is greater than a in a regular BST ordered lexicographically, but in our case f may be smaller or greater than a, since it's ordered based on the index of f and a).
To add or get, we start at the root and recursively go through the tree, determining whether our insert or lookup position is to the left or right based on the target index and the subtree sizes.
More specifically, we have the following recursive definitions:
(with some added complexity for null nodes and actually inserting the node)
node.add(index, element):
if index <= left.subtreeSize
left.add(index, element)
else
// anything to the right is after left subtree and current node, so those must be excluded
right.add(index - left.subtreeSize - 1, element)
node.get(index, element):
if index == left.subtreeSize
return node
if index < left.subtreeSize
return left.get(index)
else
return right.get(index - left.subtreeSize - 1)
To understand this better, the following example tree might be helpful:
Values: Indices (in-order pos): Subtree sizes:
a 5 8
/ \ / \ / \
b g 1 6 5 2
/ \ \ / \ \ / \ \
f c h 0 3 7 1 3 1
/ \ / \ / \
e d 2 4 1 1
If we want to insert a new node at position 5, for example, it will be inserted to the right of d.
Below is a small test program to demonstrate this (creating the tree shown above).
Note that balancing will still need to be done to achieve O(log n) running time per operation.
class Test
{
static class Node<T>
{
Node<T> left, right;
T data;
int subtreeCount;
Node(T data) { this.data = data; subtreeCount = 1; }
public String toString(int spaces, char leftRight)
{
return String.format("%" + spaces + "s%c: %s\n", "", leftRight, data.toString())
+ (left != null ? left.toString(spaces+3, 'L') : "")
+ (right != null ? right.toString(spaces+3, 'R') : "");
}
int subtreeSize(Node<T> node)
{
if (node == null)
return 0;
return node.subtreeCount;
}
// combined add and get into 1 function for simplicity
// if data is null, it is an get, otherwise it's an add
private T addGet(int index, T data)
{
if (data != null)
subtreeCount++;
if (index == subtreeSize(left) && data == null)
return this.data;
if (index <= subtreeSize(left))
{
if (left == null && data != null)
return (left = new Node<>(data)).data;
else
return left.addGet(index, data);
}
else if (right == null && data != null)
return (right = new Node<>(data)).data;
else
return right.addGet(index-subtreeSize(left)-1, data);
}
}
static class TreeArray<T>
{
private Node<T> root;
public int size() { return (root == null ? 0 : root.subtreeCount); }
void add(int index, T data)
{
if (index < 0 || index > size())
throw new IndexOutOfBoundsException("Index: " + index + ", Size: " + size());
if (root == null)
root = new Node<>(data);
else
root.addGet(index, data);
}
T get(int index)
{
if (index < 0 || index >= size())
throw new IndexOutOfBoundsException("Index: " + index + ", Size: " + size());
return root.addGet(index, null);
}
#Override
public String toString() { return root == null ? "Empty" : root.toString(1, 'X'); }
}
public static void main(String[] args)
{
TreeArray<String> tree = new TreeArray<>();
tree.add(0, "a");
tree.add(0, "b");
tree.add(1, "c");
tree.add(2, "d");
tree.add(1, "e");
tree.add(0, "f");
tree.add(6, "g");
tree.add(7, "h");
System.out.println("Tree view:");
System.out.print(tree);
System.out.println("Elements in order:");
for (int i = 0; i < tree.size(); i++)
System.out.println(i + ": " + tree.get(i));
}
}
This outputs:
Tree view:
X: a
L: b
L: f
R: c
L: e
R: d
R: g
R: h
Elements in order:
0: f
1: b
2: e
3: c
4: d
5: a
6: g
7: h
Live demo.
LinkedList is a linked-list with access\insert\remove requires O(n), linked-lists support sequential access O(n).
ArrayList is an array with insert\remove requires O(2n), but access requires O(1), arrays support random access O(1).
to find a more optimal hybrid structure, you can start with this:
template <T>
public class LinkedArrayList
{
LinkedList<ArrayList<T>> list;
public LinkedArrayList ()
{
list = new LinkedList<ArrayList<T>> ();
}
// ..
}
You'll have to balance segments (arrays) in the list between access complexity, and insert\remove complexity
I'm working on a problem for class I'm stuck on. It involves adding a methods to the Binary Search Tree found here: http://algs4.cs.princeton.edu/32bst/BST.java.html
I need to develop an iterative ceiling method that finds the ceiling for a given key. It cannot be recursive.
Here is my code so far. I understand the basics of the algorithm I am supposed to implement, but I'm finding actually doing so hard to wrap my head around.
Thanks in advance for any help you might be able to offer.
public Key ceiling_i(Key key)
{
Node t = root;
for(int i = 0; i < size(); i++){
int cmp = key.compareTo(t.key);
if(cmp == 0) return t.key;
else if(cmp < 0) t = t.left;
else if(cmp > 0) t = t.right;
}
return null;
}
Edit: The main problem I am having is how to deal with the iterations after the first one. According to my book, "If a given key is greater than the key at the root of a BST,
then the ceiling of key (the largest key in the BST greater
than or equal to key) must be in the right subtree. If key is
less than the key at the root, then the ceiling of key could
be in the left subtree; if not (or if key is equal to the key
at the root), then the key at the root is the ceiling of key." I am not sure how to deal with that in the loop.
Your code is a good start. But your for loop does not make sense to me.
public Key ceiling_i(Key key)
{
Node t = root;
Node t largestVisited = null;
while(t != null) {
int cmp = key.compareTo(t.key);
if(cmp == 0) return t.key;
else if(cmp < 0) { largestVisited = Min(t, largestVisited); t = t.left; }
else if(cmp > 0) { t = t.right; largestVisited = Min(t, largestVisited); }
}
return largestVisited;
}
Node Min(Node a, Node b) { return the node with the smaller key; }
Tip: You could have derived this code by first writing the recursive solution and noticing that it is tail recursive. Tail recursive functions can trivially made non-recursive by just reusing the already existing local variables. No need to open another stack-frame if you won't ever use the old one again.
The code from that book is NOT tail-recursive because the first ceiling() call has operations done on it before the returns.
private Node ceiling(Node x, Key key) {
if (x == null) return null;
int cmp = key.compareTo(x.key);
if (cmp == 0) return x;
if (cmp < 0) {
Node t = ceiling(x.left, key);
if (t != null) return t;
else return x;
}
return ceiling(x.right, key);
}
Change ceilng() so the recursive calls are an "accumulator" type of tail-call. Do this by passing additional parameters which hold the work done so far.
Null or a node is passed as x.left when cmp < 0. What has "accumulated" so far is a larger-than-item node x found at this conditional test.
In the original version after the the first recursive call, t is either null or some tree node. Node x is then used after if t is null. In a modified version an additional parameter will be passed the x node.
In the other conditional test of cmp > 0 with the tail call, there is no new "accumulated" work since x is smaller than the item and is not used in deciding a return value if x.right is null.
Notice what happens if we pass an "accumulated x" to "larger" in a modified ceiling(x, key, larger) function. In this modified function, the condition "if (x == null) return null" is replaced by "if (x == null) return larger" and all the t-value evaluations are removed after the first recursive call. With the second tail-call just pass null to larger.
So the conversion should look something like this:
private Node ceiling (Node x, Key key, Node larger) {
if (x == null) return larger;
int cmp = key.compareTo(x.key);
if (cmp == 0) return x;
if (cmp < 0) {
return ceiling(x.left, key, x);
}
return ceiling(x.right, key, null);
}
Now the function is tail-recursive and can be further converted into an iterative loop form.
private Node ceiling (Node x, Key key) {
Node larger = null;
while (x != null) {
int cmp = key.compareTo(x.key);
if (cmp == 0) return x;
if (cmp < 0) {
larger = x;
x = x.left;
} else {
x = x.right;
}
}
return larger;
}
Calculate the longest path between two nodes.
The path is in an arch.
Signature of method is:
public static int longestPath(Node n)
In the example binary tree below, it is 4 (going thru 2-3-13-5-2).
This is what I have right now and for the given tree it just returns 0.
public static int longestPath(Node n) {
if (n != null) {
longestPath(n, 0);
}
return 0;
}
private static int longestPath(Node n, int prevNodePath) {
if (n != null && n.getLeftSon() != null && n.getRightSon() != null) {
int currNodePath = countLeftNodes(n.getLeftSon()) + countRightNodes(n.getRightSon());
int leftLongestPath = countLeftNodes(n.getLeftSon().getLeftSon()) + countRightNodes(n.getLeftSon().getRightSon());
int rightLongestPath = countLeftNodes(n.getRightSon().getLeftSon()) + countRightNodes(n.getRightSon().getRightSon());
int longestPath = currNodePath > leftLongestPath ? currNodePath : leftLongestPath;
longestPath = longestPath > rightLongestPath ? longestPath : rightLongestPath;
longestPath(n.getLeftSon(), longestPath);
longestPath(n.getRightSon(), longestPath);
return longestPath > prevNodePath ? longestPath : prevNodePath;
}
return 0;
}
private static int countLeftNodes(Node n) {
if (n != null) {
return 1+ countLeftNodes(n.getLeftSon());
}
return 0;
}
private static int countRightNodes(Node n) {
if (n != null) {
return 1+ countRightNodes(n.getRightSon());
}
return 0;
}
I understand that I'm missing a key concept somewhere... My brain goes crazy when I try tracking the flow of execution...
Am I right by saying that by finding the longest path among the root, its left & right nodes and then recurse on its left & right nodes passing them the longest path from previous method invocation and finally (when?) return the longest path, I'm not certain as to how you go about returning it...
Maybe it is just as simple:
public static int longestPath(Node n) {
if (n != null) {
return longestPath(n, 0); // forgot return?
}
return 0;
}
Its more complicated than one might think at first sight. Consider the following tree:
1
/ \
2 3
/ \
4 5
/ \ \
6 7 8
/ \ \
9 a b
In this case, the root node is not even in the longest path (a-7-4-2-5-8-b).
So, what you must do is the following: For each node n you must compute the following:
compute longest path in left subtree starting with the root of the left subtree (called L)
compute longest path in right subtree starting with the root of the right subtree (called R)
compute the longest path in left subtree (not necessarily starting with the root of the left subtree) (called l)
compute the longest path in right subtree (not necessarily starting with the root of the right subtree) (called r)
Then, decide, which combination maximizes path length:
L+R+2, i.e. going from a subpath in left subtree to current node and from current node through a subpath in right subtree
l, i.e. just take the left subtree and exclude the current node (and thus right subtree) from path
r, i.e. just take the right subtree and exclude the current node (and thus left subtree) from path
So I would do a little hack and for every node not return just a single int, but a triple of integers containing (L+R+2, l, r). The caller then must decide what to do with this result according to the above rules.
A correct algorithm is:
Run DFS from any node to find the farthest leaf node. Label that node T.
Run another DFS to find the farthest node from T.
The path you found in step 2 is the longest path in the tree.
This algorithm will definitely work, and you're not limited to just binary trees either. I'm not sure about your algorithm:
Am I right by saying that by finding the longest path among the root, its left & right nodes and then recurse on its left & right nodes passing them the longest path from previous method invocation and finally (when???) return the longest path, I'm not certain as to how you go about returning it...
because I don't understand what exactly you're describing. Can you work it by hand on an example or try to explain it better? That way you might get better help understanding if it's correct or not.
You seem to be attempting a recursive implementation of basically the same thing just simplified for binary trees. Your code seems rather complicated for this problem however. Check the discussion here for a simpler implementation.
public int longestPath() {
int[] result = longestPath(root);
return result[0] > result[1] ? result[0] : result[1];
}
// int[] {self-contained, root-to-leaf}
private int[] longestPath(BinaryTreeNode n) {
if (n == null) {
return new int[] { 0, 0 };
}
int[] left = longestPath(n.left);
int[] right = longestPath(n.right);
return new int[] { Util.max(left[0], right[0], left[1] + right[1] + 1),
Util.max(left[1], right[1]) + 1 };
}
Simple Implementation:
int maxDepth(Node root) {
if(root == null) {
return 0;
} else {
int ldepth = maxDepth(root.left);
int rdepth = maxDepth(root.right);
return ldepth>rdepth ? ldepth+1 : rdepth+1;
}
}
int longestPath(Node root)
{
if (root == null)
return 0;
int ldepth = maxDepth(root.left);
int rdepth = maxDepth(root.right);
int lLongPath = longestPath(root.left);
int rLongPath = longestPath(root.right);
return max(ldepth + rdepth + 1, max(lLongPath, rLongPath));
}
Here is my recursive solution in C++:
int longest_dis(Node* root) {
int height1, height2;
if( root==NULL)
return 0;
if( root->left == NULL ) && ( root->right == NULL )
return 0;
height1 = height(root->left); // height(Node* node) returns the height of a tree rooted at node
height2 = height(root->right);
if( root->left != NULL ) && ( root->right == NULL )
return max(height1+1, longest_dis(root->left) );
if( root->left == NULL ) && ( root->right != NULL )
return max(height2+1, longest_dis(root->right) );
return max(height1+height2+2, longest_dis(root->left), longestdis(root->right) );
}
Taking into account #phimuemue example and #IVlad solution, I decided to check it out myself, so here is my implementation of #IVlad solution in python:
def longestPath(graph,start, path=[]):
nodes = {}
path=path+[start]
for node in graph[start]:
if node not in path:
deepestNode,maxdepth,maxpath = longestPath(graph,node,path)
nodes[node] = (deepestNode,maxdepth,maxpath)
maxdepth = -1
deepestNode = start
maxpath = []
for k,v in nodes.iteritems():
if v[1] > maxdepth:
deepestNode = v[0]
maxdepth = v[1]
maxpath = v[2]
return deepestNode,maxdepth +1,maxpath+[start]
if __name__ == '__main__':
graph = { '1' : ['2','3'],
'2' : ['1','4','5'],
'3' : ['1'],
'4' : ['2','6','7'],
'5' : ['2','8'],
'6' : ['4'],
'7' : ['4','9','a'],
'8' : ['5','b'],
'9' : ['7'],
'a' : ['7'],
'b' : ['8']
}
"""
1
/ \
2 3
/ \
4 5
/ \ \
6 7 8
/ \ \
9 a b
"""
deepestNode,maxdepth,maxpath = longestPath(graph,'1')
print longestPath(graph, deepestNode)
>>> ('9', 6, ['9', '7', '4', '2', '5', '8', 'b'])
I think You are overcomplicating things.
Think about the longest path that goes through the node n and doesn't go up to the parent of n. What is the relationship between the length of that path and the heights of both subtries connected to n?
After figuring that out, check the tree recursively reasoning like this:
The longest path for a subtree with the root n is the longest path of the following three:
The longest path in the subtree, whose root is n.left_child
The longest path in the subtree, whose root is n.right_child
The longest path, that goes through the node n and doesn't go up to the parent of n
What if, for each node n, your goal was to compute these two numbers:
f(n): The length of the longest path in the tree rooted at n
h(n): The height of the tree that is rooted at n.
For each terminal node (nodes having null left and right nodes), it is obvious that f and h are both 0.
Now, the h of each node n is:
0 if n.left and n.right are both null
1 + h(n.left) if only n.left is non-null
1 + h(n.right) if only n.right is non-null
1 + max(h(n.left), h(n.right)) if both n.left and n.right are non-null
And f(n) is:
0 if n.left and n.right are both null
max(f(n.left), h(n)) if only n.left is non-null
?? if only n.right is non-null
?? if both n.left and n.right are non-null
(You need to figure out what replaces the two "??" placeholders. There are choices that make this strategy work. I have tested it personally.)
Then, longestPath(Node n) is just f(n):
public class SO3124566
{
static class Node
{
Node left, right;
public Node()
{
this(null, null);
}
public Node(Node left, Node right)
{
this.left = left;
this.right = right;
}
}
static int h(Node n)
{
// ...
}
static int f(Node n)
{
// ...
}
public static int longestPath(Node n)
{
return f(n);
}
public static void main(String[] args)
{
{ // #phimuemue's example
Node n6 = new Node(),
n9 = new Node(),
a = new Node(),
n7 = new Node(n9, a),
n4 = new Node(n6, n7),
b = new Node(),
n8 = new Node(null, b),
n5 = new Node(null, n8),
n2 = new Node(n4, n5),
n3 = new Node(),
n1 = new Node(n2, n3);
assert(longestPath(n1) == 6);
}{ // #Daniel Trebbien's example: http://pastebin.org/360444
Node k = new Node(),
j = new Node(k, null),
g = new Node(),
h = new Node(),
f = new Node(g, h),
e = new Node(f, null),
d = new Node(e, null),
c = new Node(d, null),
i = new Node(),
b = new Node(c, i),
a = new Node(j, b);
assert(longestPath(a) == 8);
}
assert(false); // just to make sure that assertions are enabled.
// An `AssertionError` is expected on the previous line only.
}
}
You should be able to write recursive implementations of f and h to make this code work; however, this solution is horribly inefficient. Its purpose is just to understand the calculation.
To improve the efficiency, you could use memoization or convert this to a non-recursive calculation that uses stack(s).
Well, umm if I've understood your question correctly, here is my solution [but in C++(I'm sorry)]:
int h(const Node<T> *root)
{
if (!root)
return 0;
else
return max(1+h(root->left), 1+h(root->right));
}
void longestPath(const Node<T> *root, int &max)
{
if (!root)
return;
int current = h(root->left) + h(root->right) + 1;
if (current > max) {
max = current;
}
longestPath(root->left, max);
longestPath(root->right, max);
}
int longest()
{
int max = 0;
longestPath(root, max);
return max;
}