文章目录
分治法的主要思想:
将一个大问题不停分割为k个子问题,如果子问题的规模仍然很大,就继续分割,直到子问题的规模易于求解为止;完成子问题的求解后,自底向上,将子问题合并为原问题。(有动态规划的味道,但是很明显的区别就在于,分治法无法避免大量冗余的重复计算
分治法的应用场景,特征:
分治法的特征:
- 该问题可以被分割为一定规模后易于求解,并且可以分割为若干子问题;(分治法的基础)
- 子问题之间相互独立;
- 子问题解决后,合并起来可以得到原问题的解;
(对比动态规划,可以发现,二者十分相似,但是关键点又有不同)
分治法的解题步骤:
1、分解:原问题分解为若干易于解决的子问题,注意:子问题应该与原问题属于同一类问题;
2、解决:利用递归,解决子问题,当子问题规模足够小,直接一步得到答案;
3、合并:子问题合并为原问题;
分治法的主要例题:
不得不提的快速排序:
主要思想:
快速排序是分治思想贯彻最彻底的排序方法,每次选择一个支点,一轮排序后,该支点在整体数据的第几位就确定下来,十分有效的排序方法。
伪代码如下:
quick_sort( int * A , int p , int r )
{
if ( p == r )
return;
//起始位置与终止位置相等,则返回;
else
int mid = partition ( A , p , r );
//取得支点所在位置;
quick_sort ( A , p , mid );
quick_sort ( A , mid + 1 , r );
//分治求解左右部分
}
int partition ( int *A , int p , int r ) {
int x = A[p];//支点
int i = p;//i记录下标
for ( int j = p+1 ; j < r ; j++ ){
if ( A[j] < x ) {
i++ ;
int temp = A[j];
A[j] = A[i];
A[i] = temp;
}
}
int temp1 = A[i];
A[i] = A[p];
A[p] = temp;
return i;
}
时间复杂度分析:
每次找到的支点,都是这一列数字中的中间元素,则
最优情况为O ( nlogn)
每次找到的支点,要么时最大值,要么是最小值,则
最坏情况:O(n^2)
经过一系列证明,平均复杂度还是O (nlogn);
过程演示:
一个partition的演示图
找到第K小元素:
主要思想:
基于快速排序的partition,可以轻松得到当前支点是数列中的第几个元素,用k与其进行比较,可以得到第k小元素。
伪代码:
kth_sort( int *A , int p , int r , int k ) {
if ( p == r )
return -1;//未找到
else{
int mid = partition ( A , p , r );
//partition在快排中
int i = mid - p + 1;// i 是A[i]的排名;
if ( k == i )
return A[i];
else if ( k < i ) {
return kth_sort ( A , p , mid-1 , k );
}
else if ( k > i ) {
return kth_sort ( A , mid+1 , r , k-i );
}
}
}
时间复杂度
与快速排序保持一致;
中间的中间取得所有元素的中间大小元素:
详细的我也没有过深研究,大致的意思就是,将元素分割为5组,(可能有剩余成员,但是无伤大雅),每一组取其中间值,如下图;
然后在取出的5个中间元素中,再取出其中间值,这样就得到了整个数据的中间值:
很巧妙的一个方法,具体的代码我也没实现过,CSDN上有相应的代码,此处就不附上链接了。
总结:
分治算法是基础算法,其主要思想在后面的学习中会经常用到,需要我们重点掌握。还有很多例子还没列举出来,但是都可以理解为换汤不换药,都是分治思想的应用,熟悉掌握快速排序的思想,分治算法就解决了很大一部分了。