目录
分治,字面意思是“分而治之”,其基本思想为将一个难以直接解决的大问题,分割成一些规模较小的相同问题,以便各个击破,分而治之。
1.问题描述
在(无序)数组中找到第 k 大 / 小的元素。(本文以第k大元素为例)
输入:数组的大小n和整数k,数组nums
输出:数组中第k大的元素
题目来源:力扣215.数组中的第k个最大元素
2.解题思路
2.1实例分析
最简单粗暴的做法就是直接使用快速排序来解决这个问题,先对原数组进行排序,再返回倒数第k个的位置,快速排序的时间复杂度为O(nlogn),但其实可以通过对其过程中进行优化,是的算法的速度更快。
2.1.1 回顾快速排序
先来回顾一下快速排序,这是一个经典的分治算法。我们对数组a[l, r]做快速排序的过程是:
·分解:先从要排序数组中选出一个数为基准数,将数组中小于基准数的数放在基准数左边,大于基准数的数放在基准数右边。
·解决:对基准数左右两边数组重复划分,直到每个区间只有一个数。
·合并:因为子数组都是原址排序的,所以不需要进行合并操作,数组已经有序。
2.1.2 对快速排序的改进
和快速排序不同的是,当寻找TopK时不需要排序一个数组,只要arr[n - k] 左边的值小于等于arr[n - k],右边的值大于等于arr[n - k],无论其划分出的子数组 是否有序,arr[n - k]就是题目要寻找的TopK。也就是说并不需要对左右两部分子数 组都进行递归,而是只对寻找目标所在的子数组进行递归,这就是快速选择算法。
2.1.3 快速选择算法示例
快速选择算法要点在于划分和选择需要递归的子数组。下面由数组 [3,2,1,5,6,4]对划分进行图解示例(图中有下划线的数字是此次划分的基数)
第一次划分:

找到i和j的位置后交换

到达边界条件后递归,此时j + 1 = 1,r = 5,此时寻找目标在右部分子数组,所以对右部分子数组进行递归。
第二次划分:

找到i和j的位置后进行交换
到达边界条件后递归,此时j + 1 = 4,r = 5,此时寻找目标在右部分子数 组,所以对右部分子数组进行递归。
第三次划分:

到达边界条件后递归,此时l = 4,j = 4,此时寻找目标在左部分子数组, 所以对左部分子数组进行递归,又l = r,所以递归结束,输出结果。
2.2算法的时间复杂度分析
在每一次进行划分选择的时候都会将总数据分为两个部分,其中一部分的数比选择的数小,另一部分比选择的数大。因此,每次选择的数据规模都会减半。所以其期望时 间复杂度为O(n)。如果每次规模为n的问题都划分成1和n - 1,每次集合又向n -1 的 集合中递归,这种情况是最坏的,时间复杂度为O()。
3. 算法的实现
#include<vector>
#include<iostream>
using namespace std;
const int N = 1e5 + 10;
int n, k, nums[N];
int quick_sort(int q[], int l, int r, int k) {
if (l == r) return q[l];
// 取中点作为基点
int i = l - 1, j = r + 1, x = q[l + r >> 1];
while (i < j) {
do i ++; while (q[i] < x);
do j --; while (q[j] > x);
if (i < j) swap(q[i], q[j]);
}
// 接下来是本次优化的核心,我们不对两个子数组都进行排列,
// 而是根据k和边界的关系选择对其中一个子数组进行排列
if (k <= j) return quick_sort(q, l, j, k);
else return quick_sort(q, j + 1, r, k);
}
int main() {
// 对读入和输出进行加速
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
// 将题中所给数据进行读入
cin >> n >> k;
for (int i = 0; i < n; i ++ ) cin >> nums[i];
printf("%d\n", quick_sort(nums, 0, n - 1, n - k));
return 0;
}
文章介绍了如何利用分治策略的快速选择算法,在无序数组中高效地找到第k大的元素,避免了完全排序的过程,从而降低了时间复杂度到平均O(n)。通过对快速排序的改进,只需关注包含第k大元素的那一侧子数组,减少了不必要的递归操作。
&spm=1001.2101.3001.5002&articleId=131922624&d=1&t=3&u=7b73735ad2534be685001cab5b089b33)
1024

被折叠的 条评论
为什么被折叠?



