【问题标题】:Which corner case unit test would this fail?哪个极端案例单元测试会失败?
【发布时间】:2016-10-21 08:33:28
【问题描述】:

我尝试了Fish problem on Codility,并获得了 75% 的正确率,因为结果报告说我的代码未能通过一个简单的测试用例。结果不报告为测试用例提供了什么输入。

您能帮我找出我的代码有什么问题以及它会失败的极端情况吗?

using System;

public class Solution
{
    // Time complexity: O(N)
    // Space complexity: O(N)
    public int solution(int[] sizes, int[] direction)
    {
        if (sizes == null || direction == null)
            throw new ArgumentNullException();

        var sizesLen = sizes.Length;
        var directionLen = direction.Length;

        if (sizesLen != direction.Length)
            throw new ArgumentException();

        var len = sizesLen;

        if (len <= 1) return len;

        var survivors = new Fish[len];
        survivors[0] = new Fish(sizes[0], direction[0]);
        var curr = 0;

        for (int i = 1; i < len; i++)
        {
            var fish = new Fish(sizes[i], direction[i]);

            if (survivors[curr].Direction == 1 && fish.Direction == 0)
            {
                if (fish.Size < survivors[curr].Size) continue;

                while(curr >= 0 && 
                    fish.Size > survivors[curr].Size && 
                    survivors[curr].Direction == 1)
                {
                    curr--;
                }
            }

            survivors[++curr] = fish;
        }

        return ++curr;
    }
}

public class Fish
{
    public Fish(int size, int direction)
    {
        Size = size;
        Direction = direction;
    }

    public int Size { get; set; }
    public int Direction { get; set; }
}

【问题讨论】:

  • 是超时错误还是错误回答错误??
  • 假设 A = [ 99, 98, 92, 91, 93 ],并且 B = [1, 1, 1, 1, 0]。您的代码给出的答案为 3。预期答案 = 2

标签: c# algorithm data-structures computer-science


【解决方案1】:

如您的代码中所述,您的解决方案是O(M*N)。如问题链接中所述,代码应以线性时间运行。因此,我不会更正您的解决方案,因为它最终会在更大的测试用例中失败。我将为您提供一个您可以轻松实现的线性算法。

保留一个堆栈S,最初为空。

从0到n-1遍历数组A、i

当你遇到一个元素时,比如A[i],执行以下操作

  • 如果堆栈 S 为空,则将两个 (A[i], B[i]) 作为一对推送
  • 否则,从堆栈中提取顶部对 S 并比较 B[top] 和 B[i] 的值。

    而 B[top] 是 1 和 B[i] 是 0,那么其中一条鱼会吃掉另一条鱼。所以从栈 S 中弹出,栈顶元素。现在,用值A[top] 和A[i] 比较哪条鱼更大。无论哪个更大,那条鱼都活着。将那对推入堆栈S,对应于活着的鱼。继续 while 循环直到条件失败 如果B[top] 不是1 并且B[i] 不是0,那么只需推送新的对(A[i],B[i])

最后的堆栈大小S,就是你的答案。

注意:您可能没有通过该测试用例,因此您的解决方案会超时。例如,对于 N=100000,您的解决方案将超时。

在我的解决方案中,最坏情况的时间复杂度是O(N+N) = O(2N) = O(N)。 N 次,因为迭代数组 A 和另一个 N 次最坏情况,由于堆栈如果它不断缩小,对于 while 条件成立 true。

希望对你有帮助!!!

编辑:假设 A = [ 99, 98, 92, 91, 93 ],B = [1, 1, 1, 1, 0]。您的代码给出的答案为 3。预期答案 = 2

Edit-2:这是您修改后的代码,将通过每个测试用例

public int solution(int[] sizes, int[] direction)
    {
        if (sizes == null || direction == null)
            throw new ArgumentNullException();

        var sizesLen = sizes.Length;
        var directionLen = direction.Length;

        if (sizesLen != direction.Length)
            throw new ArgumentException();

        var len = sizesLen;

        if (len <= 1) return len;

        var survivors = new Fish[len];
        survivors[0] = new Fish(sizes[0], direction[0]);
        var curr = 0;

        for (int i = 1; i < len; i++)
        {
            var fish = new Fish(sizes[i], direction[i]);

            if (survivors[curr].Direction == 1 && fish.Direction == 0)
            {
                if (fish.Size < survivors[curr].Size) continue;

                while(curr >= 0 && 
                    fish.Size > survivors[curr].Size && 
                    survivors[curr].Direction == 1)
                {
                    curr--;
                }

                if (curr >= 0)
                {
                    if (fish.Size < survivors[curr].Size && 
                         survivors[curr].Direction == 1) 
                               continue;
                }
            }

            survivors[++curr] = fish;
        }

        return ++curr;
    }

}

public class Fish
{
    public Fish(int size, int direction)
    {
        Size = size;
        Direction = direction;
    }

    public int Size { get; set; }
    public int Direction { get; set; }
}

【讨论】:

  • @Water Cooler v2:请查看您修改后的代码。现在可以用了吗?
  • 非常感谢您不厌其烦。虽然我的算法与您所描述的并没有太大的不同,但您的研究,更具体地说是您对我的代码的修改版本,解决了这个问题。我并没有检查现在上游的鱼是否会被下游的那些早期幸存者吃掉。我不太担心性能,而是担心正确性。再次感谢您的辛勤研究和非常有帮助的回复。我非常感激。
  • 我进一步编辑了我修改后的代码版本以包含if 条件,这样if (curr &gt;= 0) { if (fish.Size &lt; survivors[curr].Size &amp;&amp; survivors[curr].Direction == 1) continue; } 因为如果survivors 堆栈已被清空,则该语句将产生ArrayIndexOutOfBounds 异常.例如,尝试使用示例输入 A = new [] { 91, 92 } 和 B = new [] { 1, 0 } 运行不带 if 语句的代码。
  • 欢迎您...实际上您的代码也是O(N),与您提到它为O(N*M) 的事实相反。快乐编码!!!
  • 感谢您对此的更正。我已使用此信息编辑了原始问题中代码中的注释。
【解决方案2】:

我认为这里的意图是使用堆栈或队列。这是一个有两个堆栈的解决方案。

  public static int Fish(int[] A, int[] B)
        {
            var downStreamFish = new Stack<int>(B.Length);
            var upStreamFish = new Stack<int>(B.Length);
            var result = B.Length;
            for (var i = 0; i < B.Length; i++)
            {
                // push the fish into up/down stream stack.
                if (B[i] == 1)
                    downStreamFish.Push(i);
                else
                    upStreamFish.Push(i);

                // check to see whether it's possible to eat a fish
                while (downStreamFish.Count > 0 && upStreamFish.Count > 0)
                {
                    var dfIndex = downStreamFish.Peek();
                    var ufIndex = upStreamFish.Peek();
                    //NOTE:downstream fish index must be less than upstream fish index in order for 'eat' to happen
                    if (dfIndex < ufIndex) 
                    {
                        if (A[dfIndex] > A[ufIndex])
                            upStreamFish.Pop();
                        else
                            downStreamFish.Pop();

                        result--; // one fish is eatten
                    }
                    else
                        break; // eat condition is not met 
                }
            }
            return result;
        }

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-02-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-08-16
    • 1970-01-01
    相关资源
    最近更新 更多