求无序数组的中位数(c语言版本)

在面试时,会经常被问道,如何求解一个无序数组的中位数?很多人往往都会第一感觉就是,先将该数组排序,然后找出最中间的那个数,但是这种思路通常的时间复杂度最好是O(nlogn),更糟的情况下会到O(n^2),并不是最优解,也就不能impressed面试官了。下面我们聊聊这个话题。

何为中位数?

中位数,就是数组排序后位于数组最中间位置的那个元素。当然,细分析的话,还要区分该数组的长度,如果该数组长度为n,若n为奇数,则中位数就是(n+1)/2位置上的数;若n为偶数,则中位数是n/2和n/2+1位置上的两个数的平均值,这里我们为了简单起见,就用n/2位置上的数代替吧。

现在我们考虑一个更一般的问题:

如何求解一个无序数组的第k小的数?

如果能求解出第k小的数,那么就能求出第k大的数(它对应第(n-k)+1小的数),特别地,如果k为n/2或(n+1)/2的话,这就是我们要求的中位数。

关于这个问题,我发现在Allen Weiss的《数据结构与算法分析》一书中一直贯穿始终,特别是在第七章排序中,给出了时间复杂度为O(n)的解决方法,该解决方法,也就是一个"二分法"思想的快速排序思想的变体,但是比快速排序的实现更快更简单,因为它只需要对牵涉范围的那些子数组排序。

下面是我的源码实现,以作备忘。

//description: 查找无序数组中指定的第k小的数
//date: 2019-03-21

#include <stdio.h>
#include <stdlib.h>

void swap(int arr[], int a, int b){
    int tmp = arr[a];
    arr[a] = arr[b];
    arr[b] = tmp;
}

void quick_select(int arr[], int k, int s, int e){
    if(s > e || k < s || k > e){
        printf("invalid array range\n");
        return;
    }

    //这里随机将左边第一个元素设置为基准点
    int i, j, pivot = arr[s];
    if(s <= e){
        i = s;
        j = e;
        for(;;){
            while(arr[j] >= pivot && i<j){j--;}
            while(arr[i] <= pivot && i<j){i++;}
            if(i<j)
                swap(arr, i, j);
            else
                break;
        }
        //恢复pivot
        swap(arr, i, s);

        //继续进行下一轮迭代
        if(k<=i)
            quick_select(arr, k, s, i-1);
        else if(k>=i+1)
            quick_select(arr, k, i+1, e);
    }
}

//数组复制
void copy_array(int arr[], const int barr[], int n){
    int i = 0;
    for(i=0; i<n; i++)
        arr[i] = barr[i];
}

int main(int argc, char** argv){
    int a[] = {12,4,3,67,900,22,28,9,61,53,23,12};
    int n = sizeof(a) / sizeof(int);
    int k = 4;

    int b[n];
    copy_array(b, a, n);
    int m = n%2==0 ? n/2 : (n+1)/2;

    //求第k小的数
    quick_select(a, k, 0, n-1);
    printf("the %d-th smallest number: %d\n", k, a[k-1]);

    for(int i=0; i<n; i++)
        printf("%d ", a[i]);
    printf("\n");

    //求解中位数
    quick_select(b, m, 0, n-1);
    printf("the median nuber(%d-th): %d\n", m, b[m-1]);

    for(int i=0; i<n; i++)
        printf("%d ", b[i]);
    printf("\n");

    return 0;
}

这里给出了求解第4小和第6小的数的排序,因为数组长度为12,我就把6当做是该数组的中位数了。

运行截图如下:

 

### 计算数组的平均值 为了计算一维数组的平均值,在不知道具体元素数量的情况下,可以动态读取用户输入直到遇到特定终止符(如负数或特殊字符),从而确定数组的实际长度。下面展示了一个简单的例子: ```c #include <stdio.h> int main() { int num; double sum = 0, average; int count = 0; printf("请输入整数序列(以非数字结束):\n"); while(scanf("%d", &num) != EOF){ sum += num; ++count; } if(count > 0){ average = sum / count; printf("您输入了%d个有效数值。\n", count); printf("这些数的总和为%f\n", sum); printf("它们的平均值为%.2f\n", average); }else{ printf("未接收到任何有效的整数数据。\n"); } return 0; } ``` 这段代码通过`scanf()`函数不断接收用户的输入,并累加至变量`sum`中,同时记录下实际参与运算的有效数字的数量`count`[^1]。 ### 寻找数组中位数 对于已知大小的一维有序数组来说,可以直接根据其奇偶性决定中位数的位置;而对于无序数组,则需先对其进行排序处理后再按上述方法操作。这里给出一个针对任意顺序的一维数组解其中位数的例子: ```c #include <stdio.h> #include <stdlib.h> // qsort() // 定义比较器用于qsort() static int compare(const void *a, const void *b){ return (*(int*)a - *(int*)b); } double findMedianSortedArrays(int* nums, int size){ if(size % 2 == 1){ // 如果是奇数个元素 return nums[size/2]; } else { // 否则就是偶数个元素 return (nums[(size-1)/2]+nums[size/2])/2.; } } int main(){ int numbers[] = {-5,-4,3,8}; // 测试用例 int length = sizeof(numbers)/sizeof(*numbers); qsort(numbers,length,sizeof(int),compare); // 对数组进行升序排列 printf("The median is %.2lf.\n",findMedianSortedArrays(numbers,length)); return 0; } ``` 此段程序首先定义了一个辅助性的比较函数供内置库函数`qsort()`调用来完成对原始数组快速排序工作。之后利用自定义的功能函数`findMedianSortedArrays()`依据传入参数判断当前数组内含有的元素数目是否为单双来分别采取不同的策略获取最终的结果[^3]。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值