查找中位数而不对数组进行排序

时间:2019-04-20 01:35:59

标签: c

我正在寻求实现一个非常简单的函数,该函数通过计算较小元素的数量和较大元素的数量(如果数量相等)来查找未排序数组的中值,然后将原始元素视为中值。

我知道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。

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

2 个答案:

答案 0 :(得分:4)

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

  我正在寻求实现一个非常简单的函数,该函数通过计算较小元素的数量和较大元素的数量(如果数量相等)来查找未排序数组的中值,然后将原始元素视为中值。

如果这样做的速度比O(nlog n)快,则只能这样做,因为那是qsort的时间复杂性。我建议尝试使用中位数算法。您可以here对其进行阅读,这是该站点的代码,但是删除了注释:

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;

    if (ia > ib) return 1;
    else if(ia < ib) return -1;
    else return 0;
}

int med(int count, int *array)
{
    int tmp[count];

    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.
}

答案 1 :(得分:3)

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

我认为概念上的错误是,当两个数字相等时,您已经任意决定递增smaller。如果您遍历代码,那么在显示的示例中将看到为什么得到-1的结果:最终得到larger=1(5)和smaller=3(0、2和3)。因此,由于larger不等于smaller,因此median并未设置为3,而是保持为-1。

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