分治--215. 数组中的第K个最大元素/medium 理解度C

本文介绍如何使用快速选择算法在O(n)时间复杂度内找出整数数组中第k个最大的元素,通过分治策略和基准元素划分,适用于数据分析、算法设计等多种IT应用场景。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1、题目

给定整数数组 nums 和整数 k,请返回数组中第 k 个最大的元素。

请注意,你需要找的是数组排序后的第 k 个最大的元素,而不是第 k 个不同的元素。

你必须设计并实现时间复杂度为 O(n) 的算法解决此问题。

 

示例 1:

输入: [3,2,1,5,6,4], k = 2
输出: 5

示例 2:

输入: [3,2,3,1,2,4,5,5,6], k = 4
输出: 4

 

提示:

  • 1 <= k <= nums.length <= 105
  • -104 <= nums[i] <= 104
Related Topics
  • 数组
  • 分治
  • 快速选择
  • 排序
  • 堆(优先队列)

2、题目分析

借助快排的分治思想,找到区间支点,再判断支点所在位置是否为第k大的位置

3、复杂度最优解代码示例

    public int findKthLargest(int[] nums, int k) {
        // 使用快速选择算法,类似于快速排序的思想

        // 调整k为索引,因为找的是第K个最大元素,而索引是从0开始的
        k = nums.length - k;

        int left = 0;
        int right = nums.length - 1;

        while (left < right) {
            // 划分数组,获取划分点的索引
            int pivotIndex = partition(nums, left, right);

            // 判断划分点的索引是否等于k,如果是则找到第K个最大元素
            if (pivotIndex == k) {
                return nums[pivotIndex];
            } else if (pivotIndex < k) {
                // 如果划分点的索引小于k,则说明第K个最大元素在右边,缩小右边界
                left = pivotIndex + 1;
            } else {
                // 如果划分点的索引大于k,则说明第K个最大元素在左边,缩小左边界
                right = pivotIndex - 1;
            }
        }

        // 如果left等于right,或者while循环结束后left大于right,则说明已经找到第K个最大元素
        return nums[left];
    }

    private int partition(int[] nums, int low, int high) {
        // 选择最左边的元素作为基准值
        int pivot = nums[low];

        // 遍历数组,将小于基准值的元素放到左边,大于基准值的元素放到右边
        while (low < high) {
            // 在右侧找到第一个小于基准值的元素
            while (low < high && nums[high] >= pivot) {
                high--;
            }
            // 将该元素放到左侧
            nums[low] = nums[high];

            // 在左侧找到第一个大于基准值的元素
            while (low < high && nums[low] <= pivot) {
                low++;
            }
            // 将该元素放到右侧
            nums[high] = nums[low];
        }

        // 将基准值放到正确的位置上
        nums[low] = pivot;

        return low; // 返回基准值的索引
    }

4、适用场景

  1. 数据分析:在处理大量数据时,找出第K个最大元素可以帮助我们了解数据的分布情况,例如在销售数据中找出销售额排名第K的商品。

  2. 算法设计:在设计一些需要排序的算法时,如快速排序、堆排序等,找出第K个最大元素可以作为算法的一部分。

  3. 游戏开发:在一些游戏中,可能需要找出得分排名第K的玩家,这时就需要找出数组中的第K个最大元素。

  4. 网络编程:在网络编程中,可能需要找出传输速度排名第K的节点,这时也需要找出数组中的第K个最大元素。

  5. 机器学习:在机器学习中,可能需要找出预测结果排名第K的模型,这时也需要找出数组中的第K个最大元素。

  6. 数据库查询:在数据库查询中,可能需要找出某个字段值排名第K的记录,这时也需要找出数组中的第K个最大元素。

### 分治算法实现找到数组中第K小元素 分治法的核心思想是将一个问题分解成若干个规模较小的子问题,这些子问题是原问题的简化版本。通过递归解决这些子问题并将它们的结果合并来得到最终解。对于寻找数组中的第 \( k \) 小元素这一问题,可以通过类似于快速排序的分区操作完成。 以下是基于 C 语言的一个具体实现: #### 使用分治法查找第 \( k \) 小元素的代码示例 ```c #include <stdio.h> // 定义交换函数 void swap(int* a, int* b) { int t = *a; *a = *b; *b = t; } // 划分函数 (Partition),返回枢纽元的位置 int partition(int arr[], int low, int high) { int pivot = arr[high]; // 假设最后一个元素作为枢轴 int i = low - 1; // 较小元素索引初始化 for (int j = low; j <= high - 1; j++) { if (arr[j] < pivot) { // 如果当前元素小于枢轴 i++; // 更新较小元素索引 swap(&arr[i], &arr[j]); // 交换位置 } } swap(&arr[i + 1], &arr[high]); // 把枢轴放到正确位置上 return i + 1; // 返回枢轴所在位置 } // 查找第 k 小元素的辅助函数 int findKthSmallestUtil(int arr[], int low, int high, int k) { if (low == high) { // 只有一个元素的情况 return arr[low]; } int pivotIndex = partition(arr, low, high); // 获取划分后的枢轴位置 int numElementsLeft = pivotIndex - low + 1; if (numElementsLeft == k) { // 枢轴正好是我们要找的第 k 小元素 return arr[pivotIndex]; } else if (k < numElementsLeft) { // 要找的元素在左半部分 return findKthSmallestUtil(arr, low, pivotIndex - 1, k); } else { // 要找的元素在右半部分 return findKthSmallestUtil(arr, pivotIndex + 1, high, k - numElementsLeft); } } // 主调用接口 int findKthSmallest(int arr[], int n, int k) { return findKthSmallestUtil(arr, 0, n - 1, k); } // 测试程序 int main() { int arr[] = {7, 10, 4, 3, 20, 15}; int n = sizeof(arr) / sizeof(arr[0]); int k = 3; printf("The %d-th smallest element is: %d\n", k, findKthSmallest(arr, n, k)); return 0; } ``` 上述代码实现了利用分治策略查找数组中第 \( k \) 小元素的功能[^1]。它采用了类似快速排序的分区方法,在每次迭代过程中选取一个枢轴,并将其放置于其应处的有序位置。如果该位置恰好对应目标 \( k \),则直接返回;否则继续处理左侧或右侧子区间。 这种方法的时间复杂平均为 \( O(n) \)[^2],最坏情况下可能达到 \( O(n^2) \),但这通常发生在数据分布极端的情况下(例如已经完全逆序)。为了优化性能,可以选择随机化枢轴或者三数取中等方式减少退化的可能性。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值