分治法概述
设计思想
将规模为n的问题分解为k个规模较小的子问题,这些子问题互相独立且与原问题形式相同,递归地解这些子问题,然后将各子问题的解合并得到原问题的解。
分治法所能解决的问题一般具有以下几个特征:
- 该问题的规模缩小到一定的程度就可以容易地解决。
- 该问题可以分解为若干个规模较小的相同问题。
- 利用该问题分解出的子问题的解可以合并为该问题的解。
- 该问题所分解出的各个子问题是相互独立的,即子问题之间不包含公共的子问题。
求解步骤
- 分解:将原问题分解为若干个规模较小,相互独立,与原问题形式相同的子问题。
- 求解子问题:若子问题规模较小而容易被解决则直接求解,否则递归地求解各个子问题。
- 合并:将各个子问题的解合并为原问题的解。
分治法的一般的算法设计框架如下:
divide-and-conquer(P)
{
if |P|≤n0 return adhoc(P);
将P分解为较小的子问题 P1,P2,…,Pk;
for(i=1;i<=k;i++) //循环处理k次
yi=divide-and-conquer(Pi); //递归解决Pi
return merge(y1,y2,…,yk); //合并子问题
}
求解排序问题
快速排序
基本思想:在待排序的n个元素中任取一个元素(通常取第一个元素)作为基准,把该元素放入最终位置后,整个数
据序列被基准分割成两个子序列,所有小于基准的元素放置在前子序列中,所有大于基准的元素放置在后子序列中,
并把基准排在这两个子序列的中间,这个过程称作划分。
然后对两个子序列分别重复上述过程,直至每个子序列内只有一个记录或空为止。
f(a,s,t) ≡ 不做任何事情 当a[s..t]中长度小于2
f(a,s,t) ≡ i=Partition(a,s,t); 其他情况
f(a,s,i-1);
f(a,i+1,t);
分治策略:
- 分解:将原序列a[s…t]分解成两个子序列a[s…i-1]和a[i+1…t],其中i为划分的基准位置。
- 求解子问题:若子序列的长度为0或为1,则它是有序的,直接返回;否则递归地求解各个子问题。
- 合并:由于整个序列存放在数组中a中,排序过程是就地进行的,合并步骤不需要执行任何操作。
快排code:
#include<iostream>
using namespace std;
int partition(int a[],int s,int t){
int i=s,j=t,vot=a[s];
while(i!=j){
while(j>i&&a[j]>=vot){
j--;
}
a[i]=a[j];
while(j>i&&a[i]<=vot){
i++;
}
a[j]=a[i];
}
a[i]=vot;
return i;
}
void quik_sort(int a[],int s,int t){
if(s