find the smallest depth leaf node in bst - binary-search-tree

Need to get the leaf node that has minimum depth. I cannot think of a good way to do it without storing additional information in each node, please suggest, thanks very much.

The brute force solution is a breadth-first search terminating at the first leaf found, this will be easier to implement iteratively than recursively.
See for instance the pseudo-code in my answer to "Breadth First Vs Depth First" just add another condition to the while-loop.
BTW--This will get you a leaf with the minimum depth, as there may be more than one at that depth. Getting the full set of minimum depth leaves is a little harder. I guess go with an iterative deepening strategy.
Finding out what level that node is one.
Three choices:
Find the node first and the search down the tree for it. It sounds wasteful, but that second search requires visiting only as many nodes as the level, so it really is fast.
Alternately you can keep track as you go. You use three counters levelCounter, thisLevelCounter and nextLevelCounter. Every time you more to a new node you decrement thisLevelCounter, and when it hits zero you've moved down a level so do
levelCounter++
thisLevelCounter = nextLevelCounter
nextLevelCounter = 0
Every time you add a child node to the search list, increment nextLevelCounter.
Every time you store a new child node increment nextLevelCounter
Finally, the iterative deepening strategy gives you the sucess level for free (which iteration finds it...) and has the same order of performance (though a slightly higher multiplier) as the breadth first search.

Here code version (hope I didn't miss any error check):
void min_leaf(node_t *t, int *min, int lev, node_t **n) {
if (!t) {
return;
}
if (lev > *min) {
printf("Back from %d at lev %d, min: %d already found\n",
t->key,
lev,
*min);
return;
}
if (!t->left && !t->right) {
if (*min > lev) {
*min = lev;
*n = t;
}
} else {
min_leaf(t->left, min, lev+1, n);
min_leaf(t->right, min, lev+1, n);
}
}
void bst_print_min_leaf(bst_t* bst) {
int min = 10000; /* Replace it with some really large number */
node_t *minn = NULL;
min_leaf(bst->root, &min, 0, &minn); /*level: root is level 0 */
if (minn) printf("min leaf is at depth: %d: (%p:%d)\n", min, minn, minn->key);
}

Related

Time complecity big O of modifided DFS (using DFS to search for path in maze)

I wrote a program that solves maze that is not ideal (has 1 or more correct paths) using recursion DFS algorithm. I have a problem with the time complexity of my program, becouse i read on the internet that the time complexity for DFS is O(v+n) where n is the number of node and v the number of edges. In my case if i don't find the correct path i go back and go the other bath if there is a branching, so i am thinking how it will affect my time complexity. NOTE(mazes that i use are directed graph, so i can't go back and have to go from up to down)
void DFS3 (int *visited, double **graf, int *paths, int **results, int n, int v, int end){
visited [ v ] = 1;
if(v==end){ // if we find the end we write the table of paths (our path) to the table of results
results[r][0]=k+1;
for(int j=1;j<k+1;j++)
results[r][j]=paths[j-1];
results[r][k+1]=end; // we write the last node (our wayout) to the table of results (it's not included in table of path)
r++;
visited[v]=0; // we mark the last node as not visited
}
for(int i = 1; i < n*n+1; i++ ){
if( ( graf [ v ][ i ] != 0 ) && visited [ i ] != 1 ){
paths[k]=v; // if we find the connection between node (a path) we write it to the paths and run the DFS3 of this node
k++;
DFS3 ( visited, graf, paths,results, n, i, end);
}
}
paths[k]=0; // if there is a dead end we go back to the first branching and delete the nodes that aren't correct path
k--;
visited[v]=0; // we mark them as unvisited
}
In DFS, as long as you are marking the nodes as already visited, you can avoid visiting them again. So, while you still have to evaluate all the edges (and there can be O(|V|^2 many directed edges), you don't have to go back down old paths.
If you want the shortest path through the maze (and not just the first one you happen to find), DFS is not great since you'd need to let it keep running to find all paths, then choose the shortest one. BFS will get you the shortest path first, but almost surely take longer to find a path through than DFS would. But, it's also O(|V| + |E|)

Efficient way of Square of a Sorted Array

I am solving leetcode solution. The question is
Given an integer array nums sorted in non-decreasing order, return an array of the squares of each number sorted in non-decreasing order.
Example 1:
Input: nums = [-4,-1,0,3,10]
Output: [0,1,9,16,100]
Explanation: After squaring, the array becomes [16,1,0,9,100].
After sorting, it becomes [0,1,9,16,100].
Example 2:
Input: nums = [-7,-3,2,3,11]
Output: [4,9,9,49,121]
I solved this through map and then use sorted() for sorting purpose and lastly converted in toIntArray().
My solution
class Solution {
fun sortedSquares(nums: IntArray): IntArray {
return nums.map { it * it }.sorted().toIntArray()
}
}
After all I am taking a look in the discuss success, I found this solution
class Solution {
fun sortedSquares(A: IntArray): IntArray {
// Create markers to use to navigate inward since we know that
// the polar ends are (possibly, but not always) the largest
var leftMarker = 0
var rightMarker = A.size - 1
// Create a marker to track insertions into the new array
var resultIndex = A.size - 1
val result = IntArray(A.size)
// Iterate over the items until the markers reach each other.
// Its likely a little faster to consider the case where the left
// marker is no longer producing elements that are less than zero.
while (leftMarker <= rightMarker) {
// Grab the absolute values of the elements at the respective
// markers so they can be compared and inserted into the right
// index.
val left = Math.abs(A[leftMarker])
val right = Math.abs(A[rightMarker])
// Do checks to decide which item to insert next.
result[resultIndex] = if (right > left) {
rightMarker--
right * right
} else {
leftMarker++
left * left
}
// Once the item is inserted we can update the index we want
// to insert at next.
resultIndex--
}
return result
}
}
The guy also mention in the title Kotlin -- O(n), 95% time, 100% space
So my solution is equal in time and space complexity with other solution with efficient time and space? Or Is there any better solution?
So my solution is equal in time and space complexity with other solution with efficient time and space?
No, your solution runs in O(n log n) time, as it relies on sorted(), which likely runs in O(n log n). Since the alternative solution does not sort the items, it indeed runs on O(n) time. Both solutions use O(n) space, although your solution uses three times as much space (each of map, sorted and toIntArray create a copy of the input).

What is the time complexity of below function?

I was reading book about competitive programming and was encountered to problem where we have to count all possible paths in the n*n matrix.
Now the conditions are :
`
1. All cells must be visited for once (cells must not be unvisited or visited more than once)
2. Path should start from (1,1) and end at (n,n)
3. Possible moves are right, left, up, down from current cell
4. You cannot go out of the grid
Now this my code for the problem :
typedef long long ll;
ll path_count(ll n,vector<vector<bool>>& done,ll r,ll c){
ll count=0;
done[r][c] = true;
if(r==(n-1) && c==(n-1)){
for(ll i=0;i<n;i++){
for(ll j=0;j<n;j++) if(!done[i][j]) {
done[r][c]=false;
return 0;
}
}
count++;
}
else {
if((r+1)<n && !done[r+1][c]) count+=path_count(n,done,r+1,c);
if((r-1)>=0 && !done[r-1][c]) count+=path_count(n,done,r-1,c);
if((c+1)<n && !done[r][c+1]) count+=path_count(n,done,r,c+1);
if((c-1)>=0 && !done[r][c-1]) count+=path_count(n,done,r,c-1);
}
done[r][c] = false;
return count;
}
Here if we define recurrence relation then it can be like: T(n) = 4T(n-1)+n2
Is this recurrence relation true? I don't think so because if we use masters theorem then it would give us result as O(4n*n2) and I don't think it can be of this order.
The reason, why I am telling, is this because when I use it for 7*7 matrix it takes around 110.09 seconds and I don't think for n=7 O(4n*n2) should take that much time.
If we calculate it for n=7 the approx instructions can be 47*77 = 802816 ~ 106. For such amount of instruction it should not take that much time. So here I conclude that my recurrene relation is false.
This code generates output as 111712 for 7 and it is same as the book's output. So code is right.
So what is the correct time complexity??
No, the complexity is not O(4^n * n^2).
Consider the 4^n in your notation. This means, going to a depth of at most n - or 7 in your case, and having 4 choices at each level. But this is not the case. In the 8th, level you still have multiple choices where to go next. In fact, you are branching until you find the path, which is of depth n^2.
So, a non tight bound will give us O(4^(n^2) * n^2). This bound however is far from being tight, as it assumes you have 4 valid choices from each of your recursive calls. This is not the case.
I am not sure how much tighter it can be, but a first attempt will drop it to O(3^(n^2) * n^2), since you cannot go from the node you came from. This bound is still far from optimal.

How to maximize the value of the path?

We are given a N*N grid. And we are at the top left corner of the grid initially. Every square of the grid has some value attached to it, that is if someone reaches that square he wins the amount of money in dollars equal to the value attached to the square. Now legal moves are one step towards the right or one step towards the bottom. We have to reach the bottom right corner of the grid in a path such that we can maximize the amount of money won. Obviously we have to stay within the grid and cannot wander off it.
I started this problem by a greedy approach that at each step we look at the immediate right square and immediate square below the occupied square, and at each step choose the square having the higher value. But this does not give the right result all the time. For example in the following grid,
{ 6, 9, 18, 1 }
{ 2, 10, 0, 2 }
{ 1, 100, 1, 1 }
{ 1, 1, 1, 1 }
here my algorithm gives the maximum valued path as
6 -> 9 -> 18 -> 1 -> 2 -> 1 -> 1
which totals to 37, but we could have earned more on the path
6 -> 9 -> 10 -> 100 -> 1 -> 1 -> 1
which totals to 128. Could you people please help me in building a suitable algorithm? I have not yet coded this one because it would give a wrong output anyway. I don't know how to cope with this problem without brute force which would consist of seeing the values in all the paths not containing the square with the minimum value, and then finding the maximum.
#include <iostream>
#include <queue>
using namespace std;
int main()
{ int n; cin >> n;
int a[n+1][n+1], b[n+1][n+1];
for (int i=0;i<n;i++)
{
for (int j=0;j<n;j++)
{
cin >> a[i][j]; b[i][j]=a[i][j];
}
}
queue <int> q; int u,v,m=0;
q.push(0);q.push(0);
while (q.size()!=0)
{
u=q.front(); q.pop(); v=q.front(); q.pop();
if (v<n-1)
{
m=b[u][v]+a[u][v+1];
if (m>b[u][v+1])
{ b[u][v+1]=m; }
q.push(u);q.push(v+1);
}
if (u<n-1)
{
m=b[u][v]+a[u+1][v];
if (m>b[u+1][v])
{ b[u+1][v]=m; }
q.push(u+1);q.push(v);
}
}
cout << b[n-1][n-1];
return 0;
}
The problem can be solved with the following approach. Each cell at position (i,j) gets associated with a value val(i,j) which is the maximum total value possible by reaching it with the described legal moves (to the bottom, to the right) starting at position (0,0). The value at position (0,0) is the value from the grid, in the sequel termed as grid(i,j) for every i, j in {0,...,N-1}. We obtain the follwing recurrence relation
val(i,j) = grid(i,j) + max{ val(i-1,j), // path coming from upper cell
val(i,j-1) // path coming from left cell
}
where we suppose that indices outside of {0,...,N-1} * {0,...N-1} yield a value of negative infinity and are never really used. The recurrence relation is valid as there are at most two cases to reach a cell, namely from its upper neighbor or its left neighbour (except for cells at the border, which perhaps may be reached from only one neighbour).
The key point for an efficient evaluation of val is to organize the calculation of values in a sequence such that all needed neighbors are already evaluated; this can be done by succesively staring calculation at the leftmost cell for which val is not yet calculated and working from there in an upwards-rightwards manner until the top row is reached. This is iterated until position val(N-1,N-1) is evaluated, which yields the desired result.
If in addition the specific path to (N-1,N-1) is demanded, either backtracking or some auxiliary data structure has to be used to store how the value in the above recurrence relation was calculated, i.e. which term yields the maximum.
Edit
Alternatively, the evaluation can be done row-wise from left to right, which also has the desired property that all necessary values for the recurrence relation are already calculated; this is apparently easier to implement. In either case, the runtime bound will be O(n^2).
Actually this is a problem which is solvable using dynamic programming. You only need to adapt the algorithm for calculating the edit distance allowing for varying rewards.
The algorithm is described for example in https://web.stanford.edu/class/cs124/lec/med.pdf
The basic idea is that you start from top and you fill a square whenever you now its neighbouring (top,left) field.
The value you put in the field is the value of the higher of the two neighbours and the value of the current field. When you reach bottom right you just have to follow the path back.

ClosestKeyBefore(k) in a BST

I am trying to find the closest key to k before k is reached in a binary search tree.
The more I think about it, the more I get confused as to where it could possibly be.
Formal definition of this method:
Return the key of the item with the largest key less than or equal to k
At first I thought it would be the parent of k.. but after looking at BSTs its becoming very unclear to me. I need the algorithm for this.
It should run in O(h) time where h is the height of the tree.
Thanks!
I guess you could make use of recursion to remember your current best value (ie. closest so far). If value is bigger than the current node, you search the right subtree and you know your current node is the best answer IF there is nothing found in right subtree. In the search of the right subtree, you return your current best answer (less than or equal) and this answer will just propagate up.
When value is less than current node, you search the left subtree. The current node cannot be your best answer because it is greater than the value you're searching. Hence just return whatever left subtree returns.
The code would look something like this. This is just a simple example with integers and it returns -1 when no valid values are found.
public static int closestKeyBefore(Node node, int val) {
if(node==null) return -1;
if(node.value == val) return val;
int retval = -1;
if(node.value<val) { // Value is bigger than current node, search right
retval = closestKeyBefore(node.right,val);
if(retval==-1) { //Not found in right subtree
retval = node.value; // Current node is the best answer. return this
}
}
else { // Value is smaller than current node, search left
retval = closestKeyBefore(node.left,val);
}
return retval;
}