Find root that will always make a complete Binary Search Tree - java

I need to make a Complete Binary Search Tree.
If I have a method that looks like this:
public void createCompleteTree(Node n, int i)
And I for example use the number 9 as the i value, what do I do to find a root that will make a complete tree?
If I use 9 as the value, the numbers would be 1,2,3,4,5,6,7,8,9.
For a complete Binary search tree, the root must be 6, like below:
How can I make a method that knows this? It should work with any kind of number, so if I want to use number 14 it should be able to.
So far the only code I have is an insert method, which just checks if the number to be inserted is greater (goes to the right) or smaller (goes to the left) than the node we are currently at. x is the number to be inserted, t is the current node we are at in the tree:
private BinaryNode<AnyType> insert( AnyType x, BinaryNode<AnyType> t )
{
if( t == null )
return new BinaryNode<>( x, null, null );
int compareResult = x.compareTo( t.element );
if( compareResult < 0 )
t.left = insert( x, t.left );
else if( compareResult > 0 )
t.right = insert( x, t.right );
else
; // Duplicate; do nothing
return t;
}

Binary tree with N levels may contain from 2^(N-1) to 2^N - 1 element.
Last (lowest) level of tree that you describe may contain from 1 to 2^(N-1) elements in the strict order.
Tree with K elements and N levels contains K - 2^(N-1) + 1 elements on its last level.
Left subtree of this tree contains C = min(K - 2^(N-1) + 1, 2^(N-2)) elements.
So root of tree will be 2^(N-2) + C -th element

This is the solution:
From what I can tell computing the offset done by incrementing the offset for each additional element in length until you get to 1/2 of the width of a level. So, a BST with height of 4 has 8 elements in the lowest level. Lists of size 8, 9, 10, … 15 create BST with height of 4. For those lists the root index into the list is then 4, 5, 6, 7, 7, 7, 7, 7.
Seems to work
private int calcMid(int length) {
if ( length <= 4 )
return length / 2;
int levelSize = 1;
int total = 1;
while ( total < length ) {
levelSize *= 2;
total += levelSize;
}
int excess = length - (total - levelSize);
int minMid = (total - levelSize + 1) / 2;
if ( excess <= levelSize / 2 ) {
return minMid + (excess - 1);
} else {
int midExcess = levelSize/2;
return minMid + (midExcess - 1);
}
}
Found as part of this code:
https://stackoverflow.com/a/52749727/9899617

Root of your binary tree does not have to be constant.
There exists self-balancing trees. Check this out: enter link description here

Related

max height of binary search tree

so I need to find the max height of a binary tree but for some reason the result of the code provided below is off by 1.
For example if the max height is 3 the following code will give me 2.
if the max height is 4 the result will be 3.
I am not sure why? the root is not considered for the calculations of the max height therefore i set leftCounter and rightCounter to be 0.
any ideas?
public int getMaxHeight(BST.TreeNode<E> n) {
if(n == null) {
return 0;
}
int leftCounter = 0;
int rightCounter = 0;
if(n.left != null) {
leftCounter = getMaxHeight(n.left) +1 ;
}
if(n.right != null) {
rightCounter = getMaxHeight(n.right) +1 ;
}
if(leftCounter > rightCounter) {
return leftCounter;
}
else
return rightCounter;
}
the max height of this binary tree should be 3:
because of the elements 5,9,11. the root is not counted for the max
height.
15
_____|____
10 21
___|___ __|__
9 14 16 24
|__
25
Your code actually returns the correct value; it's just that you misunderstand what the height of a tree means. The height is the number of edges on the longest path from root to leaf, not the number of nodes on the path. So the following tree
3
_____|____
4 5
___|___ __|__
6 7 8 9
has a height of 2, not 3. What you are looking for is the number of levels in the tree, not the height.
public int getNumberOfLevels(BST.TreeNode<E> n) {
if(n == null) return 0;
int left = getNumberOfLevels(n.left);
int right = getNumberOfLevels(n.right);
return 1 + Math.max(left, right);
}
you can initialize leftCounter, and RightCounter by 1. Reason being if(n==null) -> false which means height is atleast 1

Cumulative bitwise operations

Suppose you have an Array A = [x, y, z, ...]
And then you compute a prefix/cumulative BITWISE-OR array P = [x, x | y, x | y | z, ... ]
If I want to find the BITWISE-OR of the elements between index 1 and index 6, how can I do that using this precomputed P array? Is it possible?
I know it works in cumulative sums for getting sum in a range, but I am not sure with bit operations.
Edit: duplicates ARE allowed in A, so A = [1, 1, 2, 2, 2, 2, 3] is a possibility.
There is impossible to use prefix/cumulative BITWISE-OR array to calculate the Bitwise-or of some random range, you can try with a simple case of 2 elements and verify yourself.
However, there is different approach, which is making use of prefix sum.
Assuming that we are dealing with 32 bit integer, we know that, for the bitwise-or sum from range x to y, the ith bit of the result will be 1 if there exists a number in range (x,y) that has ith bit is 1. So by answering this query repeatedly:
Is there any number in range (x, y) that has ith bit set to 1?
We can form the answer to the question.
So how to check that in range (x, y), there is at least a number that has bit ith set? we can preprocess and populate the array pre[n][32]which contain the prefix sum of all 32 bit within the array.
for(int i = 0; i < n; i++){
for(int j = 0; j < 32; j++){
//check if bit i is set for arr[i]
if((arr[i] && (1 << j)) != 0){
pre[i][j] = 1;
}
if( i > 0) {
pre[i][j] += pre[i - 1][j];
}
}
}
And, to check if bit i is set form range (x, y) is equalled to check if:
pre[y][i] - pre[x - 1][i] > 0
Repeat this check 32 times to calculate the final result:
int result = 0;
for (int i = 0; i < 32; i++){
if((pre[y][i] - (i > 0 ? pre[x - 1][i] : 0)) > 0){
result |= (1 << i);
}
}
return result;
A plain prefix array does not work, because in order to support arbitrary range queries it requires elements to have an inverse relative to the operator, so for example for addition that inverse is negation, for XOR that inverse is the element itself, for bitwise OR there is no inverse.
A binary indexed tree also does not work, for the same reason.
But a sideways heap does work, at the cost of storing about 2*n to 4*n elements (depending on how much is added by rounding up to a power of two), a much smaller expansion than 32*n. This won't make the most exciting use of a sideways heap, but it avoids the problems of an explicitly linked tree: chunky node objects (~32 bytes per node) and pointer chasing. A regular implicit binary tree could be used, but that makes it harder to relate its indexes to indexes in the original array. A sideways heap is like a full binary tree but, notionally, with no root - effectively we do have a root here, namely the single node on the highest level that is stored. Like a regular implicit binary tree a sideways heap is implicitly linked, but the rules are different:
left(x) = x - ((x & -x) >> 1)
right(x) = x + ((x & -x) >> 1)
parent(x) = (x & (x - 1)) | ((x & -x) << 1)
Additionally we can compute some other things, such as:
leftmostLeaf(x) = x - (x & -x) + 1
rightmostLeaf(x) = x + (x & -x) - 1
The lowest common ancestor of two nodes, but the formula is a bit large.
Where x & -x can be written as Integer.lowestOneBit(x).
The arithmetic looks obscure, but the result is a structure like this, which you can step through the arithmetic to confirm (source: The Art of Computer Programming volume 4A, bitwise tricks and techniques):
Anyway we can use this structure in the following way:
store the original elements in the leaves (odd indexes)
for every even index, store the bitwise OR of its children
for a range query, compute the OR of elements that represent a range that does not go outside the queried range
For the query, first map the indexes to leaf indexes. For example 1->3 and 3->7. Then, find the lowest common ancestor of the endpoints (or just start at the highest node) and recursively define:
rangeOR(i, begin, end):
if leftmostLeaf(i) >= begin and rightmostLeaf(i) <= end
return data[i]
L = 0
R = 0
if rightmostLeaf(left(i)) >= begin
L = rangeOR(left(i), begin, end)
if leftmostLeaf(right(i)) <= end
R = rangeOR(right(i), begin, end)
return L | R
So any node that corresponds to a range that is totally covered is used as a whole. Otherwise, if the left or right children are covered at all they must be recursively queried for their contribution, if either of them is not covered then just take zero for that contribution. I am assuming, by the way, that the query is inclusive on both ends, so the range includes both begin and end.
It turns out that rightmostLeaf(left(i)) and leftmostLeaf(right(i)) can be simplified quite a lot, namely to i - (~i & 1) (alternative: (i + 1 & -2) - 1) and i | 1 respectively. This seems awfully asymmetrical though. Under the assumption that i is not a leaf (it won't be in this algorithm, since a leaf is either fully covered or not queried at all), they become i - 1 and i + 1 respectively, much better. Anyway we can use that all the left descendants of a node have a lower index than it, and all right descendants have a higher index.
Written out in Java it could be (not tested):
int[] data;
public int rangeOR(int begin, int end) {
return rangeOR(data.length >> 1, 2 * begin + 1, 2 * end + 1);
}
private int rangeOR(int i, int begin, int end) {
// if this node is fully covered by [begin .. end], return its value
int leftmostLeaf = i - (i & -i) + 1;
int rightmostLeaf = i + (i & -i) - 1;
if (leftmostLeaf >= begin && rightmostLeaf <= end)
return data[i];
int L = 0, R = 0;
// if the left subtree contains the begin, query it
if (begin < i)
L = rangeOR(i - (Integer.lowestOneBit(i) >> 1), begin, end);
// if the right subtree contains the end, query it
if (end > i)
R = rangeOR(i + (Integer.lowestOneBit(i) >> 1), begin, end);
return L | R;
}
An alternative strategy is starting from the bottom and going up until the two sides meet, while collecting data on the way up. When starting at begin and its parent is to the right of it, the right child of the parent has a higher index than begin so it is part of the queried range - unless the parent was the common ancestor of both upwards "chains". For example (not tested):
public int rangeOR(int begin, int end) {
int i = begin * 2 + 1;
int j = end * 2 + 1;
int total = data[i];
// this condition is only to handle the case that begin == end,
// otherwise the loop exit is the `break`
while (i != j) {
int x = (i & (i - 1)) | (Integer.lowestOneBit(i) << 1);
int y = (j & (j - 1)) | (Integer.lowestOneBit(j) << 1);
// found the common ancestor, so done
if (x == y) break;
// if the low chain took a right turn, the right child is part of the range
if (i < x)
total |= data[x + (Integer.lowestOneBit(x) >> 1)];
// if the high chain took a left turn, the left child is part of the range
if (j > y)
total |= data[y - (Integer.lowestOneBit(y) >> 1)];
i = x;
j = y;
}
return total;
}
Building the tree in the first place is not trivial, building it in ascending order of indexes does not work. It can be built level-by-level, starting at the bottom. Higher nodes are touched early (for example for the first layer the pattern is 2, 4, 6, while 4 is in the second layer), but they will be overwritten anyway, it's fine to temporarily leave a non-final value there.
public BitwiseORRangeTree(int[] input) {
// round length up to a power of two, then double it
int len = input.length - 1;
len |= len >> 1;
len |= len >> 2;
len |= len >> 4;
len |= len >> 8;
len |= len >> 16;
len = (len + 1) * 2;
this.data = new int[len];
// copy input data to leafs, odd indexes
for (int i = 0; i < input.length; i++)
this.data[i * 2 + 1] = input[i];
// build higher levels of the tree, level by level
for (int step = 2; step < len; step *= 2) {
for (int i = step; i < this.data.length; i += step) {
this.data[i] = this.data[i - (step >> 1)] | this.data[i + (step >> 1)];
}
}
}

Triangle Puzzle: Find maximum total from top to bottom, starting at top moving moving to adjacent numbers

As the title suggests, I need to solve this puzzle.
5
9 6
4 6 8
0 7 1 5
The path I need to find is the max sum from top to bottom, only moving to adjacent children. So this path would be 5-9-6-7, with a sum of 27.
My code works for every set of data I input myself, but when I attempt the puzzles with the provided textFile's data, my sum/answer is not accepted as correct.
I cannot for the life of me figure out what is wrong with my code. Is there some exception I am not seeing?
public class Triangle
{
public static void main(String[] args) throws IOException
{
File file = new File("Tri.txt");
byte[] bytes = new byte[(int) file.length()];
try{
//Read the file and add all integers into an array with the correct size. Array size is found with number of bytes file.length()
//Parse string to integer
FileInputStream fis = new FileInputStream(file);
fis.read(bytes);
fis.close();
String[] valueStr = new String(bytes).trim().split("\\s+");
int[] list = new int[valueStr.length];
for (int i = 0; i < valueStr.length; i++)
list[i] = Integer.parseInt(valueStr[i]);
System.out.println(computeMaxPath(list));
}
catch(Exception e)
{
e.printStackTrace();
}
}
static int computeMaxPath(int[] list){
//Disregard row number one since it is the root. Start row number count at 2
int rowNumber = 2;
//set the sum to the value of the root.
int sum = list[0];
//selected index begins at the root, index 0
int selectedIndex = 0;
for (int j = 1; j < list.length; j=j+rowNumber)
{
// for every iteration the right child is found by adding the current selected index by z. What is z?
// the left child is of course found in the index -1 of the right child.
// z is the amount of of elements in the triangle's row. Row 3 has 3 elements, 4 has 4, etc.
// For exmaple, if the selectedIndex is index 4, its right child can be found by adding the index to the next row element count.
// 4 + 4 = 8 the right child is in index 8 and left is in index 7
int rightChildIndex = selectedIndex + rowNumber;
int leftChildIndex = selectedIndex + rowNumber - 1;
//set the appropriate index for the greater child's index
selectedIndex = list[rightChildIndex] >= list[leftChildIndex] ? rightChildIndex : leftChildIndex;
//increment the sum of the path
sum = sum + list[selectedIndex];
System.out.println(selectedIndex);
//increment the row number
rowNumber++;
}
return sum;
}
}
Essentially, my algorithm works by adding the string of ints from the text file into an array. The first selected index is of course the root node. To find the right child I add the selected index by the next row's length and subtract by 1 to find the left child index.
Any ideas?
This algorithm uses the wrong logic. In this case your algorithm works because it has the required properties to make your algorithm work, for other inputs this obviously not the case. For example consider the following (extreme) example:
1
1 0
0 0 9
Your algorithm works by simply always selecting the child with the larger sum, so in this case your algorithm would result in the path {1 , 1 , 0}, while the correct algorithm would result in {1 , 0 , 9}.
The correct algorithm would require to traverse the tree and search all paths in order to find the correct solution:
int findSum(int[] tree , int at_node){
if(at_node >= length(tree))
return 0 //end of the tree, quit recursive search
//maximum-path including node is the path with the greatest sum that includes either the left or right child of the node.
return max(findSum(tree , leftChild(at_node)) ,
findSum(tree , rightChild(at_node)) + tree[at_node]
}
As #JohnBollinger mentioned:
This top-to-bottom-approach is pretty simple. But on cost of efficiency. A more efficient, but also more efficient solution that only traverses each node exactly once. In the above stated algorithm a tree that represents the time each node was visited would look like a pascal's triangle, thus making 2 ^ height array-lookups. The bottom-top approach would only require height + height - 1 + ... + 1 lookups.
int findSumBottomTop(int[] tree , int height){
//initialize counter for previous level
int[] sums = new int[height + 1]
fill(sums , 0)
//counter for the level counts down to 1 (note that this variable is not 0-based!!!)
int lvl = height
//counter for nodes remaining on the current level (0-based)
int remaining_in_lvl = lvl - 1
//maximum-paths for each node on the current level
int[] next_level = new int[lvl]
//iterate over all nodes of the tree
for(int node = length(tree) - 1; node > -1 ; node--){
int left_max_path = sums[remaining_in_lvl]
int right_max_path = sums[remaining_in_lvl + 1]
next_level[remaining_in_lvl] = max(right_max_path , left_max_path) + tree[node]
//decrement counter for remaining nodes
remaining_in_lvl -= 1
if(remaining_in_lvl == -1){
//end of a level was encountered --> continue with lvl = lvl - 1
lvl--
//update to match length of next
remaining_in_lvl = lvl - 1
//setup maximum-path counters for next level
sums = next_level
next_level = new int[sums.length - 1]
}
//there is exactly one sum remaining, which is the sum of the maximum-path
return sums[0];
}
The basic idea of this would be the following:
Consider this example tree:
0 ^ 6
0 1 | 3 6
0 1 2 | 1 3 5
0 1 2 3 | 0 1 2 3
0 0 0 0 0
tree traversal sums
sums would be the values of sums that would be produced for each level. We simply start searching at the bottom and searching the maximum-path from each node in a level to the bottom. This would be the maximum of the maximum-path of the left child and the maximum-path of the right child + the value of the node.
if there is not limit on the number of rows, for example, input can have hundred of rows. it worth to implement this like a directed acyclic graph and then use an algorithm to find the largest path
Try this.
static int computeMaxPath(int[] a, int self, int row) {
if (self >= a.length)
return 0;
else
return a[self] + Math.max(
computeMaxPath(a, self + row + 1, row + 1),
computeMaxPath(a, self + row + 2, row + 1));
}
static int computeMaxPath(int[] a) {
return computeMaxPath(a, 0, 0);
}
This is one of my favorite Project Euler problems (#18). Just for reference, here's a complete bottom-to-top solution in the Haskell language:
f = foldr (\a b -> let c = zipWith (+) a b
in if null (drop 1 c)
then c
else zipWith max c (tail c)) (repeat 0)
main = print (f z) where
  z = map (map read . words) (lines s) :: [[Int]]

Recursive method to count the number of combinations

this is a java code that recursively counts the number of payments in specific coins (ex. 1, 2, 5, 20, 50 etc.). I have tried to figure out how it works but can't seem to get it. Could somebody please be so kind and explain the math and logic behind the code and how this recursion works? I would really appreciate it.
// Returns the count of ways we can sum S[0...m-1] coins to get sum n
int count( int S[], int m, int n ){
// If n is 0 then there is 1 solution (do not include any coin)
if (n == 0)
return 1;
// If n is less than 0 then no solution exists
if (n < 0)
return 0;
// If there are no coins and n is greater than 0, then no solution exist
if (m <=0 && n >= 1)
return 0;
// count is sum of solutions (i) including S[m-1] (ii) excluding S[m-1]
return count( S, m - 1, n ) + count( S, m, n-S[m-1] );
}
The method works like this:
First statements are the stopping conditions for the current recursion (without these for all cases then you end up with an infinite loop which ultimately end in a StackOverFlow)
The final line is where the calculation occurs. Each of the statements is reducing the problem into smaller chunks by:
count( S, m - 1, n ) reduces the number of coins (m-1) which excludes the last coin in the next recursive call to
count( S, m, n-S[m-1]) uses the last coin in the array and reduces the sum that is need to reach by the value of that coin
Consider this small example:
S[] = {1,2) // We have a 1 and 2 cent coin
m = S.length // Consider all possibilities ( = 2)
n = 3 // How many ways can we make 3c
// Obviously with: 1x1c + 1x2c
// and: 3x1c
The recursion as a tree; left branch = count( S, m - 1, n ), right branch = count( S, m, n-S[m-1]):
m=2;n=3
/ \
m=1;n=3 m=2;n=1
/ \ / \
m=0;n=3 m=1;n=2 m=1;n=1 m=2;n=-1
/ \ / \
m=0;n=2 m=1;n=1 m=0;n=1 m=1;n=0
/ \
m=0;n=1 m=1;n=0
This recursion can be thought of as a Pre-order Traversal of this tree.
If you then consider the conditions of the method for where a solution is found or not. So at the leaf nodes where n = 0.
Each which comes about like this:
First solution
m=1;n=3 - Exclude the last coin (2c)
m=1;n=2 - Use this coin (1c) and reduce by 1
m=1;n=1 - Use this coin (1c) and reduce by 1
m=1;n=0 - Use this coin (1c) and reduce by 1
n = 0 - a solution (3x1c)
Second Solution
m=2;n=1 - Use this coin(2c) and reduce by 2
m=1;n=1 - Exclude the last coin (2c)
m=1;n=0 - Use this coin (1c) and reduce by 1
n = 0 - a solution (1x2c + 1x2c)
At each node a value is returned - 0 (no solution) or 1 (a solution) - to add to the total count of solutions found. Once the recursion ends this final value is returned and is the number of solutions.
Some additional notes:
This piece of code will only consider the first m coins in the array S so to consider all the possible ways the initial call to the method needs to have m == S.length
Assumes that each coin can be used multiple times
Code modification with print statements to see the recursion:
public static void main(String[] args){
int[] coins = new int[]{1,2};
System.out.println("Final Count = " + count(coins, coins.length, 3, ""));
}
public static int calls = 0;
public static int count( int S[], int m, int n , String from){
calls++;
System.out.print("Call#" + calls + ": " + from + "; m = " + m + "; n = " + n);
// If n is 0 then there is 1 solution (do not include any coin)
if (n == 0)
{
System.out.println(" - Solution Found");
return 1;
}
// If n is less than 0 then no solution exists
if (n < 0)
{
System.out.println(" - No Solution Found n < 0");
return 0;
}
// If there are no coins and n is greater than 0, then no solution exist
if (m <=0 && n >= 1)
{
System.out.println(" - No Solution Found (other Case)");
return 0;
}
System.out.println();
// count is sum of solutions (i) including S[m-1] (ii) excluding S[m-1]
return count( S, m - 1, n , from + "E" ) + count( S, m, n-S[m-1], from + "I" );
}
From the code, I'm assuming S is an array with at least m elements, with each element representing an available coin denomination, and n is the intended sum.
The comments really say it all, except that the last comment is backwards. count( S, m - 1, n ) is the number of solutions excluding the last coin in the current range. count( S, m, n-S[m-1] ) is the number of solutions using that coin.
The exclude case simply drops the last coin in the current range by reducing m by one.
The include case uses it by reducing n by that coin's value. Since the include case does not also reduce m, presumably any coin denomination can be used multiple times. It does not matter if the coin is too big - that is taken care of by returning 0 if n < 0.
If anything about the base cases is not clear from the comments, please ask a specific question.

Combining intersecting intervals [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Union of intervals
how to overlap intervals efficiently
Given a list of intervals say all integers, it should be possible to collapse them into one interval if they do intersect or overlap otherwise the given intervals remain unaffected.
Say, if the input is e.g.I [(2-6), (1-4), (8-12)], the expected output is [(1-6), (8-12)]
e.g. II [(4-7), (2-6), (1-4), (8-12), (7-9)] the expected output is [(1-12)].
Correction: Missed the sorting part, so yes, it is a O(nlogn) time NOT O(n). Thanks for pointing that out.
I have written and tested a O(nlogn) time and O(2n) space algorithm approach that works. Sharing the code of this approach below. I am interested in hearing different approaches to solving this problem, possibly more efficient.
//Assuming each of the intervals, (2-6) and so on are represented as "Interval" objects (class definition shown below), where low = 2 and high = 6
// Step1: Sort by the low endpoint of the given intervals
// Step2: find Union of the sorted intervals
//Input:
List<Interval> intervalList = new ArrayList<Interval>();
//Output:
List<Interval> unionList = new ArrayList<Interval>();
private static final Comparator<Interval> Low_EPSorter = new LowEPSorter();
class Interval {
int low, high;
Interval(int l, int h, int m) {
low = l;
high = h;
}
}
////-------BEGIN: Method which finds the Union Of given intervals ----//////
void UnionOfIntervals() {
//Find intersection and combine intervals as necessary
int sz = intervalList.size();
// sort by low endpoint
Collections.sort(intervalList, Low_EPSorter);
for(int i = 0; i < sz; i++) {
int j = i;
if(j > 0) {
if( Intervals.intersect(intervalList.get(j), intervalList.get(j-1)) ) {
Interval v = union(intervalList.get(j), intervalList.get(j-1));
checkAndAdd(v, unionList);
}
else {
if(i == 1) {
unionList.add(intervalList.get(j-1));
unionList.add(intervalList.get(j));
}
else {
unionList.add(intervalList.get(j));
}
} //No intersection
} //If 2 elements atleast
}
//Print intervals after union
System.out.println("Input intervals after sorting:");
for(Interval v : intervalList) {
System.out.print(v.low + "," + v.high + " ");
}
System.out.println();
System.out.println("Union of intervals:");
for(Interval v : unionList) {
System.out.print(v.low + "," + v.high + " ");
}
}
void checkAndAdd(Interval x, List t) {
int top = t.size()-1;
if( top >=0 && Intervals.intersect(unionList.get(top), x) ) {
Interval v = union(unionList.get(top), x);
t.remove(top);
t.add(v);
}
else {
t.add(x);
}
}
////-------END: Method which finds the Union Of given intervals ----//////
////--- helper methods --- ////
static boolean intersect(Interval a, Interval b) {
boolean r = false;
if(b.high < a.low || b.low > a.high)
r = false;
else if(a.low <= b.high && b.low <= a.high)
r = true;
return r;
}
Interval union(Interval a, Interval b) {
int l = (a.low < b.low) ? a.low : b.low;
int max = (a.high > b.high) ? a.high : b.high;
return new Interval(l, max);
}
private static class LowEPSorter implements Comparator<Interval> {
public int compare(Interval a, Interval b) {
int r = 0;
if(a.low < b.low)
r = -1;
else if(a.low > b.low)
r = 1;
return r;
}
}
Take a array of size n ( where n is the largest number ) and fills the interval start and end with 1 and -1 respectively.
By this I mean if the intervals are
{[1-4],[6-8]}
Then the array elements would be like
array[1]=1,array[4]=-1,array[6]=1,array[8]=-1
and the rest all other locations of array would be set to zero.
Now traverse the array and by scanning the array we can get the intervals, like for the case
{[1-4],[2-5],[7-9]},
first fill the array as stated above , the array A would look like(assuming the starting index be 1):
A=[1,1,0,-1,-1,0,1,0,1]
Now traverse the array A from beginning and take a variable sum=0 and add the value stored at location of array to sum.
Stating the sum at each index of array:
At location 1: sum = 1 ( 1 at index 1 )
At location 2: sum = 2 ( 1 at index 2 )
At location 3: sum = 2 ( 0 at index 3 )
At location 4: sum = 1 ( -1 at index 4 )
At location 5: sum = 0 ( -1 at index 5 )
Now the sum reaches to zero it means an interval ends here so the new interval will be [1-5]
At location 6: sum = 0 ( 0 at index 6 )
At location 7: sum = 1 ( 1 at index 7 )
(At location 7 the sum again becomes greater than zero it means an interval has just started)
At location 8: sum = 1 ( 0 at index 8 )
At location 9: sum = 0 ( -1 at index 9 )
Interval started at location 7 just ended so the new interval ranges would be
{[1-5],[7-9]}
Hope it helps.
If you are looking for a more efficient algorithm than O(n) for this problem, I don't believe that you will find it. No matter what data structure you use for storing the initial interval values, your worst case scenario is that none of the intervals overlap and that you have to check each interval to confirm this, hence the O(n). Even with a HashMap and a very elaborate key structure, you are still looking at O(n).
With this being said, I'm not sure if any other approach is worth investigating as you have already found an algorithm that solves it in the best time possible, O(n).
Just an idea: manage a set of non-intersecting intervals. Starting from empty set, add incoming intervals. If new interval intersects with one or two existing intervals, combine them. To calculate intersections, use 2 TreeMaps referencing to the same set of intervals but by different keys: low and high bounds.
Just another idea:
Use a Bool array
make all the values false by default
for all the intervals(in input) make the values true
Scan the updated array and get the final intersected result.

Categories