【问题标题】:Finding median without sorting an array查找中位数而不对数组进行排序
【发布时间】:2019-04-20 01:35:58
【问题描述】:

我希望实现一个非常简单的函数,它通过计算较小元素的数量和较大元素的数量来找到未排序数组的中值,如果它们的数量相等,那么原始元素被视为中值。

我知道一些算法,比如 minHeap 和 Quick Select,但我试图让事情变得简单,就像人类用肉眼简单地计算越来越大的数字一样。到目前为止,我已经实现了下面的函数,但是当我在数组中有重复的条目并且数组长度为偶数和奇数时会出现问题。

我是 C 编程新手,需要了解问题所在。下面是代码,我写了一个函数来返回可变长度的随机数组来测试这个函数。

int med(int count, int *array)
{
int i, j, median = -1, smaller = 0, larger = 0;

for(i = 0; i < count; i++)
{
    for(j = 0; j < count; j++)
    {
        //larger++

        if(array[i] < array[j] && i!=j)
        {
            larger++;
        }
        //Smaller++
        if(array[i] >= array[j] && i!=j)
        {
            smaller++;
        }
    }
    printf("\nFor pivot: %d", array[i]);
    if(larger == smaller)
    {
        printf("\n Smaller: %d", smaller);
        printf(" Larger: %d", larger);
        median = array[i];
        break;
    }
    else
    {
        printf("\n Smaller: %d", smaller);
        printf(" Larger: %d", larger);

        larger = 0;
        smaller = 0;
    }
}
return median;
}

在某些情况下,例如 {3,5,0,2,3},我的函数返回 -1,但实际结果应该是 3。

编辑 最初我从严格的更大或更小开始,但是当我有重复的条目时,这个条件(更大==更小)永远不会受到影响,因此我认为相等的元素更小。我在处理平等方面有困难

【问题讨论】:

  • 有什么理由要避免对其进行排序吗?
  • 它开始看起来像XY-problem 但你让我很好奇。这种新的排序算法的目的是什么?
  • 肯定不是xy问题!我确实知道有多种方法可以做到这一点,因为我提到了其中两种方法,而您在答案中给出了一种方法。我只是从人类如何在给定 4,5 个数字的情况下找到中位数(如果较小的数量等于较大的数量,则为每个元素计数)来思考它,你就拥有了!新算法也是我想出来的,在网上找不到,为什么不试试呢!
  • 但是使用 O(n²) 的中值函数创建排序算法的目的是什么?如果你很好地实现排序,那么排序算法将是 O(n²log n),但这甚至比冒泡排序和选择排序等最简单的排序还要糟糕。如果你使用简单的排序实现,它将是 O(n³)
  • 我明白这一点,而且我总是可以证明找到中位数的方法。但首先,这是我的方法,我被困住了。有很多解决方法,但为什么要选择一种而不试图克服这个问题?

标签: c


【解决方案1】:

B. Shefter 为您找到了错误。但是,我仍然想解决这个问题。

我希望实现一个非常简单的函数,它通过计算较小元素的数量和较大元素的数量来找到未排序数组的中值,如果它们的数量相等,则原始元素被视为中值。

只有这样做,如果你能比 O(nlog n) 更快,因为那是qsort 的时间复杂度。我建议尝试中位数算法的中位数。你可以阅读它here,这里是该站点的代码,但删除了 cmets:

int select(int *a, int s, int e, int k){
    if(e-s+1 <= 5){
        sort(a+s, a+e);
        return s+k-1;
    }
    
    for(int i=0; i<(e+1)/5; i++){
        int left = 5*i;
        int right = left + 4;
        if(right > e) right = e;
        int median = select(a, 5*i, 5*i+4, 3);
        swap(a[median], a[i]);
    }
    
    return select(a, 0, (e+1)/5, (e+1)/10);
}

我知道一些算法,例如使用 minHeap 和 Quick Select,但我试图让事情变得简单,就像人类用肉眼简单地计算越来越小的数字一样。

虽然保持简单是一件好事,但请确保这就是您的工作。 C 标准库有一个内置的快速排序。如果你使用那个,代码可能是这样的:

int int_cmp(const void *a, const void *b) 
{ 
    const int ia = *(const int *)a; 
    const int ib = *(const int *)b;

    return ia-ib;
}

int med(int count, int *array)
{
    int tmp[count]; // You might want to use malloc instead

    memcpy(tmp, array, count * sizeof(*array));

    qsort(tmp, count, sizeof(tmp[0]), int_cmp);

    return tmp[count/2];
}

它更快更容易阅读。你的代码是 O(n²) 而这是 O(nlog n)。

您在评论中提到要将其用于新的排序方法。然后我想提一下,具有奇数个元素的集合的中位数通常不是该集合的成员,因此您需要更改中位数的定义以满足您的需求。

这是一个示例,说明如何以一种非常易读的方式实现您想要的,同时仍然保持您的想法。我首先添加一个子问题,而不是“数组中的中位数是多少”是“数组的中位数是 x”。然后我们对数组中的每个元素都问这个问题,直到找到中位数。

int is_median(int x, int *array, int count) {
    int l=0, h=0;

    for(int i=0; i<count; i++) {
        if(array[i] < x) l++;
        else if(array[i] > x) h++;
    }
    
    if(h == l) return 1; // This is always a sufficient condition

    // Here you need to decide what to do. Just the above is not enough
    // for your purposes.
    else if(<condition>) return 1; 

    else return 0;
}

int med(int count, int *array) {
    for(int i = 0; i < count; i++) {
        if(is_median(array[i], array, count)) return array[i];
    }
    return 0; // This line should never be executed. It't only here
              // to suppress a warning.
}
    

【讨论】:

    【解决方案2】:

    -1 来自以下内容:您的代码将median 初始化为-1,除非larger == smaller,否则它永远不会改变。如果在遍历整个数组后从未发生过这种情况,代码将返回 -1。

    我认为概念上的错误是您在两个数字相等时任意决定增加smaller。如果您浏览您的代码,您会明白为什么在您展示的示例中得到 -1:您最终会得到 larger=1(5)和 smaller=3(0、2 和 3)。因此,由于larger 不等于smallermedian 不设置为 3 并保持为 -1。

    这就是问题所在。如何处理等式以修复概念性错误取决于您!

    【讨论】:

    • 是的,我明白为什么会出现-1。我故意保留 = 是因为我想将它们视为较小的数字,这就是问题所在,我不能随意选择增加较小的值,因为在某些情况下它不起作用,如上所示。此外,如果我选择删除它并考虑 {4,1,0,1,4} 案例中位数应该为 1 但条件永远不会达到并且中位数永远不会更新。这就是我被卡住的地方“如何处理这个?”
    • @CCPP 不计算等于枢轴的数组元素。 if (abs(larger - smaller) &lt;= 1) 那么你找到了中位数。
    • @user3386109 所以你的意思是保持条件严格更大和严格更小加上这个 abs(larger - small)
    • @CCPP 是的,我相信是的。如果数组中的元素个数(不包括枢轴)是奇数,则largersmaller 将相差 1。
    • @user3386109 用 {1,5,1,5,1} 试了不行!
    猜你喜欢
    • 2016-03-02
    • 2018-06-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-08-21
    • 1970-01-01
    • 2013-12-19
    • 1970-01-01
    相关资源
    最近更新 更多