Cracking the Coding Interview - Example 9 (Runtime Example) - runtime

This question is related to the depth of binary search tree, but my question is regarding of understanding the example code that is shown below:
int sum(Node node) {
if (node == null){
return 0;
}
return sum (node.left) + node.value + sum(node.right);
}
The author said the depth of this is roughly log N, I am not sure how to get log N.
Also, my understanding of this code, it seems to be a infinite one. I know it is not, but I can not convince myself. For example, if this is 1~3 array, the node start # 2, so:
LVL 1:sum(1)+2+sum(3)
LVL 2:sum(sum(0)+1+sum(2))+2+sum(sum(2)+3+sum(0))
LVL 3: ... (The sum(2) will repeat the LVL 1, and it will never end)
Anyone can help me point out the issue?
Thank you!

Decided to turn my comment into an answer:
Binary trees are log n as you keep cutting it in half each time, but this is for a balanced tree. A very unbalanced one would be O(N) if everything was on the right, for example.
So, why is it not infinite?
Since this is recursive it needs a way to stop calling itself, and that is when node is null, then it bails out.
So, in every tree you will eventually reach child nodes that aren't set, and these will stop the recursion, which prevents it from being infinite.
If you remove that if statement then you will get an exception, but in a recursive algorithm that is actually doing something that won't end you would get a stack overflow.

Related

How does minimax algorithm work on tictactoe?

I've read a lot of documents regarding minimax algorithm and it's implementation on the game of tic-tac-toe but I'm really having a hard time applying it.
Here are links that I've read 1, 2, 3, 4, 5 and an example using java 6
Considering the pseudocode: 7
function minimax(node, depth, maximizingPlayer)
if depth = 0 or node is a terminal node
return the heuristic value of node
if (maximizingPlayer is TRUE) {
bestValue = +∞
for each child of the node {
val = minimax(child, depth-1, FALSE)
bestValue = max(bestValue, val)
}
return bestValue
} else if maximizingPlayer is FALSE {
bestValue = -∞
for each child of the node {
val = minimax(child, depth-1, TRUE)
bestValue = min(bestValue, val)
}
return bestValue
}
Here are my questions:
1. What will I pass to the signature node, is it the valid moves for the current player?
2. What is + and - infinity, What are their possible values?
3. What is the relationship between minimax and the occupied cells on the board.
4. What are child nodes how are values extracted from it?
5. How will I determine the maximum allowed depth?
Node is the grid with the information of which cells were already played
±∞ is just a placeholder, you can take int.maxValue or any other "big"/"small" value in your programming language. It is used later as a comparison to the calculated value of a node and 0 might already be a valid value. (Your pseudocode is wrong there, if we assign +∞ to bestValue, we want min(bestValue, val) and max(bestValue, val) for -∞.
Not sure what you mean.
Child nodes are possible moves that can be made. Once no move can be made the board is evaluated and score is returned.
The search space in TicTacToe is very small, in other games a heuristic score is returned, depending if the situation is is favorable to the player or not. In your scenario there should be no hard depth.
First of all, stop asking 100 questions in one question. Ask one, try to understand and figure out whether you actually need to ask another.
Now to your questions:
What will I pass to the signature node, is it the valid moves for the
current player?
No, you will pass only node, depth, maximizingPlayer (who plays - min or max). Here for each child of the node you find the possible moves from that node. In real scenario most probably you will have a generator like getChildren using which you will get your children
What is + and - infinity, What are their possible values?
minimax algorithm just tries to find the maximum value over all minimum values recursively. What is the worst possible result a maximum player can get - -infinity. The worse for minimum is when maximum will get +infinity. So these are just starting values.
What is the relationship between minimax and the occupied cells on the
board.
Not sure what do you mean here. Minimax is the algorithm, occupied cell is an occupied cell. This question is similar to what is the relation between dynamic programming and csv file.
What are child nodes how are values extracted from it?
they are all possible positions that can be reached from your current position. For example
How will I determine the maximum allowed depth?
In standard minimax it is till you will reach the end of the tree. At the end of the tree your evaluation function will give you a reward. Because the tree is most of the time too huge to fit anywhere people use a cutoff and use approximation to evaluation function (heuristics). The easiest cut of is look n moves ahead and hope that your evaluation function is good enough and your opponent think n-1 moves ahead.

Where did I go wrong in simplifying this code fragment of reversing a singly linked list?

I found a solution on the CareerCup for reversing a singly linked list:
void reverse(node n, node prev)
{
if (n == null) { head = prev; return; }
reverse(n.next, n);
n.next = prev;
}
call reverse (head, null);
Which is good to understand and study.If the original list is "1->2->3->4->5", the reversed output will be "5->4->3->2->1".
I am trying to simplifying this solution a bit by using only one parameter, which is the current Node where are working on:
void reverse2(Node n)
{
if(n.next==null){ head = n;return; }
reverse2(n.next);
n=n.next.next;
}
call reverse2(head)
Maybe you may already tell, the result is not correct, after the method finishes, the modified remains "1->2->3->4->5".
I was confused at first, after thought for a while, I think my fault is as this line:
n=n.next.next;
in which I was planning to make the next node point back to current node, however, in my current recursion, I am working on the node "n" it self, I wasn't changing anything of this current node n, thus the resulting list is not changed at all.
I am not 100% convinced by myself, could experts offer some guidance? Thanks.
The problem with you algorithm is that it changes nothing in the original data structure.
The original algorithm did this:
n.next = prev;
which actually reversed one node. You only call:
n.next = prev;
Which simply changes a parameter of the method, but does nothing else.
I doubt you could solve this problem with a recursion method that takes only one parameter, because after modifying the current node N(i) you need to move on to the next node N(i+1), and that next node should be made to point to N(i). So the next call should know both about N(i) and N(i+1).
First thought,
n=n.next.next; why are you changing the entire node as such??
To reverse the linked list isn't it only required to change the next attribute?
It should have been something like
n.next.next = n
consider when n points to 4 in 1->2->3->4->5
now we have 4.next = 5 and 5.next = null initially
on excecution it would be like
4.next.next => 5.next = 4
1->2->3->4->5---
^------|

Algorithm interview from Google

I am a long time lurker, and just had an interview with Google where they asked me this question:
Various artists want to perform at the Royal Albert Hall and you are responsible for scheduling
their concerts. Requests for performing at the Hall are accommodated on a first come first served
policy. Only one performance is possible per day and, moreover, there cannot be any concerts
taking place within 5 days of each other
Given a requested time d which is impossible (i.e. within 5 days of an already sched-
uled performance), give an O(log n)-time algorithm to find the next available day d2
(d2 > d).
I had no clue how to solve it, and now that the interview is over, I am dying to figure out how to solve it. Knowing how smart most of you folks are, I was wondering if you can give me a hand here. This is NOT for homework, or anything of that sort. I just want to learn how to solve it for future interviews. I tried asking follow up questions but he said that is all I can tell you.
You need a normal binary search tree of intervals of available dates. Just search for the interval containing d. If it does not exist, take the interval next (in-order) to the point where the search stopped.
Note: contiguous intervals must be fused together in a single node. For example: the available-dates intervals {2 - 15} and {16 - 23} should become {2 - 23}. This might happen if a concert reservation was cancelled.
Alternatively, a tree of non-available dates can be used instead, provided that contiguous non-available intervals are fused together.
Store the scheduled concerts in a binary search tree and find a feasible solution by doing a binary search.
Something like this:
FindDateAfter(tree, x):
n = tree.root
if n.date < x
n = FindDateAfter(n.right, x)
else if n.date > x and n.left.date < x
return n
return FindDateAfter(n.left, x)
FindGoodDay(tree, x):
n = FindDateAfter(tree, x)
while (n.date + 10 < n.right.date)
n = FindDateAfter(n, n.date + 5)
return n.date + 5
I've used a binary search tree (BST) that holds the ranges for valid free days that can be scheduled for performances.
One of the ranges must end with int.MaxValue, because we have an infinite amount of days so it can't be bound.
The following code searches for the closest day to the requested day, and returns it.
The time complexity is O(H) when H is the tree height (usually H=log(N), but can become H=N in some cases.).
The space complexity is the same as the time complexity.
public static int FindConcertTime(TreeNode<Tuple<int, int>> node, int reqDay)
{
// Not found!
if (node == null)
{
return -1;
}
Tuple<int, int> currRange = node.Value;
// Found range.
if (currRange.Item1 <= reqDay &&
currRange.Item2 >= reqDay)
{
// Return requested day.
return reqDay;
}
// Go left.
else if (currRange.Item1 > reqDay)
{
int suggestedDay = FindConcertTime(node.Left, reqDay);
// Didn't find appropriate range in left nodes, or found day
// is further than current option.
if (suggestedDay == -1 || suggestedDay > currRange.Item1)
{
// Return current option.
return currRange.Item1;
}
else
{
// Return suggested day.
return suggestedDay;
}
}
// Go right.
// Will always find because the right-most node has "int.MaxValue" as Item2.
else //if (currRange.Item2 < reqDay)
{
return FindConcertTime(node.Right, reqDay);
}
}
Store the number of used nights per year, quarter, and month. To find a free night, find the first year that is not fully booked, then the quarter within that year, then the month. Then check each of the nights in that month.
Irregularities in the calendar system makes this a little tricky so instead of using years and months you can apply the idea for units of 4 nights as "month", 16 nights as "quarter", and so on.
Assume, at level 1 all schedule details are available.
Group schedule of 16 days schedule at level 2.
Group 16 level 2 status at level 3.
Group 16 level 3 status at level 4.
Depends on number of days that you want to expand, increase the level.
Now search from higher level and do binary search at the end.
Asymtotic complexity:-
It means runtime is changing as the input grows.
suppose we have an input string “abcd”. Here we traverse through each character to find its length thus the time taken is proportional to the no of characters in the string like n no of char. Thus O(n).
but if we put the length of the string “abcd” in a variable then no matter how long the string be we still can find the length of thestring by looking at the variable len. (len=4).
ex: return 23. no matter what you input is we still have the output as 23.
thus the complexity is O(1). Thus th program will be running in a constant time wrt input size.
for O(log n) - the operations are happening in logarithmic steps.
https://drive.google.com/file/d/0B7eUOnXKVyeERzdPUE8wYWFQZlk/view?usp=sharing
Observe the image in the above link. Over here we can see the bended line(logarithmic line). Here we can say that for smaller inputs the O(log n) notation works good as the time taken is less as we can see in the bended line but when the input grows the linear notation i.e O(n) is considered as better way.
There are also the best and worst case scenarios to be seen. Like the above example.
You can also refer to this cheat for the algorithms: http://bigocheatsheet.com/
It was already mentioned above, but basically keep it simple with a binary tree. You know a binary tree has log N complexity. So you already know what algorithm you need to use.
All you have to do is to come up with a tree node structure and use binary tree insertion algorithm to find next available date:
A possible one:
The tree node has two attributes: d (date of the concert) and d+5 (end date for the blocking period of 5 days). Again to keep it simple, use a timestamp for the two date attributes.
Now it is trivial to find next available date by using binary tree inorder insertion algorithm with initial condition of root = null.
Why not try to use Union-Find? You can group each concert day + the next 5 days as part of one set and then perform a FIND on the given day which would return the next set ID which would be your next concert date.
If implemented using a tree, this gives a O(log n) time complexity.

How to print a binary tree level by level? Interview question!

How to print a binary tree level by level?
This is an interview question I got today. Sure enough, using a BFS style would definitely work. However, the follow up question is: How to print the tree using constant memory? (So no queue can be used)
I thought of converting the binary tree into a linked list somehow but have not come up with a concrete solution.
Any suggestions?
Thanks
One way to avoid using extra memory (much extra, anyway) is to manipulate the tree as you traverse it -- as you traverse downward through nodes, you make a copy of its pointer to one of the children, then reverse that to point back to the parent. When you've gotten to the bottom, you follow the links back up to the parents, and as you go you reverse them to point back to the children.
Of course, that's not the whole job, but it's probably the single "trickiest" part.
Extending on what Jerry Coffin said, I had asked a question earlier about doing something similar using in-order traversal. It uses the same logic as explained by him.
Check it out here:
Explain Morris inorder tree traversal without using stacks or recursion
You can do it by repeatedly doing an in-order traversal of the tree while printing only those nodes at the specified level. However, this isn't strictly constant memory since recursion uses the call stack. And it's super inefficient. Like O(n * 2^n) or something.
printLevel = 1;
while(moreLevels) {
moreLevels = printLevel(root, 1, printLevel);
printLevel++;
}
boolean printLevel(Node node, int currentLevel, int printLevel) {
boolean moreLevels = false;
if(node == null) {
return(false);
}
else if(currentLevel == printLevel) {
print the node value;
}
else {
moreLevels |= printLevel(node.leftChild, currentLevel + 1, printLevel);
moreLevels |= printLevel(node.rightChild, currentLevel + 1, printLevel);
}
return(moreLevels);
}

Homework: binary tree - level-order trasversal

is there a way to visit a binary tree from the lowest level to the higher (root) ?
not from the root-level to the lowest!!!
(and not using the level-order traversal and a stack...!!!) <--- its opposite..
so difficult...thank you!
There's a few challenges here that lead to different solutions:
Can you traverse up the tree? Often data structures are set up so you can only go down. You could find all leaf nodes, put them in a priority queue by level, and then traverse up.
Can you store O(n) additional data? You could traverse it in a normal breadth-first manner, inserting pointers into a priority queue by level, as with the previous solution, but this time inserting all nodes during the initial traversal. This will increase the maximum size of the auxiliary data used during traversal though.
Is the tree guaranteed to be balanced and full, like it might be in a Heap-like tree? If it is, you can traverse it in a simpler manner, by just going to the right places.
You probably could do it easily IF you maintained a pointer to the node at the greatest depth. If you don't, then you must find that node before begining your traversal. Also, your nodes will all have to have pointers to their parents.
I explain in a better way. I have an algebraic expression tree (so not balanced). I have to valuate it USING a queue (and only a queue). I asked this question because I think the only way is to take nodes starting from the lowest level, till the root...
example:
tree ( + ( * ( 2 ) ( 2 ) ) ( 3 ) )
I take the queue and:
enqueue(1);
enqueue(2);
(*) -----> dequeue; dequeue; result = 2 * 2; enqueue(result);
enqueue 3;
(+) -----> dequeue; dequeue; result = 4 + 3; give result;
so I need to have this traversal: 2 ; 2 ; * ; 3 ; +
I dont know if it's clear...
Provided I understood you question correctly: If you want to traverse the tree visiting a leaf first and the root last, you can visit the nodes on the way back as you traverse the tree.
function traverse(node)
for each child of node
traverse(child)
end
visit(node)
end
If you want to visit the nodes in level order, you could do something like this (though using a stack -- I'm not sure whether you didn't want one at all or some particular solution using a stack):
queue.push(root)
while queue is not empty
node = queue.pop()
for each child of node
queue.push(child)
stack.push(child)
end
end
while stack is not empty
visit(stack.pop())
end
You can do it using only a queue, but with worse time complexity, if you do it like this:
for i = treedepth down to 0
queue.push(root)
while queue is not empty
node = queue.pop()
if node has depth i
visit(node)
else
for each child of node
queue.push(child)
end
end
end
end
The tree depth and node levels can be found using an initial traversal, if needed.
However, if you are allowed to make recursive calls, you actually have access to a stack (the call stack). This can be exploited to implement the second solution, but making the stack implicit.
function unwind(queue)
if queue is not empty
node = queue.pop()
unwind(queue)
visit(node)
end
end
queue.push(root)
while queue is not empty
node = queue.pop()
for each child of node
queue.push(child)
queue2.push(child)
end
end
unwind(queue2)
And of course, if you have access to almost any other data structure (list, array, priority queue, double ended queue, etc.) you can easily implement a stack yourself. But then it would be rather pointless to forbid stacks in the first place.
Queue is only useful for traversing level-order from root to leaf of the tree.
You can use a Depth-first Traversal for printing a certain level.
Like this:
void printLevel(BinaryTree *p, int level) {
if (!p) return;
if (level == 1) {
cout << p->data << " ";
} else {
printLevel(p->left, level-1);
printLevel(p->right, level-1);
}
}
To print all levels from leaf up to root, you would need to find the maximum depth of the tree. This could be done easily using depth-first traversal as well (You can easily google the solution).
void printLevelOrder(BinaryTree *root) {
int height = maxHeight(root);
for (int level = height; level >= 1; level--) {
printLevel(root, level);
cout << endl;
}
}
The run time complexity is surprisingly, O(N), where N is the total number of nodes.
For more information and run-time complexity analysis, refer to the page below:
http://www.ihas1337code.com/2010/09/binary-tree-level-order-traversal-using_17.html

Resources