基本概念
1.把一个复杂的问题分成两个或更多的相同或相似的子问题,再把子问题分成更小的子问题……直到最后子问题可以简单的直接求解,原问题的解即子问题的解的合并。
2.分治策略是对于一个规模为n的问题,若该问题可以容易地解决(比如说规模n较小)则直接解决,否则将其分解为k个规模较小的子问题,这些子问题互相独立且与原问题形式相同,递归地解这些子问题,然后将各子问题的解合并得到原问题的解。
适用情况
1)该问题的规模缩小到一定的程度就可以容易地解决
2)该问题可以分解为若干个规模较小的相同问题,即该问题具有最优子结构性质。
3)利用该问题分解出的子问题的解可以合并为该问题的解;
4)该问题所分解出的各个子问题是相互独立的,即子问题之间不包含公共的子子问题。
分治法的复杂性分析
一个分治法将规模为n的问题分成k个规模为n/m的子问题去解。设分解阀值n0=1,且adhoc解规模为1的问题耗费1个单位时间。再设将原问题分解为k个子问题以及用merge将k个子问题的解合并为原问题的解需用f(n)个单位时间。用T(n)表示该分治法解规模为|P|=n的问题所需的计算时间,则有:
T(n)= k T(n/m)+f(n)
通过迭代法求得方程的解:
递归方程及其解只给出n等于m的方幂时T(n)的值,但是如果认为T(n)足够平滑,那么由n等于m的方幂时T(n)的值可以估计T(n)的增长速度。通常假定T(n)是单调上升的,从而当 mi≤n<mi+1时,T(mi)≤T(n)<T(mi+1)。
分治法例题:合并排序和快速排序
public class 分治_合并排序 {
/**
* 函数说明:在数组被拆分以后进行合并
*/
static void Merge(int a[], int left, int middle, int rigth) {
//定义左端数组大小
int n1 = middle - left+1;
int n2 = rigth - middle;
//初始化数组,分配内存
int bejin[] = new int[n1];
int end[] = new int[n2];
//数组赋值
for(int i = 0; i < n1; i++)
bejin[i] = a[left + i];
for(int i = 0; i < n2; i++)
end[i] = a[middle+1+i];
//用key做原数组索引,每调用一次函数重新给原数组赋一次值
int i = 0, j = 0, key;
for(key = left; key <= rigth; key++){
if(n1>i&&n2>j&&i < n1 && bejin[i] <= end[j])
a[key] = bejin[i++];
else if(n1>i&&n2>j&&j < n2 && bejin[i] >= end[j])
a[key] = end[j++];
else if(i == n1 && j < n2)
a[key] = end[j++];
else if(j == n2 && i < n1)
a[key] = bejin[i++];
}
}
/**
* 差分数组区间,不断分支
*/
static void MergeSort(int a[],int left,int rigth) {
int middle=0;
if(left<rigth) {
middle =(rigth+left)/2;
MergeSort(a, left, middle);
MergeSort(a, middle+1, rigth);
Merge(a, left, middle, rigth);
}
}
public static void main(String[] args) {
int a[]= {85,3,52,9,7,1,5,4};
MergeSort(a, 0,7);
for(int i=0;i<8;i++) {
System.out.print(" "+a[i]);
}
}
}
public class 分治_快速排序 {
/**
*交换函数,i,j为数组索引
*/
static void swap(int A[], int i, int j)
{
int temp = A[i];
A[i] = A[j];
A[j] = temp;
}
/**
* 选取一个关键字(key)作为枢轴,一般取整组记录的第一个数/最后一个,这里采用选取序列最后一个数为枢轴。
* 设置两个变量left = 0;right = N - 1;
* 从left一直向后走,直到找到一个大于key的值,right从后至前,直至找到一个小于key的值,然后交换这两个数。
* 重复第三步,一直往后找,直到left和right相遇,这时将key放置left的位置即可。
* @return
*/
static int PartSort(int[] array,int left,int right)
{
int key = array[right];//定义基准
int count=right;//保存rigth值
while(left < right)//防止数组越界
{
while(left < right && array[left] <= key)
{
++left;
}
while(left < right && array[right] >= key)
{
--right;
}
swap(array,left,right);
}
swap(array,right,count);
return right;
}
/**
*分治思想,递归调用
*/
static void QuickSort(int array[],int left,int right)
{
if(left >= right)//表示已经完成一个组
{
return;
}
int index = PartSort(array,left,right);//枢轴的位置
QuickSort(array,left,index - 1);
QuickSort(array,index + 1,right);
}
public static void main(String[] args) {
int a[]= {1,5,-5,54,15,67,16,23};
QuickSort(a,0,7);
for(int i=0;i<a.length;i++) {
System.out.print(" "+a[i]);
}
System.out.print("\n");
}
}
算法心得
作为分治法里很典型的一种算法,合并排序和快速排序充分展现了分治法的思想,分而治之,在此次编程使用此方法中,给我的体会是程序简单分为两部分,第一部分,不断“拆”,缩小子问题规模,达到最优子结构。然后合并,在合并过程中,应为子问题足够小,容易计算,再者不断合并子问题答案,最终求出问题解。