用C语言实现寻找第k小问题,并分析各种情况下的时间复杂度

文章介绍了一个利用递归和快速排序算法寻找数组中第k小元素的函数实现。在findKthSmallest()函数中,首先验证k的范围,然后调用findKthSmallestRec()进行递归查找。递归函数通过partition()进行数组划分,并根据枢轴元素的位置判断在哪一侧继续搜索。时间复杂度在最优情况下为O(nlogn),平均情况为O(n),最坏情况为O(n^2)。
摘要由CSDN通过智能技术生成

findKthSmallest() 函数是寻找第 k 小值的主函数,它接收待排序的数组,数组的长度以及要寻找的第 k 小的数为输入。

在 findKthSmallest() 函数中,我们首先判断 k 的值是否在 1 和 n 之间,若不在则返回错误信息。否则,调用 findKthSmallestRec() 函数递归寻找数组中第 k 小的数。

在 findKthSmallestRec() 函数中,首先对数组进行划分,并返回枢轴元素的位置,然后比较枢轴元素从左开始到此时的长度是否等于 k,如果是的话,就找到了第 k 小的数。如果枢轴左边的元素的总数小于 k,则在枢轴右边的数组中递归查找第 (k - length) 小的元素。如果枢轴左边的元素的总数大于 k,则在枢轴左边的数组中递归查找第 k 小的元素。

最后,partition() 函数是快速排序中的 partition 操作,将待排序的数组划分为两个部分,返回枢轴的位置。注意到这里的 partition() 函数与快速排序算法中的 partition() 函数实现相同。

// 寻找第 k 小的元素
int findKthSmallest(int arr[], int n, int k) {
    // 如果 k 的值不在 1~n 之间,返回错误信息
    if (k < 1 || k > n) {
        printf("Error: k is out of range.\n");
        return -1;
    }
    // 否则调用递归寻找第 k 小的数
    return findKthSmallestRec(arr, 0, n - 1, k);
}

// 递归划分数组,寻找第 k 小的数
int findKthSmallestRec(int arr[], int left, int right, int k) {
    // 如果数组只有一个元素,那么该元素就是第 k 小的数
    if (left == right) {
        return arr[left];
    }
    // 对 arr[left..right] 进行划分
    int pivotIndex = partition(arr, left, right);
    // 计算枢轴元素左边的子数组长度
    int length = pivotIndex - left + 1;
    // 如果枢轴元素左边的子数组长度等于 k,则该枢轴元素就是第 k 小的数
    if (k == length) {
        return arr[pivotIndex];
    }
    // 如果枢轴元素左边的子数组长度大于 k,则递归在枢轴元素左边的子数组寻找第 k 小的数
    else if (k < length) {
        return findKthSmallestRec(arr, left, pivotIndex - 1, k);
    }
    // 如果枢轴元素左边的子数组长度小于 k,则递归在枢轴元素右边的子数组寻找第 (k - length) 小的数
    else {
        return findKthSmallestRec(arr, pivotIndex + 1, right, k - length);
    }
}

// 划分数组,返回枢轴的位置
int partition(int arr[], int left, int right) {
    // 将 arr[right] 选为枢轴元素
    int pivot = arr[right];
    // 定义下标 i,表示枢轴元素的位置的左边都小于等于 pivot
    int i = left - 1;
    // 遍历 arr[left..right-1]
    for (int j = left; j < right; j++) {
        // 如果 arr[j] 小于等于枢轴元素,则将其放在左侧
        if (arr[j] <= pivot) {
            i++;
            swap(&arr[i], &arr[j]);
        }
    }
    // 将枢轴元素放在划分点位置,将其左边的元素都小于等于枢轴元素,右边的元素都大于等于枢轴元素
    swap(&arr[i + 1], &arr[right]);
    // 返回枢轴元素的位置
    return i + 1;
}

// 交换两个元素的值
void swap(int *a, int *b) {
    int tmp = *a;
    *a = *b;
    *b = tmp;
}

在最优、平均和最坏情况下,寻找第 k 小的数的复杂度方程不同。

  1. 最优情况下,每次划分都得到两个长度相等的子数组,此时时间复杂度为:

    T(n)=2T(n/2)+O(n)

    根据主定理,最优情况下的时间复杂度为 O(nlogn)。

  2. 平均情况下,我们假设每次划分得到的两个子数组的大小相同,此时时间复杂度为:

    T(n)=T(n/2)+O(n)

    根据主定理,平均情况下的时间复杂度为 O(n)。

  3. 最坏情况下,每次划分得到的一个子数组为空,而另一个子数组的长度为 n−1。在最坏情况下,每次都需要遍历整个数组,此时时间复杂度为:

    T(n)=T(n−1)+O(n)

    将递归展开:

    T(n)​=T(n−1)+O(n)=T(n−2)+O(n−1)+O(n)=T(n−3)+O(n−2)+O(n−1)+O(n)⋯

    可以得到最坏情况下的时间复杂度为 O(n^2)。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值