一、什么是分治法/什么时候用分治法
- 将一个复杂分解为若干规模相同、相互独立、但类型相同的子问题。
- 子问题足够小时可以直接求解。
- 存在将子问题的解组合为原问题的解的途径。
这种解决问题的策略称为分治法。
二、为什么用分治法
由于子问题的类型和原问题的类型相同,可以编写递归程序简单的解决问题。例如:求最大最小值问题、折半查找、归并排序、快速排序、求第k小元等等。
三、分治法解题步骤
SolutionType DandC(Q){
if(问题规模足够小)
直接求解;
else{
将问题分解为若干子问题q1,q2,...;
return Combine(DandC(q1),DandC(q2),...);
}
}
四、典例
- 三分查找
#include <iostream>
using namespace std;
int TriSearch(int *a, int l, int r, int x){
if(l <= r){
int d1 = l + (r - l) / 3; // 左侧三等分点
int d2 = r - (r - l) / 3; // 右侧三等分点
if(x == a[d1]) return d1;
if(x == a[d2]) return d2;
if(x < a[d1])
return TriSearch(a, l, d1 - 1, x);
if(x > a[d2])
return TriSearch(a, d2 + 1, r, x);
// (d1, d2)
return TriSearch(a, d1 + 1, d2 - 1, x);
}
return -1;
}
- 快速排序
int partition(int l, int r){
int i = l, j;
for(j = i + 1; j <= r; j ++){
if(a[j] < a[l])
swap(a[++i], a[j]);
}
swap(a[l], a[i]);
return i;
}
int a[N];
void qsort(int l, int r){
if(l < r){
int j = rand() % (r - l + 1) + l; // 随机数法选择主元
swap(a[l], a[j]);
qsort(l, j - 1);
qsort(j + 1, r);
}
}
- 归并排序
int a[N];
void Merge(int l, int m, int r){
int b[N];
int i = l, j = m + 1, k = 0;
while(i <= m && j <= r){
if(a[i] < a[j]) b[k ++] = a[i ++];
else b[k ++] = a[j ++];
}
while(i <= m) b[k ++] = a[i ++];
while(j <= r) b[k ++] = a[j ++];
for(i = l, k = 0; i <= r; i ++, k ++) // copy back
a[i] = b[k];
}
void MergeSort(int l, int r){
if(l < r){
int m = l + r >> 1;
MergeSort(l, m);
MergeSort(m + 1, r);
Merge(l, m, r);
}
}
- 第 k 小元素问题
int a[N];
int select(int l,int r, int k){
while(1){
int j = partition(l, r);
if(j + 1 == k) return a[j];
else if(j + 1 > k) r = j - 1;
else l = j + 1;
}
}