【问题标题】:Need help coming up with elegant fix for flaw in my algorithm需要帮助为我的算法中的缺陷提出优雅的修复
【发布时间】:2015-04-20 01:47:17
【问题描述】:

我正在练习与一家大型在线零售商的面试,现在正试图为“两个排序数组的中位数”问题提出一个优雅的解决方案。我知道youtube 上提出的解决方案,但我正在尝试编写一个替代方案。我已经写出来了

include <stdio.h>

int M2SA ( int * A, int m, int * B, int n )
{
   /* Returns the median of two sorted arrays A and B of lengths m and n respectively. 
      Assumes A and B aren't both empty. 
   */ 
   int ida = 0, idb = 0, idmed = (m+n)/2;
   while ((ida + idb) != idmed)
   {
     if (A[ida] < B[idb])
        ++ida;
     else
        ++idb;
   }
   return (A[ida] < A[idb] ? A[ida] : B[idb]);
} 



int main()
{
    int arr1 [] = {1, 1, 59, 69};
    int arr2 [] = {-4, 0, 49, 59, 59, 59};
    printf("%d", M2SA(arr1, sizeof(arr1)/sizeof(int), arr2, sizeof(arr2)/sizeof(int)));
    return 0;
}

打印出正确答案 (59),但我意识到存在一个缺陷,即我的算法仅在 idmed 不大于 mn 时才有效。例如,如果数组是{1}{69, 293, 393, 1923129},则idawhile 循环的第一次迭代之后等于1,因此while 循环的第二次迭代尝试访问A[1] .但是,我想不出一个简单的修复方法来添加到我的代码中。有没有简单的?

【问题讨论】:

  • 中位数的一个也被广泛使用的定义(对于偶数长度系列)是将中位数周围的两个相邻数字的算术平均值作为结果。 median [1;2;3;4] = (2+3)/2 = 2.5
  • 您实际上是在采用merge sort 方法来解决问题,即 O(n)。因此,您必须像处理合并排序一样处理列表结尾条件。

标签: c++ c algorithm


【解决方案1】:

这是对您的解决方案的优雅修复:

int ida = 0, idb = 0, idmed = (m + n) / 2;
while (ida < m && idb < n && ida + idb < idmed) {
    A[ida] < B[idb] ? ++ida : ++idb;
}
if (ida == m) return B[idmed - m];
if (idb == n) return A[idmed - n];
return A[ida] < B[idb] ? A[ida] : B[idb];

【讨论】:

    【解决方案2】:

    基本上有3种情况需要考虑:

    1. 两个阵列都没有被完全消耗。
    2. 第一个数组已被消耗,中位数在第二个数组中。
    3. 第二个数组已被消耗,中位数在第一个数组中。

    实施必须了解这 3 种情况。 触发 3 种情况中的哪一种取决于输入数据。

    例如,如果所有较小的值都在数组 1 中,而较大的值在数组 2 中,并且数组 1 的长度小于数组 2 的长度,则数组 1 中的索引为数组 1 的长度。所以访问它不是一个好主意。

    这里是使用 for 循环而不是 while 的实现。我更喜欢这里的 for 循环,因为更容易看到循环最终会终止。

    main() 函数包含一组测试用例,它们会触发上述所有三个代码路径。

    int 
    m2a
    ( _In_reads_(s1) const int * a1
    , int s1
    , _In_reads_(s2) const int *a2
    , int s2
    )
    {
        int mi = (s1 + s2) / 2;
        int ai1 = 0;
        int ai2 = 0;
        for (int i = 0; i < mi;i++)
        {
            if (ai1 < s1 && ai2 < s2)
            {
                if (a1[ai1] < a2[ai2])
                {
                    ai1++;
                }
                else
                {
                    ai2++;
                }
            }
            else
            {
                if (ai1 < s1)
                {
                    ai1++;
                }
                if (ai2 < s2)
                {
                    ai2++;
                }
            }
        }
        int result = 0;
        if (ai1 < s1 && ai2 < s2)
        {
            result = a1[ai1] < a2[ai2] ? a1[ai1] : a2[ai2];
        }
        else
        {
            if (ai1 < s1)
            {
                result = a1[ai1];
            }
            if (ai2 < s2)
            {
                result = a2[ai2];
            }
        }
        return result;
    }
    
    int _tmain(int argc, _TCHAR* argv[])
    {
        {
            const int s1 = 4;
            const int s2 = 4;
            int a1[s1] = { 1, 2, 3, 4 };
            int a2[s2] = { 1, 2, 3, 4 };
            int m = m2a(a1, s1, a2, s2);
            printf("%d\n", m);
        }
        {
            const int s1 = 5;
            const int s2 = 4;
            int a1[s1] = { 5,6,7,8,9 };
            int a2[s2] = { 1, 2, 3, 4 };
            int m = m2a(a1, s1, a2, s2);
            printf("%d\n", m);
        }
        {
            const int s1 = 4;
            const int s2 = 5;
            int a1[s1] = { 1, 2, 3, 4 }; 
            int a2[s2] = { 5, 6, 7, 8, 9 };
            int m = m2a(a1, s1, a2, s2);
            printf("%d\n", m);
        }
        {
            const int s1 = 1;
            const int s2 = 5;
            int a1[s1] = { 99 };
            int a2[s2] = { 5, 6, 7, 8, 9 };
            int m = m2a(a1, s1, a2, s2);
            printf("%d\n", m);
        }
        {
            const int s1 = 5;
            const int s2 = 1;
            int a1[s1] = { 5, 6, 7, 8, 9 };
            int a2[s2] = { 99 }; 
            int m = m2a(a1, s1, a2, s2);
            printf("%d\n", m);
        }
    
        {
            const int s1 = 5;
            const int s2 = 5;
            int a1[s1] = { 1,1,1,1,1 };
            int a2[s2] = { 1,1,1,1,1 };
            int m = m2a(a1, s1, a2, s2);
            printf("%d\n", m);
        }
    
        {
            const int s1 = 5;
            const int s2 = 1;
            int a1[s1] = { 5, 6, 7, 8, 9 };
            int a2[s2] = { 3 };
            int m = m2a(a1, s1, a2, s2);
            printf("%d\n", m);
        }
        return 0;
    }
    

    修复了剩余的错误。脑残胜过“优雅”。如果您有 3 个案例,尝试隐藏它是危险的。因此,循环的主体也显示了 3 个案例...

    【讨论】:

    • _In_reads(s1) 符号从何而来?这并不常见。前面的逗号真的让我很烦(我讨厌它们),但这在某种程度上是风格上的,所以每个人都有自己的。
    • _In_reads(count) 来自sal.h。它帮助静态代码检查器查找数组错误,并且通常以工具可访问的方式注释参数和函数。我更喜欢前导逗号而不是尾随逗号,因为我可以简单地将 1 条评论放在一行中并完成。尾随逗号有点麻烦。
    • 在最后一个测试用例中,将99替换为3,你会发现你有一个数组bug。
    【解决方案3】:

    也许你可以改变:

    if (A[ida] < B[idb])
    

    收件人:

    if (ida < m && A[ida] < B[idb])
    

    还有:

    return (A[ida] < A[idb] ? A[ida] : B[idb]);
    

    收件人:

    return (ida < m && A[ida] < A[idb] ? A[ida] : B[idb]);
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2017-02-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多