我的代码的哪一部分让我的表现受到影响? (Codility的MaxCounter)

时间:2015-05-27 08:48:25

标签: java algorithm data-structures big-o time-complexity

我有以下问题:

您将获得N个计数器,最初设置为0,您可以对它们进行两种操作:

    increase(X) − counter X is increased by 1,
    max counter − all counters are set to the maximum value of any counter.

给出了M个整数的非空零索引数组A.此数组表示连续操作:

    if A[K] = X, such that 1 ≤ X ≤ N, then operation K is increase(X),
    if A[K] = N + 1 then operation K is max counter.

例如,给定整数N = 5且数组A使得:

A[0] = 3
A[1] = 4
A[2] = 4
A[3] = 6
A[4] = 1
A[5] = 4
A[6] = 4

每次连续操作后计数器的值为:

(0, 0, 1, 0, 0)
(0, 0, 1, 1, 0)
(0, 0, 1, 2, 0)
(2, 2, 2, 2, 2)
(3, 2, 2, 2, 2)
(3, 2, 2, 3, 2)
(3, 2, 2, 4, 2)

目标是在所有操作之后计算每个计数器的值。

写一个函数:

class Solution { public int[] solution(int N, int[] A); } 

给定一个整数N和一个由M个整数组成的非空零索引数组A,返回一个表示计数器值的整数序列。

例如,给定:

A[0] = 3
A[1] = 4
A[2] = 4
A[3] = 6
A[4] = 1
A[5] = 4
A[6] = 4

该函数应返回[3,2,2,4,2],如上所述。

假设:

    N and M are integers within the range [1..100,000];
    each element of array A is an integer within the range [1..N + 1].

复杂度:

    expected worst-case time complexity is O(N+M);
    expected worst-case space complexity is O(N), beyond input storage (not counting the storage required for input arguments).

可以修改输入数组的元素。

我使用以下代码回答了这个问题,但只有80%而不是100%的性能,尽管有O(N + M)时间复杂度:

public class Solution {

    public int[] solution(int N, int[] A) {

        int highestCounter = N;
        int minimumValue = 0;
        int lastMinimumValue = 0;
        int [] answer = new int[N];

        for (int i = 0; i < A.length; i++) {
            int currentCounter = A[i]; 
            int answerEquivalent = currentCounter -1;

            if(currentCounter >0 && currentCounter<=highestCounter){
                answer[answerEquivalent] = answer[answerEquivalent]+1; 

                if(answer[answerEquivalent] > minimumValue){
                    minimumValue = answer[answerEquivalent];
                }
            }

            if (currentCounter == highestCounter +1 && lastMinimumValue!=minimumValue){
                lastMinimumValue = minimumValue;
                Arrays.fill(answer, minimumValue);
            }
        }
        return answer;
    }

}

我的表现在哪里受苦?该代码给出了正确的答案,但尽管具有合适的时间复杂性,但仍未执行最新规范。

7 个答案:

答案 0 :(得分:4)

每当遇到Arrays.fill(answer, minimumValue);的“最大计数器”操作时,您应该跟踪由于“最大计数器”操作而分配的最后一个最大值,而不是调用O(N),并且在处理完所有操作后,只更新整个阵列一次。这将需要O(N + M)。

我将变量名称从min更改为max,以减少混淆。

public class Solution {

    public int[] solution(int N, int[] A) {

        int highestCounter = N;
        int maxValue = 0;
        int lastMaxValue = 0;
        int [] answer = new int[N];

        for (int i = 0; i < A.length; i++) {
            int currentCounter = A[i]; 
            int answerEquivalent = currentCounter -1;

            if(currentCounter >0 && currentCounter<=highestCounter){
                if (answer[answerEquivalent] < lastMaxValue)
                    answer[answerEquivalent] = lastMaxValue +1;
                else 
                    answer[answerEquivalent] = answer[answerEquivalent]+1; 

                if(answer[answerEquivalent] > maxValue){
                    maxValue = answer[answerEquivalent];
                }
            }

            if (currentCounter == highestCounter +1){
                lastMaxValue = maxValue;
            }
        }
        // update all the counters smaller than lastMaxValue
        for (int i = 0; i < answer.length; i++) {
            if (answer[i] < lastMaxValue)
                answer[i] = lastMaxValue;
        }
        return answer;
    }

}

答案 1 :(得分:1)

以下操作为O(n)时间:

Arrays.fill(answer, minimumValue);

现在,如果给出一个测试用例,其中max counter操作经常重复(比如总操作的n/3) - 你得到了O(n*m)算法(最坏情况分析) ),而不是O(n+m)

您可以使用initializes an array in O(1)每次此操作发生时的算法,在O(n+m)时间内对其进行优化。
这样可以将最坏情况时间复杂度从O(n*m)减少到O(n+m) 1

(1)理论上,使用相同的想法,它甚至可以在O(m)中完成 - 无论计数器的数量大小,但第一个allocation of the arrays takes O(n) time in java

答案 2 :(得分:0)

这有点像@Eran的解决方案,但将功能封装在一个对象中。基本上 - 跟踪max值和atLeast值,让对象的功能完成其余的工作。

private static class MaxCounter {

    // Current set of values.
    final int[] a;
    // Keeps track of the current max value.
    int currentMax = 0;
    // Min value. If a[i] < atLeast the a[i] should appear as atLeast.
    int atLeast = 0;

    public MaxCounter(int n) {
        this.a = new int[n];
    }

    // Perform the defined op.
    public void op(int k) {
        // Values are one-based.
        k -= 1;
        if (k < a.length) {
            // Increment.
            inc(k);
        } else {
            // Set max
            max(k);
        }
    }

    // Increment.
    private void inc(int k) {
        // Get new value.
        int v = get(k) + 1;
        // Keep track of current  max.
        if (v > currentMax) {
            currentMax = v;
        }
        // Set new value.
        a[k] = v;
    }

    private int get(int k) {
        // Returns eithe a[k] or atLeast.
        int v = a[k];
        return v < atLeast ? atLeast : v;
    }

    private void max(int k) {
        // Record new max.
        atLeast = currentMax;
    }

    public int[] solution() {
        // Give them the solution.
        int[] solution = new int[a.length];
        for (int i = 0; i < a.length; i++) {
            solution[i] = get(i);
        }
        return solution;
    }

    @Override
    public String toString() {
        StringBuilder s = new StringBuilder("[");
        for (int i = 0; i < a.length; i++) {
            s.append(get(i));
            if (i < a.length - 1) {
                s.append(",");
            }
        }
        return s.append("]").toString();
    }
}

public void test() {
    System.out.println("Hello");
    int[] p = new int[]{3, 4, 4, 6, 1, 4, 4};
    MaxCounter mc = new MaxCounter(5);
    for (int i = 0; i < p.length; i++) {
        mc.op(p[i]);
        System.out.println(mc);
    }
    int[] mine = mc.solution();
    System.out.println("Solution = " + Arrays.toString(mine));
}

答案 3 :(得分:0)

My solution:100 \ 100

class Solution
    {
        public int maxCounterValue;

    public int[] Counters;

    public void Increase(int position)
    {
        position = position - 1;
        Counters[position]++;
        if (Counters[position] > maxCounterValue)
            maxCounterValue = Counters[position];
    }

    public void SetMaxCounter()
    {
        for (int i = 0; i < Counters.Length; i++)
        {
            Counters[i] = maxCounterValue;
        }
    }



    public int[] solution(int N, int[] A)
    {
        if (N < 1 || N > 100000) return null;
        if (A.Length < 1) return null;

        int nlusOne = N + 1;
        Counters = new int[N];
        int x;
        for (int i = 0; i < A.Length; i++)
        {
            x = A[i];
            if (x > 0 && x <= N)
            {
                Increase(x);
            }

            if (x == nlusOne && maxCounterValue > 0) // this used for all maxCounter values in array. Reduces addition loops
                SetMaxCounter();
            if (x > nlusOne)
                return null;
        }

        return Counters;

    }
}

答案 4 :(得分:0)

  1. (@ molbdnilo:+1!)因为这只是一个算法测试,所以对变量过于冗长没有任何意义。对于从零开始的数组索引调整,“answerEquivalent”?给我一个休息!只需回答[A [i] - 1]即可。
  2. 测试说假设A值总是介于1和N + 1之间。因此不需要检查这一点。
  3. fillArray(。)是一个O(N)过程,它在O(M)过程中。当期望的最大复杂度为O(M + N)时,这使得整个代码成为O(M * N)过程。 实现此目的的唯一方法是仅转发计数器的当前最大值。这使您可以在A [i]为N + 1时始终保存正确的最大计数器值。后一个值是之后所有增量的一种基线值。在所有A值被操作之后,那些从未通过数组条目递增的计数器然后可以通过复杂度为O(N)的第二个for循环被带到全计数器基线。
  4. 看看伊兰的解决方案。

答案 5 :(得分:0)

这就是我们如何消除O(N*M)复杂性 在这个解决方案中,我没有为每个A[K]=N+1填充结果数组,而是尝试保留所有元素的最小值,并在完成所有操作后更新结果数组。

如果有增加操作,则更新该位置:

  if (counter[x - 1] < minVal) {
     counter[x - 1] = minVal + 1;
  } else {
     counter[x - 1]++;
  }

跟踪结果数组的每个元素的minVal。

这是完整的解决方案:

public int[] solution(int N, int[] A) {
    int minVal = -1;
    int maxCount = -1;

    int[] counter = new int[N];

    for (int i = 0; i < A.length; i++) {
        int x = A[i];

        if (x > 0 && x <= N) {
            if (counter[x - 1] < minVal) {
                counter[x - 1] = minVal + 1;

            } else {
                counter[x - 1]++;
            }

            if (maxCount < counter[x - 1]) {
                maxCount = counter[x - 1];
            }
        }

        if (x == N + 1 && maxCount > 0) {
            minVal = maxCount;
        }
    }

    for (int i = 0; i < counter.length; i++) {
        if (counter[i] < minVal) {
            counter[i] = minVal;
        }
    }

    return counter;
}

答案 6 :(得分:0)

This is my swift 3 solution(100/100)

function rgb_array($color, $include_alpha = true) {
    return array_slice(preg_split('~[^\d.]+~', $color, -1, PREG_SPLIT_NO_EMPTY), 0, $include_alpha + 3);
}