[英]How to improve the time complexity of this algorithm that has nested loops?
Input:输入:
Given an array of integers T = [t0, t1, t2, ... tk]
representing a row where each element is the maximum waiting time.给定一个整数数组
T = [t0, t1, t2, ... tk]
表示一行,其中每个元素是最大等待时间。 And an array E = [e0, e1, e2, ... ei]
representing all possible expiration times.还有一个数组
E = [e0, e1, e2, ... ei]
表示所有可能的到期时间。 Finally, an integer K
is given which is the maximum size of a container.最后,给出一个 integer
K
,它是容器的最大尺寸。
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.对于每一个过期时间
E
,需要获取最后一个元素T
的 position ,并且T
中的每个等待时间必须大于或等于过期时间E
才能够进入容器。
Example Case 1:示例案例 1:
Input:输入:
K = 2; T = [1, 4, 4, 3, 1, 2, 6]; E = [1, 2, 3, 4, 5, 6, 7]
Output: 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
;在
E[0]
中,过期时间为 1,所以第T
行的第 2 个将是最后一个进入容器的,所以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
;在
E[1]
中,过期时间为 2,因此第T
行中的第 3 个将是第一个元素过期后最后进入容器的,所以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
;在
E[2]
中,过期时间为 3,因此第T
行中的第 3 个将是第一个元素过期后最后进入容器的,所以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
;在
E[3]
中,过期时间为 4,因此第T
行中的第 3 个将是第一个元素过期后最后进入容器的,所以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
;在
E[4]
中,过期时间为 5,在这种情况下, T
中除了最后一个元素几乎所有元素都已过期,但是,由于无法完成容器,它必须返回 position 0,因此Kth[4] = 0
;
And so on for E[5]
and E[6]
.依此类推
E[5]
和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:这是我能够提出的代码,但它在 O(E * T) 时间内运行,这对于性能限制来说太慢了:
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?如何提高该算法的性能?
I think you can easily do this in O(E + T) instead of O(E x T).我认为你可以在 O(E + T) 而不是 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).循环非常简单,由于嵌套,乍一看似乎是 O(E x T),但内循环永远不会在外循环内重置,所以它确实是 O(E + T)。
I will assume that E < 65536 in the following code.我将在下面的代码中假设 E < 65536。
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.如果 E.max 没有保证,但 T.max 有保证,并且我们约束 memory,那么我们可以实现滚动数组。
For example, if T.max = 10, we can use the below code to replace bits from the code above...例如,如果 T.max = 10,我们可以使用下面的代码替换上面代码中的位...
// 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.最后,如果我们对输入没有任何保证,我们可以使用 HashMap<> 来替换跟踪数组。 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.
由于 HashMap 会为每个元素执行 memory 分配,它会比上述两种解决方案慢得多,但它可以无限制地处理各种输入。
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);
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.