一、简介
将一个复杂的问题分解成两个或多个相似的子问题,递归地解决这些子问题,然后将子问题的解合并以解决原始问题。这种方法的核心思想是将大问题分解为小问题,通过解决小问题来简化大问题的解决过程。
二、解题思路
- 分解(Divide):
将原始问题分解成两个或多个规模较小但类似于原始问题的子问题。 - 解决(Conquer):
递归地解决每个子问题。如果子问题的规模足够小,可以直接解决,不再递归。 - 合并(Combine):
将子问题的解合并,形成原始问题的解。
三、应用场景
- 排序算法:
快速排序(Quick Sort):通过选择一个基准元素,将数组分为两部分,一部分包含小于基准的元素,另一部分包含大于基准的元素,然后递归地对这两部分进行排序。
归并排序(Merge Sort):将数组分成两半,递归地对每一半进行排序,然后合并已排序的两半。 - 搜索算法:
二分查找(Binary Search):在有序数组中查找元素,通过不断将搜索范围减半来定位元素。 - 数学问题:
最大子数组和问题:通过分治法将数组分为两部分,分别求解两部分的最大子数组和,然后考虑跨越两部分边界的最大子数组和。 - 几何问题:
点定位问题:在一个平面上,给定一个点集和一个目标点,判断目标点位于点集的哪一侧。通过分治法将点集分为两部分,递归地解决子问题。 - 并行计算:
在并行计算中,分治法可以用来将问题分解成多个子任务,这些子任务可以并行处理。
四、模板函数
// 分治法模板函数
template <typename T, typename Compare>
void divideConquer(std::vector<T>& data, int left, int right, Compare comp) {
if (left >= right) {
// 基本情况:当子数组只有一个或没有元素时,不需要进一步分解
return;
}
// 分解步骤:选择一个基准元素,将数组分为两部分
int pivotIndex = left; // 这里简单地选择第一个元素作为基准
int mid = (left + right) / 2;
// 递归地对左右两部分进行分治
divideConquer(data, left, mid, comp);
divideConquer(data, mid + 1, right, comp);
// 合并步骤:合并已排序的两部分
merge(data, left, mid, right, comp);
}
// 合并已排序的两部分
template <typename T, typename Compare>
void merge(std::vector<T>& data, int left, int mid, int right, Compare comp) {
int n1 = mid - left + 1;
int n2 = right - mid;
// 创建临时数组
std::vector<T> L(n1), R(n2);
// 拷贝数据到临时数组
for (int i = 0; i < n1; i++) {
L[i] = data[left + i];
}
for (int j = 0; j < n2; j++) {
R[j] = data[mid + 1 + j];
}
// 初始化指针
int i = 0, j = 0, k = left;
// 合并临时数组回原数组
while (i < n1 && j < n2) {
if (comp(L[i], R[j])) {
data[k++] = L[i++];
} else {
data[k++] = R[j++];
}
}
// 拷贝剩余的元素
while (i < n1) {
data[k++] = L[i++];
}
while (j < n2) {
data[k++] = R[j++];
}
}