Reputation: 39
Input:
Given an array of integers T = [t0, t1, t2, ... tk]
representing a row where each element is the maximum waiting time. And an array E = [e0, e1, e2, ... ei]
representing all possible expiration times. Finally, an integer K
is given which is the maximum size of a container.
Problem:
For each expiration time E
, it is necessary to obtain the position of the last element T
that was able to enter the container of size K, and each waiting time in T
must be greater than or equal to the expiration time E
to be able to enter the container.
Example Case 1:
Input:
K = 2; T = [1, 4, 4, 3, 1, 2, 6]; E = [1, 2, 3, 4, 5, 6, 7]
Output:
Kth = [2, 3, 3, 3, 0, 0, 0]
Explanation:
In E[0]
, the expiration time is 1, so the 2nd in row T
will be the last to enter the container, so Kth[0] = 2nd
;
In E[1]
, the expiration time is 2, so the 3rd in row T
will be the last to enter the container since the 1st element has expired, so Kth[1] = 3rd
;
In E[2]
, the expiration time is 3, so the 3rd in row T
will be the last to enter the container since the 1st element has expired, so Kth[2] = 3rd
;
In E[3]
, the expiration time is 4, so the 3rd in row T
will be the last to enter the container since the 1st element has expired, so Kth[3] = 3rd
;
In E[4]
, the expiration time is 5, in this case almost all elements of T
except the last one have expired, however, as it was not possible to complete the container, it must return position 0, therefore Kth[4] = 0
;
And so on for E[5]
and E[6]
.
Here is the code I've been able to come up with but it runs in O(E * T) time which is too slow for the performance constraints:
public static List<Integer> kthElement(int k, List<Integer> T, List<Integer> E) {
List<Integer> kthElements = new ArrayList<>();
for (int i = 0; i < E.size(); i++) {
System.out.println(E.get(i));
int currentElement = 0;
int elementsFilled = 0;
for (int j = 0; j < T.size(); j++) {
if (elementsFilled >= k || k > T.size()) {
break;
}
if (T.get(j) >= E.get(i)) {
elementsFilled++;
currentElement = j + 1;
}
}
if (elementsFilled >= k) {
kthElements.add(currentElement);
} else {
kthElements.add(0);
}
}
return kthElements;
}
How can I improve the performance of this algorithm?
Upvotes: 1
Views: 137
Reputation: 827
I think you can easily do this in O(E + T) instead of O(E x T).
The loop is quite simple, and will appear to be O(E x T) at first glance because of the nesting, but the inner loop never gets reset within the outer loop so it is indeed O(E + T).
I will assume that E < 65536 in the following code.
List<Integer> out = new ArrayList<>();
int inPos = 0;
int kCnt = 0;
int last = 0;
int[] kTracker = new int[65536];
for (int i = 0; i < E.size(); i++)
{
// Remove Expired
kCnt -= kTracker[i];
// Fill Container
while (kCnt < K && inPos < T.length)
{
int exp = i + T.get(inPos);
if (exp < kTracker.length)
{
// don't bother tracking if > E.max, as it won't affect the output.
kTracker[exp]++;
}
last = inPos;
kCnt++;
inPos++;
}
// record output
out.add(last);
}
If there aren't guarantees on E.max, but there are guarantees on T.max, and we constrain memory, then we can implement a rolling array instead.
For example, if T.max = 10, we can use the below code to replace bits from the code above...
// Creating a rolling kTracker
int kTrackerNow = 0;
int[] kTracker = new int[10];
...
// Expiring elements
kCnt -= kTracker[kTrackerNow];
kTracker[kTrackerNow] = 0;
// Progressing to the next time period
kTrackerNow = (kTrackerNow + 1) % kTracker.length;
// Tracking a new item from T[inPos]
kTracker[(kTrackerNow + T[inPos]) % kTracker.length]++;
Finally, if we don't have any guarantees on the input, we can use HashMap<> to replace the tracking array. As the HashMap will perform memory allocation for each and every element, it will be far slower than the above 2 solutions, but it can deal with all kinds of input without restriction.
List<Integer> out = new ArrayList<>();
int inPos = 0;
int kCnt = 0;
int last = 0;
Map<Integer, Integer> kTracker = new HashMap<>();
for (int i = 0; i < E.size(); i++)
{
// Remove Expired
Integer removed = kTracker.remove(i);
if (removed != null)
{
kCnt -= removed;
}
// Fill Container
while (kCnt < K && inPos < T.length)
{
kTracker.put(i + T.get(inPos), kTracker.getOrDefault(i + T.get(inPos), 0) + 1);
last = inPos;
kCnt++;
inPos++;
}
// record output
out.add(last);
}
Upvotes: 1