简体   繁体   English

如何提高具有嵌套循环的算法的时间复杂度?

[英]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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM