Reputation: 906
I have two arrays A
and B
. And I have to find the number of elements required by array C
to fit into a specific length.
Length = B[i]-A[i]
For example:
A[0] = 1 B[0] = 4
A[1] = 4 B[1] = 5
A[2] = 5 B[2] = 9
A[3] = 8 B[3] = 10
and
C[0] = 4
C[1] = 6
C[2] = 7
C[3] = 10
C[4] = 2
Answer would be 4
.
Explanation:
C[0] is falling in the range of (A[0],B[0]) and (A[1],B[1])
C[1] is falling in the range of (A[2],B[2])
C[2] is of no use
C[3] is falling in the range of (A[3],B[3])
so the total is 4
My approaches:
Traditional approach: Simple looping every element of C
in array A
and if find in the range set the value of A[i]
and B[i]
to minus infinity or remove them(for Arraylist).
HashTable: Storing the value in hash table as A[i]
as key and B[i]
as value. Then finding the element for a given C[i]
and removing the key. I think it is not a good approach
Please provide me a efficient algorithm for this.
Upvotes: 5
Views: 3598
Reputation: 1208
I experimented with some tree structures but eventually I went back to a simple binary search, so this is now O(nlogn):
static int getAnswerV2a(int[] A, int[] B, int[] C) {
int len = A.length;
ArrayList<Interval> intervals = new ArrayList<Interval>(len);
for (int i=0; i<len; i++) {
intervals.add(new Interval(A[i], B[i]));
}
for (int i = 0; i < C.length; i++) {
int c = C[i];
binarySearch(intervals, 0, intervals.size(), c);
if (intervals.isEmpty()) return i+1;
}
return -1;
}
static class Interval {
int start, end;
Interval(int start, int end) {
this.start = start;
this.end = end;
}
boolean matches(int c) {
return start <= c && c <= end;
}
}
// Find an interval (between indices i and j) that matches c
// When found, remove it from the List and adjust indices accordingly
static void binarySearch(ArrayList<Interval> intervals, int i, int j, int c) {
if (i==j)
return;
else if (i+1==j) {
if (intervals.get(i).matches(c))
intervals.remove(i);
}
else {
int mid = (int) Math.floor((i+j) / 2);
Interval interval = intervals.get(mid);
if (interval.start > c)
binarySearch(intervals, i, mid, c);
else if (interval.end < c)
binarySearch(intervals, mid+1, j, c);
else {
intervals.remove(mid);
binarySearch(intervals, i, j-1, c);
}
}
}
Upvotes: 0
Reputation:
for(int i =0 ;i<C.length;i++)
for(int j=0;j<A.length;j++)
{
if(A[i]<=C[i] && B[i]>=C[i])
{ answer++;
A[i] = Integer.Max_Value;
}
}
It will give you a right answer
Upvotes: 0
Reputation: 26501
Here is one quick sketch for solving it in O(n log n). It may be not the best approach in practice, but is just meant to offer an n log n approach.
To summarize, it's a sweeping line technique. Or rather sweeping point, in this case.
1. Sort arrays A, B, and C (cost: n log n). Think of the arrays as queues
(that is, "remove" elements from the left by increasing a start index).
2. Create current-intervals set (initially empty. Has O(log n) operations.)
2. While A not empty or B not empty or C not empty:
3. get the lowest element from the first (if any) elements in A, B, and C.
4. if it's an A:
5. Add the corresponding interval to the current-intervals list
6. else if it's a B:
7. Remove the corresponding interval from the current-intervals list
8. else it must be a C, so:
9. The answer to the query corresponding to C is the current-intervals list
(in the current state)
Upvotes: 0
Reputation: 1208
This is a more efficient version using ArrayLists:
int len = arrA.length;
ArrayList<Integer> A = new ArrayList<Integer>(len);
ArrayList<Integer> B = new ArrayList<Integer>(len);
for (int i=0; i<len; i++) {
A.add(arrA[i]);
B.add(arrB[i]);
}
for (int i = 0; i < C.length; i++) {
int c = C[i];
int j = 0;
while (j<A.size() && A.get(j) <= c) {
if (B.get(j) >= c) {
A.remove(j);
if (A.isEmpty()) return i+1;
B.remove(j);
}
else {
j++;
}
}
}
return 0;
Upvotes: 1
Reputation: 1208
Ok so based on your answer, we have no choice but to check each element of C against A and B. But since A and B are in ascending order, performance should be reasonable:
int len = A.length;
boolean[] eliminated = new boolean[len]; // initially all elements are false
int totalEliminated = 0;
for (int i = 0; i < C.length; i++) {
int c = C[i];
for (int j = 0; j < len && A[j] <= c; j++) {
if (B[j] >= c && !eliminated[j]) {
System.out.println(c + " eliminates " + A[j] + " - " + B[j]);
eliminated[j] = true;
if (++totalEliminated == len) {
return i+1;
}
}
}
}
return 0;
Upvotes: 1
Reputation: 1541
If the input ranges are already ordered from smallest to largest (B[i] <= A[i+1]):
int bottomRangeIndex = 0;
int topRangeIndex = numberOfABRangesYouHave - 1;
int answerCounter = 0;
C.sort(); // Sort C so that it goes from smallest to largest number.
for number in C: {
if (number < A[bottomRangeIndex]) {
continue;
} else if (number > B[topRangeIndex]) {
return answerCounter;
} else {
while ((number > B[bottomRangeIndex++]) && (bottomRangeIndex <= topRangeIndex)) {
// Just keeps incrementing bottomRangeIndex.
}
if (bottomRangeIndex > topRangeIndex) {
return answerCounter;
} else {
answerCounter++;
bottomRangeIndex++;
}
}
}
This might have some bug that I am not thinking of, but basically what this does is:
Try this and see if it works. If this is homework and you really can't sort C, then you can modify this to give you the right answer with a small amount of tweaking, but I won't say how...
Upvotes: 1