Related
I was given this question during a recent interview: Given a BST whose nodes contains an Integer as value, find all subtrees whose nodes fall between integers X (min) and Y (max), where X<Y. These subtrees cannot overlap each other.
I have solved variations of this problem, example - print keys of a BST that fall in a given range. But couldn't figure this one out, since it involves finding all connected sub-graphs of the main graph/tree that satisfy very specific constraints. Any pointers/help/pseudo code is appreciated.
Added notes -
The problem defined the datastructure of node as having a left pointer, right pointer, and a integer value. There was no way to mark a node.
Was asked to solve this in Java.
When i said subtree/subgraph, i meant a connected set of nodes, not a list of disjoint nodes. sorry for the confusion.
The concrete solution depends on the definition of a subtree. Consider the following BST:
5
3
2
4
8
-
9
And we want to find the subtrees in the range [4,8]. It is obvious that the 4 node belongs to the output. But what about the other half tree? If a subtree refers to a node with all of its children, then that's the entire result. If a subtree is actually a subset of the input nodes, the nodes 5 and 8 belong to the result but their connections to the 3 and 9 nodes have to be stripped away.
In any case, the following algorithm can handle both. The preprocessor define WHOLE_SUBTREES defines whether subtrees are entire subcomponents with all children.
static List<BSTNode> FindSubtreesInRange(BSTNode root, int rangeMin, int rangeMax)
{
var result = new List<BSTNode>();
if (IsTreeWithinRange(root, rangeMin, rangeMax, int.MinValue, int.MaxValue, result))
result.Add(root);
return result;
}
static bool IsTreeWithinRange(BSTNode root, int rangeMin, int rangeMax, int treeRangeMin, int treeRangeMax, List<BSTNode> resultList)
{
if (treeRangeMin >= rangeMin && treeRangeMax <= rangeMax)
return true;
if ( treeRangeMin > rangeMax || treeRangeMax < rangeMin)
return false;
if (root.Key < rangeMin)
{
if (root.Right != null && IsTreeWithinRange(root.Right, rangeMin, rangeMax, root.Key + 1, treeRangeMax, resultList))
resultList.Add(root.Right);
return false;
}
if (root.Key > rangeMax)
{
if (root.Left != null && IsTreeWithinRange(root.Left, rangeMin, rangeMax, treeRangeMin, root.Key, resultList))
resultList.Add(root.Left);
return false;
}
if (root.Left == null && root.Right == null)
return true;
if (root.Left == null)
{
#if WHOLE_SUBTREES
if (!IsTreeWithinRange(root.Right, rangeMin, rangeMax, root.Key + 1, treeRangeMax, resultList))
root.Right = null;
return true;
#else
return IsTreeWithinRange(root.Right, rangeMin, rangeMax, root.Key + 1, treeRangeMax, resultList);
#endif
}
if (root.Right == null)
{
#if WHOLE_SUBTREES
if (!IsTreeWithinRange(root.Left, rangeMin, rangeMax, treeRangeMin, root.Key, resultList))
root.Left = null;
return true;
#else
return IsTreeWithinRange(root.Left, rangeMin, rangeMax, treeRangeMin, root.Key, resultList);
#endif
}
var leftInRange = IsTreeWithinRange(root.Left, rangeMin, rangeMax, treeRangeMin, root.Key, resultList);
var rightInRange = IsTreeWithinRange(root.Right, rangeMin, rangeMax, root.Key + 1, treeRangeMax, resultList);
if (leftInRange && rightInRange)
return true;
#if WHOLE_SUBTREES
if (!leftInRange)
root.Left = null;
if (!rightInRange)
root.Right = null;
return true;
#else
if (leftInRange)
resultList.Add(root.Left);
if (rightInRange)
resultList.Add(root.Right);
return false;
#endif
}
The idea is as follows: If only one subtree of a given node lies within the given range, then this must be the root of a new subtree. If both lie in the range, then they are not the root of a subtree. Instead, the parent level should handle the according decision.
The algorithm starts with the following: We traverse the tree and remember in which ranges the keys may be (treeRangeMin/Max). This allows a fast check if an entire subtree lies in the given range (first statement of the IsTreeWithinRange method.
The next two statements handle the case if the current node's key lies outside the given range. Then only one of it's subtrees might be within the range. If that's the case, this subtree is added to the result list.
Next, we check if the subtrees exist. If both do not, then the current tree is completely contained within the range.
If only one subtree exists, then the action differs based on whether we may split trees. If we may split the tree, the following happens: If the subtree is not within the range, we cut it off and return true (because the current node is contained within the given range). If we may not split trees, we just propagate the result of the recursive call.
Lastly, if both children exist. If one of them is not contained within the range, we cut it off (if we are allowed to). If we are not allowed, we add the subtree to the result list that lies within the given range.
This is pretty much simple to solve. For having subtrees that do not overlap, i included a marked field, initialized to false for every node.
The algorithm is as follows:
Traverse the BST beginning from root using DFS method. Now if a node is encountered in DFS , that is not marked and it satisfies the constraint(falls between X and Y), then there is a solution with a subtree rooted at that node, but we do not know how big that subtree can be? So we do the following:
Pass its left and right child to another method check, which will do the following:
Traverse the subtree rooted at the node and traverse it in DFS fashion as long as constraints are satisfied and nodes encountered are unmarked. As soon as any one condition is violated, then return.
Now, the original DFS method may be called on already marked vertices but the if condition will evaluate to false. Hence the target is achieved.
I solved it using JAVA language and for condition that keys lie between 10 and 21(exclusive). Here is the code:
One more thing,if nothing is printed after subtree rooted at X with childs as, then it denotes a subtree with single node.
class BST
{
public Node insert(Node x,int key)
{
if(x==null)
return new Node(key,null,null,false);
else if(key>x.key)
{
x.right=insert(x.right,key);
return x;
}
else if(key<x.key)
{
x.left=insert(x.left,key);
return x;
}
else {x.key=key;return x;}
}
public void DFS(Node x)
{
if(x==null)
return;
if(x.marked==false&&x.key<21&&x.key>10)
{
System.out.println("Subtree rooted at "+x.key+" with childs as");
x.marked=true;
check(x.left);
check(x.right);
}
DFS(x.left);
DFS(x.right);
}
public void check(Node ch)
{
if(ch==null)
return;
if(ch.marked==false&&ch.key<21&&ch.key>10)
{
System.out.println(ch.key);
ch.marked=true;
check(ch.left);
check(ch.right);
}
else return;
}
public static void main(String []args)
{
BST tree1=new BST();
Node root=null;
root=tree1.insert(root,14);
root=tree1.insert(root,16);
root=tree1.insert(root,5);
root=tree1.insert(root,3);
root=tree1.insert(root,12);
root=tree1.insert(root,10);
root=tree1.insert(root,13);
root=tree1.insert(root,20);
root=tree1.insert(root,18);
root=tree1.insert(root,23);
root=tree1.insert(root,15);
tree1.DFS(root);
}
}
class Node
{
Node left,right;
int key;
boolean marked;
Node(int key,Node left,Node right,boolean b)
{
b=false;
this.key=key;
this.left=left;
this.right=right;
}
}
Feel free for any queries.
This can be done recursively, and we keep a list of subtrees which we append to whenever a compliant subtree is found. The recursive function returns true when the subtree rooted at the argument node is wholly in range. It's the caller decision (the parent node) to determine what to do when the child's recusruve call returns true or false. For example, if the current node value is in the range , and its children's subtrees are also completely in range, then we simply return true. But if only one of the children's subtrees is in the range, and the other is not in range, then we return false (since the not all of the current node subtree is in the range), but we also append the child that was in the range to the list. If the current node value is not in the range we return false, but we also check either the left or right child, and append it to the list of subtrees if it's compliant:
def subtree_in_range(root, x, y):
def _subtree_in_range(node):
in_range=True
if node:
if node.val>=x and node.val<=y:
if not _subtree_in_range(node.left):
in_range=False
if node.right and _subtree_in_range(node.right):
l.append(node.right)
elif not _subtree_in_range(node.right):
in_range=False
if node.left:
l.append(node.left)
else:
in_range=False
s=node.left
if node.val<x:
s=node.right
if s and _subtree_in_range(s):
l.append(s)
return in_range
l=[]
if _subtree_in_range(root):
l.append(root)
return l
When doing range search, workhorse function for range, written in some generic language, might like this:
function range(node, results, X, Y)
{
if node is null then return
if node.key is in [X, Y] then results.add(node.key)
if node.key < Y then range(node.right, results, X, Y)
if node.key > X then range(node.left, results, X, Y)
}
For subtree version problem we need to store subtree root nodes instead of keys and keep track if we are in subtree or not. The latter can be solved by passing subtree wise parent in range call, which also is required for new structure creation. Desired function is below. As you can see, main change is one extra argument and node.key in [X, Y] branch
function range_subtrees(node, parent, results, X, Y)
{
if node is null then return
node_clone = null
if node.key is in [X, Y] then
node_clone = node.clone()
if parent is null then
results.add(node_clone)
else
parent.add_child(node_clone)
if node.key < Y then range_subtrees(node.right, node_clone, results, X, Y)
if node.key > X then range_subtrees(node.left, node_clone, results, X, Y)
}
This should create a collection of subtree root nodes, where each subtree is a copy of original tree's structure.
I am having a hard time understanding the stack frame picture of a binary tree traversal
I have this function to calculate the height of a binary tree starting with the root node
public static int height(TreeNode<String> t){
if(t == null)
return -1;
int left = height(t.left);
System.out.println("left " +left);
int right = height(t.right);
System.out.println("right " +right);
if(left > right)
return left + 1;
else
return right + 1;
}
I have a binary tree setup as follows
Root
/ \
A B
The returned height value is 1 which is correct but I am having a hard time understanding the stack frame flow of the two recursive functions. What is the flow order like?
I get the following out values on my two print statements
left -1
right -1
left 0
left -1
right -1
right 0
Executions of height follow the tree shape in order.
height is called first on Root and then recursively on Root.left in line 4 and then again recursively on Root.left.left, which is null. At this point the last execution of height returns -1 and terminates. The remaining last execution assigns this to left and prints it.
This same execution nex calls height on Root.left.right. This is also null, so -1 is returned and printed, after which the if statement computes -1 + 1 == 0 and returns this.
We are now back to the call on Root.left, and the returned value 0 is assigned to left and is printed, whereupon the same execution calls height again and again on down to Root.right.left, which is null, so again -1 is printed.
You should see the pattern at this point.
left -1 A.left is null
right -1 A.right is null
left 0 Root.left is A
left -1 B.left is null
right -1 B.right is null
right 0 Root.right is B
But if you want a clearer picture you should probably code something like
public static int height(TreeNode<String> t){
if(t == null)
return -1;
int left = height(t.left);
System.out.println(t+" left "+left);
int right = height(t.right);
System.out.println(t+" right "+right);
if(left > right)
return left + 1;
else
return right + 1;
}
Not sure what exactly is your problem, but here it goes:
1) Initial call to height with root node
2) First recursive call to height with A node
3) Second recursive call to height with A.left (which is null)
4) First if statement fires, returns -1 - back to after the call in point 3)
5) First print statement - "left -1". Third recursive call to height with A.right (which is null)
6) if fires again, returns -1 - back to after the call in point 5)
7) Second print statement - "right -1".
8) left is -1 and right is -1, hence right + 1 is returned (0)- back to after the call in point 2)
9) Third print statement - "left 0".
10) Next recursive call of height with B node. Repeat steps 2-8 substituting B for A. Repeating this yields two print statements "left -1" and "right -1".
11) After getting back, right is 0 and thus the last print statement reads "right 0".
12) left is 0 and right is 0, hence right + 1 is returned
Like many newbies, my head blows up from recursion. I looked up a lot of answers/explanations on SO. but I am still unclear on the concept. (This is not homework, I am trying to relearn what I unlearned and recursion was never a string point)
Given a preorder traversal, construct a binary tree. With recursion, it has to be deceptively simple :) but I just can't get it.
I see that the order of the arr has to be in the order nodes are inserted. What bugs me is:
What if the node already has a left/right? How does this work?
How can the recursion insert nodes, in say the following preorder?
12, 10, 6, 13
15 is root, 5, 3 and left
How does 6 get inserted correctly as 10's left child?
12
10 13
6*
Here is the skeleton code:
main()
{
int[] arr = {};
//make the first node a root node.
node n = new node(arr[0]);
buildbst(n, arr, 0)
}
buildbst(node root, int[] arr, int i)
{
if (i == arr.length) return;
if (arr[i] < root.data)
root.left = new node (arr[i]);
else
root.right = new node(arr[i]);
buildbst(root.left, arr, i++);
buildbst(root.right, arr, i++);
}
EDIT:
I just realised, if I pass in the recursive call buildbst(root.left, arr+i, i++)
is that the right way? Or am I approaching this all wrong - a mish-mash of dynamic programming and recursion and divide and conquer...
It can't already have a left / right child. You call it for the root, which has no children to start. Then you call it for the left child and create children where appropriate and call the function for those children and so on. You never visit the left child again once you go right and you can't get to a node from a function called on its child (since there is no connection up the tree, except the recursion stack).
This is what should happen when given 12, 10, 6, 13:
Creates the root 12
Calls buildbst(node(12), arr, 1)
Create node(12).left = node(10)
Calls buildbst(node(10), arr, 2)
Create node(10).left = node(6)
Calls buildbst(node(6), arr, 3)
13 > 12, must be right child of 12, so do nothing
13 > 12, must be right child of 12, so do nothing
Create node(12).right = node(13)
Calls buildbst(node(13), arr, 3)
Oh look, no more elements, we're done.
The above is not what will happen with your code for 2 reasons:
Your code will only create either a left or a right child, not both (because of the if-else))
Your code doesn't have the must be right child of '12' check, which is a little complex
The below code should cover it.
node buildbst(int[] arr)
{
node n = new node(arr[0]);
// 9999999999 is meant to be > than the biggest element in your data
buildbst(n, arr, 1, 9999999999);
return node;
}
int buildbst(node current, int[] arr, int i, int biggestSoFar)
{
if (i == arr.length) return i;
// recurse left
if (arr[i] < current.data)
{
current.left = new node(arr[i++]);
i = buildbst(current.left, arr, i, current.data);
}
// recurse right
if (i < arr.length && arr[i] < biggestSoFar)
{
current.right = new node(arr[i++]);
i = buildbst(current.right, arr, i, biggestSoFar);
}
return i;
}
Explanation:
The purpose of biggestSoFar is to prevent:
15 15
/ /\
5 versus (the correct) 5 20
/ \ /
1 20 1
When recursing left from 15 for example, we need to stop processing elements as soon as we get an element > 15, which will happen when we get 20. Thus we pass current.data and stop processing elements if we get a bigger value.
When recursing right from 5 for example, we need to stop processing elements as soon as we get an element > 15, which will happen when we get 20. Thus we pass biggestSoFar and stop processing elements if we get a bigger value.
I'm stuck on a code challenge, and I want a hint.
PROBLEM: You are given a tree data structure (without cycles) and are asked to remove as many "edges" (connections) as possible, creating smaller trees with even numbers of nodes. This problem is always solvable as there are an even number of nodes and connections.
Your task is to count the removed edges.
Input:
The first line of input contains two integers N and M. N is the number of vertices and M is the number of edges. 2 <= N <= 100.
Next M lines contains two integers ui and vi which specifies an edge of the tree. (1-based index)
Output:
Print the number of edges removed.
Sample Input
10 9
2 1
3 1
4 3
5 2
6 1
7 2
8 6
9 8
10 8
Sample Output :
2
Explanation : On removing the edges (1, 3) and (1, 6), we can get the desired result.
I used BFS to travel through the nodes.
First, maintain an array separately to store the total number of child nodes + 1.
So, you can initially assign all the leaf nodes with value 1 in this array.
Now start from the last node and count the number of children for each node. This will work in bottom to top manner and the array that stores the number of child nodes will help in runtime to optimize the code.
Once you get the array after getting the number of children nodes for all the nodes, just counting the nodes with even number of nodes gives the answer. Note: I did not include root node in counting in final step.
This is my solution. I didn't use bfs tree, just allocated another array for holding eachnode's and their children nodes total number.
import java.util.Scanner;
import java.util.Arrays;
public class Solution {
public static void main(String[] args) {
int tree[];
int count[];
Scanner scan = new Scanner(System.in);
int N = scan.nextInt(); //points
int M = scan.nextInt();
tree = new int[N];
count = new int[N];
Arrays.fill(count, 1);
for(int i=0;i<M;i++)
{
int u1 = scan.nextInt();
int v1 = scan.nextInt();
tree[u1-1] = v1;
count[v1-1] += count[u1-1];
int root = tree[v1-1];
while(root!=0)
{
count[root-1] += count[u1-1];
root = tree[root-1];
}
}
System.out.println("");
int counter = -1;
for(int i=0;i<count.length;i++)
{
if(count[i]%2==0)
{
counter++;
}
}
System.out.println(counter);
}
}
If you observe the input, you can see that it is quite easy to count the number of nodes under each node. Consider (a b) as the edge input, in every case, a is the child and b is the immediate parent. The input always has edges represented bottom-up.
So its essentially the number of nodes which have an even count(Excluding the root node). I submitted the below code on Hackerrank and all the tests passed. I guess all the cases in the input satisfy the rule.
def find_edges(count):
root = max(count)
count_even = 0
for cnt in count:
if cnt % 2 == 0:
count_even += 1
if root % 2 == 0:
count_even -= 1
return count_even
def count_nodes(edge_list, n, m):
count = [1 for i in range(0, n)]
for i in range(m-1,-1,-1):
count[edge_list[i][1]-1] += count[edge_list[i][0]-1]
return find_edges(count)
I know that this has already been answered here lots and lots of time. I still want to know reviews on my solution here. I tried to construct the child count as the edges were coming through the input and it passed all the test cases.
namespace Hackerrank
{
using System;
using System.Collections.Generic;
using System.Linq;
class Program
{
static void Main(string[] args)
{
var tempArray = Console.ReadLine().Split(' ').Select(x => Convert.ToInt32(x)).ToList();
int verticeNumber = tempArray[0];
int edgeNumber = tempArray[1];
Dictionary<int, int> childCount = new Dictionary<int, int>();
Dictionary<int, int> parentDict = new Dictionary<int, int>();
for (int count = 0; count < edgeNumber; count++)
{
var nodes = Console.ReadLine().Split(' ').Select(x => Convert.ToInt32(x)).ToList();
var node1 = nodes[0];
var node2 = nodes[1];
if (childCount.ContainsKey(node2))
childCount[node2]++;
else childCount.Add(node2, 1);
var parent = node2;
while (parentDict.ContainsKey(parent))
{
var par = parentDict[parent];
childCount[par]++;
parent = par;
}
parentDict[node1] = node2;
}
Console.WriteLine(childCount.Count(x => x.Value % 2 == 1) - 1);
}
}
}
My first inclination is to work up from the leaf nodes because you cannot cut their edges as that would leave single-vertex subtrees.
Here's the approach that I used to successfully pass all the test cases.
Mark vertex 1 as the root
Starting at the current root vertex, consider each child. If the sum total of the child and all of its children are even, then you can cut that edge
Descend to the next vertex (child of root vertex) and let that be the new root vertex. Repeat step 2 until you have traversed all of the nodes (depth first search).
Here's the general outline of an alternative approach:
Find all of the articulation points in the graph.
Check each articulation point to see if edges can be removed there.
Remove legal edges and look for more articulation points.
Solution - Traverse all the edges, and count the number of even edges
If we remove an edge from the tree and it results in two tree with even number of vertices, let's call that edge - even edge
If we remove an edge from the tree and it results in two trees with odd
number of vertices, let's call that edge - odd edge
Here is my solution in Ruby
num_vertices, num_edges = gets.chomp.split(' ').map { |e| e.to_i }
graph = Graph.new
(1..num_vertices).to_a.each do |vertex|
graph.add_node_by_val(vertex)
end
num_edges.times do |edge|
first, second = gets.chomp.split(' ').map { |e| e.to_i }
graph.add_edge_by_val(first, second, 0, false)
end
even_edges = 0
graph.edges.each do |edge|
dup = graph.deep_dup
first_tree = nil
second_tree = nil
subject_edge = nil
dup.edges.each do |e|
if e.first.value == edge.first.value && e.second.value == edge.second.value
subject_edge = e
first_tree = e.first
second_tree = e.second
end
end
dup.remove_edge(subject_edge)
if first_tree.size.even? && second_tree.size.even?
even_edges += 1
end
end
puts even_edges
Note - Click Here to check out the code for Graph, Node and Edge classes
Saw this question recently:
Given 2 arrays, the 2nd array containing some of the elements of the 1st array, return the minimum window in the 1st array which contains all the elements of the 2nd array.
Eg :
Given A={1,3,5,2,3,1} and B={1,3,2}
Output : 3 , 5 (where 3 and 5 are indices in the array A)
Even though the range 1 to 4 also contains the elements of A, the range 3 to 5 is returned Since it contains since its length is lesser than the previous range ( ( 5 - 3 ) < ( 4 - 1 ) )
I had devised a solution but I am not sure if it works correctly and also not efficient.
Give an Efficient Solution for the problem. Thanks in Advance
A simple solution of iterating through the list.
Have a left and right pointer, initially both at zero
Move the right pointer forwards until [L..R] contains all the elements (or quit if right reaches the end).
Move the left pointer forwards until [L..R] doesn't contain all the elements. See if [L-1..R] is shorter than the current best.
This is obviously linear time. You'll simply need to keep track of how many of each element of B is in the subarray for checking whether the subarray is a potential solution.
Pseudocode of this algorithm.
size = bestL = A.length;
needed = B.length-1;
found = 0; left=0; right=0;
counts = {}; //counts is a map of (number, count)
for(i in B) counts.put(i, 0);
//Increase right bound
while(right < size) {
if(!counts.contains(right)) continue;
amt = count.get(right);
count.set(right, amt+1);
if(amt == 0) found++;
if(found == needed) {
while(found == needed) {
//Increase left bound
if(counts.contains(left)) {
amt = count.get(left);
count.set(left, amt-1);
if(amt == 1) found--;
}
left++;
}
if(right - left + 2 >= bestL) continue;
bestL = right - left + 2;
bestRange = [left-1, right] //inclusive
}
}