1. 概念介绍与准备知识
最常见的分治法应用有快速排序(选择pivot),堆排序等。以堆排序为例,说明三个步骤:
- 将排序分解为对两个大小大致相同的数组排序
- 递归求解子问题,直至(n=1)
用merge函数将两个已经排序的数组合并
接下来要讲一个重要的分治法复杂度分析定理,大师定理:
2.实例分析
用分治思想分析,分解为两步(没有第三步合并):
- 以一个数v(随机选定)将数组分为三部分,根据不同情况递归求解子问题
直至数在“中间”部分里,这个时候K largest在中间部分
代码(C++):
int findKthLargest(vector<int>& nums, int k) {
return selection(nums,k);
}
//该函数选取中间位置的元素作为分割,将原来的元素分成三部分
int selection(vector<int>& nums,int k) {
vector<int> SL,SR;
int size = nums.size();
int v = nums[(size-1)/2];
int pivot;
for (int i = 0; i < size; i++) {
pivot = nums[i];
if (pivot > v) {
SL.push_back(pivot);
} else if (pivot < v) {
SR.push_back(pivot);
}
}
int Lsize = SL.size();
int Rsize = SR.size();
int Vsize = size-Lsize-Rsize;
if (k <= Lsize) {
return selection(SL,k);
} else if (Lsize < k && k <= Lsize + Vsize) {
return v;
} else {
return selection(SR,k-Lsize-Vsize);
}
}
复杂度分析:算法有三个复杂度,最好最坏和平均,取决于v选择的好坏。
- 最好:第一个选择的数就是第K大的数,这样只需要遍历数组的长度n。
- 最差:例如数组有序,且每次选的都是最小的数,那么每次都要从左边的数组继续执行,复杂度大致为n+(n-1)+(n-2)+…+k = O(n^2)
- 平均:考虑抛硬币问题,得到正面所需要抛硬币的平均次数为2,所以若将选择的v落在25th到75th(百分比)之间看做正面,则有T(n) <= T(3n/4) + O(n),根据大师定理,复杂度为O(n)
3.方法评价与其他应用
分治法的思想为我们求解问题提供了一种指南,但某些时候可能并不是最优的。(例如LeetCode 53,Dynamic Programming方法最优)
应用方面,有一个很重要的应用:快速傅里叶变换,应用于求解多项式乘法。(具体的见算法概论该章)