I was trying to solve the problem 4.7 from the book cracking the code interview (very cool book!).
Design an algorithm and write code to find the first common ancestor
of two nodes in a binary tree. Avoid storing additional nodes in a
data structure. NOTE: This is not necessarily a binary search tree.
And I came up with this solution which is not even close to the ones provided in the book. I wonder if someone can find any flaws on it?
Solution:
I created a wraper class to hold the first common ancestor (if its found) and 2 booleans to track if a or b was found when recoursively searching the tree. Please read added comments in the code below.
public static void main (String args[]){
NodeTree a, b, head, result; //initialise and fill with data
fillTreeTestData(head);
pickRandomNode(a);
pickRandomNode(b);
result = commonAnsestor(a,b,head);
if(result != null)
System.out.println("First common ansestor "+result);
else
System.out.println("Not found");
}
class TreeNode{
Object value;
TreeNode right, left;
}
class WraperNodeTree{
boolean found_a;
boolean found_b;
NodeTree n;
WraperNodeTree (boolean a, boolean b, NodeTree n){
this.n = n;
this.a = a;
this.b = b;
}
}
static WraperNodeTree commonAnsestor(NodeTree a, NodeTree b, NodeTree current){
// Let's prepare a wraper object
WraperNodeTree wraper = new WraperNodeTree(false, false, null);
// we reached the end
if(current == null) return wraper;
// let's check if current node is either a or b
if(a != null)
wraper.found_a = current.value.equals(a.value);
else if(b != null)
wraper.found_b = current.value.equals(b.value);
else
return wraper; // if both are null we don't need to keep searching recoursively
// if either a or b was found let's stop searching for it for performance
NodeTree to_search_a = wraper.found_a ? null : a;
NodeTree to_search_b = wraper.found_b ? null : b;
// let's search the left
WraperNodeTree wraperLeft = common(to_search_a,to_search_b,current.left);
// if we already have a common ancester just pass it back recoursively
if(wraperLeft.n != null) return wraperLeft;
WraperNodeTree wraperRight = common(to_search_a,to_search_b,current.right);
if(wraperRight.n != null)return wraperRight;
// keep the wraper up to date with what we found so far
wraper.a = wraper.found_a || wraperLeft.found_a || wraperRight.found_a;
wraper.b = wraper.found_b || wraperLeft.found_b || wraperRight.found_b;
// if both a and b were found, let's pass the current node as solution
if(wraper.found_a && wraper.found_b)
wraper.n = current;
return wraper;
}
If it's about finding flaws:
Flaw #1:
I think there are too many typos in your code, which may confuse the interviewer on their first read of the code (and you don't want that!). For example, you write 'NodeTree' and 'TreeNode' interchangeably. Also, you define 'commonAncestor()' and then call 'common()'. Those things make the interviewer confused and make him drift apart from the important thing, which is understanding your way of solving the problem.
Flaw #2: Typos aside, I think another flaw is that this code is difficult to follow. I think one of the reasons is because you have 'return' statements all over the body of your function (at the beginning, at the middle and at the end). This should 'normally' be avoided in favor of readability.
Usually my approach is to organize the code in the following way:
Basic border case checks (which might include return)
Main body of the function (which should NOT return under any conditions)
Last checks and final RETURN
But when you have return statements in the middle, it makes it harder for the reader to imagine the flow.
Flaw #3: I think you're trying to solve two problems with the same function (commonAncestor). You are trying to both search for 'a' and 'b' and also keeping track of the common ancestor. I think if this is an interview question, you could separate those two objectives in favor of simplicity.
For example, consider this code (might not be perfect and need some extra border checks):
/**
* [Assumption]: If we call firstCommonAncestor(a, b, root)
* we TRUST that root contains both a and b.
*
* You can (and should) discuss this
* assumption with your interviewer.
*/
public static Node firstCommonAncestor(Node a, Node b, Node root) {
// If root matches any of the nodes (a or b),
// then root is the first common ancestor
// (because of our assumption)
if(root == a || root == b) return root;
// Search for a and b in both sides
SearchResult leftResult = searchNodes(a, b, root.left);
SearchResult rightResult = searchNodes(a, b, root.right);
// If a and b are on the same side (left or right), then we
// call firstCommonAncestor on that side and that’s it
if(leftResult.aFound && leftResult.bFound)
return firstCommonAncestor(a, b, root.left);
else if(rightResult.aFound && rightResult.bFound)
return firstCommonAncestor(a, b, root.right);
else {
// If a and b are in different sides,
// then we just found the first common ancestor
return root;
}
}
class SearchResult {
boolean aFound, bFound;
}
On the code above, I'm separating the task of actually searching for 'a' and 'b' in a different function called searchNodes, which is fairly easy to implement if your interviewer asks for it. But he might not even do that. And if he does, at that point he already understood your approach, so it's easier now to "make the code a bit more complicated" without confusing the interviewer.
I hope this helps.
Related
I wanted to know if the allOf method of CompletableFuture does polling or goes into a wait state till all the CompletableFutures passed into the method complete their execution.
I looked at the code of the allOf method in IntelliJ and it is doing some sort of binary search.
Please help me to find out what the allOf method of CompletableFuture actually does.
public static CompletableFuture<Void> allOf(CompletableFuture<?>... cfs) {
return andTree(cfs, 0, cfs.length - 1);
}
/** Recursively constructs a tree of completions. */
static CompletableFuture<Void> andTree(CompletableFuture<?>[] cfs, int lo, int hi) {
CompletableFuture<Void> d = new CompletableFuture<Void>();
if (lo > hi) // empty
d.result = NIL;
else {
CompletableFuture<?> a, b;
int mid = (lo + hi) >>> 1;
if ((a = (lo == mid ? cfs[lo] :
andTree(cfs, lo, mid))) == null ||
(b = (lo == hi ? a : (hi == mid+1) ? cfs[hi] :
andTree(cfs, mid+1, hi))) == null)
throw new NullPointerException();
if (!d.biRelay(a, b)) {
BiRelay<?,?> c = new BiRelay<>(d, a, b);
a.bipush(b, c);
c.tryFire(SYNC);
}
}
return d;
}
/** Pushes completion to this and b unless both done. */
final void bipush(CompletableFuture<?> b, BiCompletion<?,?,?> c) {
if (c != null) {
Object r;
while ((r = result) == null && !tryPushStack(c))
lazySetNext(c, null); // clear on failure
if (b != null && b != this && b.result == null) {
Completion q = (r != null) ? c : new CoCompletion(c);
while (b.result == null && !b.tryPushStack(q))
lazySetNext(q, null); // clear on failure
}
}
}
final CompletableFuture<V> tryFire(int mode) {
CompletableFuture<V> d;
CompletableFuture<T> a;
CompletableFuture<U> b;
if ((d = dep) == null ||
!d.orApply(a = src, b = snd, fn, mode > 0 ? null : this))
return null;
dep = null; src = null; snd = null; fn = null;
return d.postFire(a, b, mode);
}
It doesn't do a binary search -- it's building a balanced binary tree with the input futures at the leaves, and inner nodes that each complete when its two children have both completed.
For some reason that is not apparent from the code, the author of the code must have decided it was most efficient to consider allOf(_,_) between exactly two futures to be his primitive operation, and if he's asked for an allOf(...) between more than two futures, he's manufacturing it as a cascade of these binary primitives.
The tree should be balanced for such that no matter what the last future to complete is, there will only be a small number of levels left to collapse before the future at the top can complete. This improves performance in some situations, because it ensures that as much work as possible can be handled before we're completely done, at a point where (if we're lucky) the CPU might just be sitting idle, waiting for something asynchronous to complete.
Balancing the tree is done by having the topmost inner node have about as many leaves under its left child as under its right child -- so both children get about half of the original array, and then the code recursively builds a tree from each half of the array. Splitting in halves can look a bit like the index calculations for a binary search.
The basic structure is obscured slightly by special cases that appear to be designed to
use an optimized code path with fewer allocations when some of the original futures are already completed, and
make sure that the result of allOf(_) with exactly one element will return a fresh CompleteableFuture. For most purposes it would work to just return that single element, but the author must have wanted to ensure that users of the library can rely on the object being fresh, if they are using them as keys in hash maps, or other logic that depends on being able to tell the output from the inputs, and
have only one throw new NullPointerException(); by using ?: and inline assignments instead of honest if statements. This probably produces slightly smaller bytecode at the expense of readability. Cannot be recommended as a style to learn from, unless you personally pay for the storage cost of the resulting bytecode ...
In the book, Cracking the Coding Interview (6th Edition), a problem asks me to:
Implement an algorithm to find the the kth to last element of a singly linked list
And later, the author claims:
Unfortunately, we can't pass back a node and a counter using normal return statements.
She is referring to the Java language in the above statement. She later on shows that we can only print the kth to last element in singly linked list in Java.
My question is this: Why can't we pass back a node when the recursion is popping frames? Why can't we add another argument to the recursive function such that that argument is set at the right time (i.e. when index = kth item) to the node we are looking for? I have been thinking about this since last night, and I just can't wrap my head around it.
The example answer provided looks like this:
int printKthToLast (LinkedListNode head, int k) {
if(head == null) {
return 0;
}
int index = printKthToLast(head.next, k) + 1;
if (index == k) {
System.out.println(k + "th to last node is " + head.data);
}
return index;
}
You are right when you say that we can "add another argument to the recursive function such that the argument is set at the right time". For example, this function works in the same way as the solution you posted:
private void printKthToLast(LinkedListNode head, int k, int index) {
if(head == null) {
return;
}
if(index >= k) {
System.out.println(head.data);
}
printKthToLast(head.next, k, ++index);
}
My guess is that the author was trying to illustrate that recursion typically "divides a problem into sub-problems, solves these sub-problems, and combines the results" (that is, the classic divide-and-conquer method). In this case, the division of problems into sub-problems is carried out when we call the recursive function on the "next" element, because we are getting closer to the base case (head == null). The results to combine is the index, which must be recursively incremented to determine when a node must be printed. This is why the index establishes the return type of the function (int). This is the correct way to solve the problem recursively. What you suggest is a possibility, but passing "index" as a parameter is neither division of problems into sub-problems nor combining the results.
In addition, it is also important to consider that, in Java, methods are not only identified by their names, but also by their input parameters, return types, etc. So, these two methods ...
private int printKthToLast(LinkedListNode head, int k)
private void printKthToLast(LinkedListNode head, int k, int index)
... are entirely distinct, even though they have the same name (this is called polymorphism). Therefore, including these two methods in the same solution would not be recursion.
LinkedListNode is used internally by the LinkedList.
There are no public methods on a LinkedList that allow to retrieve the internal LinkedListNode (actually called Entry).
Therefore, given a LinkedList, you cannot get access to the internal node.
Yet, if it is just to go through the list, you can simply use an iterator and recurse over it:
int printKthToLast (Iterator<E> it, int i) {
if(!it.hasNext()) {
return 0;
}
E e = it.next();
// ...
}
Here is the code for the implementation of the Binary Search Tree:
public class BST<T extends Comparable<T>> {
BSTNode<T> root;
public T search(T target)
{
//loop to go through nodes and determine which routes to make
BSTNode<T> tmp = root;
while(tmp != null)
{
//c can have 3 values
//0 = target found
//(negative) = go left, target is smaller
//(positive) = go left, target is greater than current position
int c = target.compareTo(tmp.data);
if(c==0)
{
return tmp.data;
}
else if(c<0)
{
tmp = tmp.left;
}
else
{
tmp = tmp.right;
}
}
return null;
}
/*
* Need a helper method
*/
public T recSearch(T target)
{
return recSearch(target, root);
}
//helper method for recSearch()
private T recSearch(T target, BSTNode<T> root)
{
//Base case
if(root == null)
return null;
int c = target.compareTo(root.data);
if(c == 0)
return root.data;
else if(c<0)
return recSearch(target, root.left);
else
return recSearch(target, root.right);
}
Why do I need the recursive helper method? Why can't I I just use "this.root" to carry out the recursive process that is taking place? Furthermore, if screwing up the root property of the object this method is being called on is a problem, then how is does the helper method prevent this from happening? Does it just create a pointer that is separate from the this.root property, and therefore won't mess up the root property of the object that the method is being called on?
Sorry if the question doesn't seem straight forward, but if anyone can enlighten me on what's exactly going on behind the scenes I would really appreciate it.
The method needs a starting point. It needs to have a non changing Target node and it needs to compare it with some other node to see if they are a match lets call this node current instead of root since it is the current Node the recursive method is evaluating. There really isn't a concise way of doing this when using a recursive method other than using a helper function and passing in both variables (this is the case for many recursive methods). As you said stated if you updated root you would completely alter your tree when going left or right which you wouldn't want to do. The helper function is used because it gives your recursive method a starting point. And it also keeps track of the current node you are working on as you said the method points to the Node object being evaluated but doesn't make any changes. When going left or right it doesn't modify anything it just passes in a reference to the left or right node and continues to do this until the target is found or the base case is hit.
I'm working on a project for a software engineering class I'm taking. The goal is to design a program that will use genetic programming to generate a mathematical expression that fits provided training data.
I've just started working on the project, and I'm trying to wrap my head around how to create a binary tree that will allow for user-defined tree height and keep each node separate to make crossover and mutation simpler when I get to implementing those processes.
Here are the node classes I've created so far. Please pardon what I am sure is my evident inexperience.
public class Node
{
Node parent;
Node leftchild;
Node rightchild;
public void setParent(Node p)
{
parent = p;
}
public void setLeftChild(Node lc)
{
lc.setParent(this);
leftchild = lc;
}
public void setRightChild(Node rc)
{
rc.setParent(this);
rightchild = rc;
}
}
public class OperatorNode extends Node
{
char operator;
public OperatorNode()
{
double probability = Math.random();
if (probability <= .25)
{
operator = '+';
}
else if (probability > .25 && probability <= .50)
{
operator = '-';
}
else if (probability > .50 && probability <= .75)
{
operator = '*';
}
else
{
operator = '/';
}
}
public void setOperator(char op)
{
if (op == '+' || op == '-' || op == '*' || op == '/')
{
operator = op;
}
}
/**
* Node that holds x variables.
*/
public class XNode extends Node
{
char x;
public XNode()
{
x = 'x';
}
}
import java.util.Random;
public class OperandNode extends Node
{
int operand;
/**
* Initializes random number generator, sets the value of the node from zero to 9.
*/
public OperandNode()
{
Random rand = new Random();
operand = rand.nextInt(10);
}
/**
* Manually changes operand.
*/
public void setOperand(int o)
{
operand = o;
}
}
This accomplishes everything I need out of the nodes themselves, but I'm running into problems trying to figure out how to turn these into a larger tree. I realize I need to use a collection type of some sort, but can't seem to find one in the Library that seems appropriate for what I'm trying to do.
Even a nudge in the right direction would be greatly appreciated.
So you want to build a random tree of OperatorNodes, OperandNodes, and XNodes? And you said you want to make the tree depth user defined?
Define a recursive function called buildRandomTree or something similar. It should take a single int parameter for tree depth. If the depth parameter is 1, return a random leaf node (OperandNode or XNode). If the depth parameter is more than 1, generate a random OperatorNode, and make recursive calls to generate the left and right subtrees (with depth 1 less than the current level).
Depending on what you want to do with the nodes, you will have to define other recursive functions. For example, you will probably want to generate textual representations of your expression trees. For that, you can define toString() on each of the node classes. (OperatorNode.toString() will have to call toString() on the left and right subtrees.)
You will probably also want to evaluate the expression trees (with given values for the variables). For that, you can define another recursive function, perhaps called evaluate(). It will have to take one parameter, probably a Map, which will give the variable values (or "bindings") which you want to evaluate the expression with. (Right now your expression trees can only contain a single variable "x", but I imagine you may want to add more. If you are sure you will only ever use a single variable, then evaluate can take a single numeric argument for the value of "x".)
The implementation of evaluate for your 3 node classes will all be very simple. OperandNode and VariableNode will just return a value directly; OperatorNode will have to call evaluate on the left and right subtrees, combine the values using the appropriate operation, then return the result.
Maybe looking at this will help you.
What is wrong with this method ? it seems but I am not sure that the comparison of adjacent children in the tree does not take place.
I roughly traced the workings of this algorithm by hand and I think the idea is correct maybe something wrong with the implementation or I have no Idea how recursion works, the second helper (compare) method seems to be the issue
public static int MAX(BST B) {
int m = ((Integer) B.root.data).intValue();
return call(B.root, m);
}
public static int call(node current, int max) {
//first helper method gets the max from two different levels in the tree
if(current == null)
return -1;
if(current.left == null && current.right == null)
return max;
else {
if(((Integer) current.data).intValue()>max)
max = ((Integer) current.data).intValue();
return compare(call(current.left,max),call(current.right,max));
}
}
//second helper method gets the max
static int compare(int m1, int m2) {
if(m1>m2)
return m1;
else
return m2;
}
Since you are searching the entire tree, I'm going to assume that the structure is not properly ordered.
The bug is in your call function with:
if(current.left==null&¤t.right==null) return max;
Imagine you have a tree with a root with two leaf nodes (three nodes total). The root has value 3, right has value 2, and left has value 5. The algorithm should return 5, but your code will return 3. This is because you ignore the value of any leaf (a node with no "children") with that line of code. So your code ignores the value 5, in this example, and returns max, which is 3.
You can fix this by returning compare(current.value, max) when left and right are null.
I think (not 100%) that you may have an issue because you only check if BOTH children are null if for example right is null and left is not you will attempt to call the method call on both right and. Perhaps add a case checking if one child is null and if so return call of the non null child.
... I have no Idea how recursion works ...
Recursion means the method you are in gets called from inside itself with some other arguments , and there is some check that exits by returning a value or continues to call itself recursively.
call() is indirectly recursive, as it either exits with a return of -1 or max or it calls itself again with new arguments and continues to do this until it either exits or crashes with an OutOfMemory error as the stack fills up.
This method isn't recursive: It is poorly named though.
static int compare(int m1, int m2) {
if(m1>m2)
return m1;
else
return m2;
}
and could be written ( and renamed ) as
static int min(final int m1, final int m2)
{
return Math.min(m1,m2);
}
or just inlined into
return Math.min(call(current.left,max),call(current.right,max));
either way, you are getting the minimum of the two values, not really comparing them that implies different logic and a different return value.
Either way that method isn't recursive and if the logic of m1 > m2 is appropriate it can't be the problem, more like the input to that function is not what you expect.
Step debugging is a powerful tool and one all experienced developers use every day!