1. 分治算法思想
- 分治算法的核心思想就是,分而治之,将原问题划分成n个规模较小,并且结构与原问题相似的子问题,递归地解决这些子问题,然后再合并其结果,就得到原问题的解。
- 分治算法一般都比较适合用递归来实现。分治算法的递归实现中,每一层递归都会涉及这样三个操作:
-1- 分解:将原问题分解成一系列子问题;
-2- 解决:递归地求解各个子问题,若子问题足够小,则直接求解;
-3- 合并:将子问题的结果合并成原问题。
分治算法能解决的问题,一般需要满足下面这几个条件:
- 原问题与子问题具有相同的模式;
- 子问题可以独立求解,子问题之间没有相关性,这一点是分治算法跟动态规划的明显区别;
- 具有分解终止条件,也就是说,当问题足够小时,可以直接求解;
- 可以将子问题合并成原问题,这个合并操作的复杂度不能太高。
2. 应用举例
2.1 逆序度
假如需要从小到大排序,大的数在小的数前面则记逆序数+1
- 暴力的方法:分别对每个数,看后面有几个比它小的,双重循环,复杂度O(n2)
- 归并排序法:
将原序列分成A1,A2两个序列,分别计算它两的逆序度K1,K2,再计算A1,A2之间的逆序度K3,原序列逆序度就是 K1+K2+K3
/**
* @description: 分治法求逆序数(归并排序法)
* @author: michael ming
* @date: 2019/7/3 23:27
* @modified by:
*/
#include <iostream>
class ReverseOrderCount
{
int sum;
void divide(int *a, int left, int right)
{
if(left >= right)
return;
int mid = (left+right)/2;
divide(a,left,mid);
divide(a,mid+1,right);
merge(a,left,mid,right);
}
void merge(int *a, int left, int mid, int right)
{
int i = left, j = mid+1, len = right-left+1, idx = 0;
int *temp = new int [len];
while(i <= mid && j <= right)
{
if(a[i] <= a[j])
temp[idx++] = a[i++];
else
{
sum += mid-i+1;//左边i后面的都比a[j]大
temp[idx++] = a[j++];
}
}
while(i <= mid)//剩余的半边
temp[idx++] = a[i++];
while(j <= right)//剩余的半边
temp[idx++] = a[j++];
for(idx = 0; idx < len; ++idx)
{
a[left++]= temp[idx]; //把有序的临时数组写入原来数组的起始位置
}
delete [] temp; //释放空间
temp = NULL; //指针置空
}
public:
int count(int *a, int n)
{
sum = 0;
divide(a,0,n-1);
return sum;
}
};
int main()
{
ReverseOrderCount roc;
int arr[6] = {2,4,3,1,5,6};
std::cout << "arr数组的逆序数是:" << roc.count(arr,6);
return 0;
}
- 练习题 http://poj.org/problem?id=2299
Accepted代码如下
https://github.com/hitskyer/course/blob/master/c/chenmingming/algorithm/POJ/poj2299_B.cpp
3. 分治思想处理海量数据
比如,给10GB的订单文件按照金额排序,看似是一个简单的排序问题,但是因为数据量大,有10GB,机器的内存可能只有2、3GB这样子,无法一次性加载到内存,也就无法通过单纯地使用快排、归并等基础算法来解决。
- 利用分治思想。将海量的数据集合划分为n个小的数据集合,每个小的数据集合单独加载到内存来解决,然后再将小数据集合并成大数据集合。
- 利用分治,不仅仅能克服内存的限制,还能利用多线程或者多机处理,加快处理的速度。海量的数据利用集群并行处理是大势所趋。