1. 分治法的思想
许多有用的算法在结构上是递归的:为了解决一个给定的问题,算法一次或多次递归地调用其自身以解决紧密相关的若干子问题。这些算法典型地遵循分治法的思想:将原先问题分解为几个规模较小但类似于原问题的子问题,递归地求解这些子问题,然后再合并这些问题的解来建立原问题的解。
2.分治法的步骤
分治模式在每层递归时都有三个步骤:
1) 分解:分解原问题为若干子问题,这些子问题是原问题的规模较小的实例。
2) 解决:解决这些子问题,递归地求解各子问题。然而,若子问题的规模足够小,则直接求解。
3) 合并:合并这些子问题的解成为原问题的解。
归并排序算法完全遵循分治模式。直观上其操作如下:
1) 分解:分解待排序的n 个元素的序列成各具n/2个元素的两个子序列。
2) 解决:使用归并排序递归地排序两个子序列。
3) 合并:合并两个已排序的子序列以产生已排序的答案。
3.分治法实现排序伪代码
MERGE(A,p,q,r)
n1 = q – p + 1
n2 = r – q
let L[1..n1+1] and R[1..n2+1] be new arrays
for i = 1 to n1
L[i] = A[p + i - 1]
for j = 1 to n2
R[j] = A[q +j]
L[n1 + 1] = ∞
R[n2 + 1] = ∞
i = 1
j = 1
for k = p to r
if L[i] <= R[j]
A[k] = L[i]
i = i + 1
else
A[k] = R[j]
j = j +1
4.分析算法
在上面伪代码中,我们是假设有两个数组是排序好的,分别是L和R。所以通过上面的算法来将两个已经排序好的数据放到一个数据A中,A数组最终的结果应该是有序的。所以上面的代码只是实现了其中一部分的核心功能。如果要想将一个数据分成两个数组,并且每个数组都是已经排序好的,那么我们的算法应该使用到递归的算法。结合上面的算法,我们完善一下算法,如下:
MERGE-SORT(A,p,r)
if p < r
q = [(p + r)/2]
MERGE-SORT(A,p,q)
MERGE-SORT(A,q + 1, r)
MERGE(A,p,q,r)
这样,我们先将一个数组分为两个部分,然后对各自的进行排序,之后再整合到一个数组中。如下图所示:
归并排序在数组A=[5,2,4,7,1,3,2,6]上的操作。随着算法自底向上地推进,待合并的已排好序的各序列的长度不断增加。
5.javap实现分治算法
现在我们假设有一组数据,前半部分与后半部分都是排序好的,我们使用上面已经写过的算法,用java来实现。
public class DivideAndConquer
{
public int[] merge(int[] array, int p, int q, int r)
{
int n1 = q - p + 1;
int n2 = r - q;
Integer [] lArray = new Integer[n1+1];
Integer [] rArray = new Integer[n2+1];
for(int i = 0; i < n1; i++)
{
lArray[i] = array[p + i ];
}
for(int j = 0; j < n2; j++)
{
rArray[j] = array[q + j +1];
}
lArray[n1] = Integer.MAX_VALUE;
rArray[n2] = Integer.MAX_VALUE;
int i = 0;
int j =0;
for(int k = p; k <= r; k++)
{
if (lArray[i] <= rArray[j])
{
array[k] = lArray[i];
i = i + 1;
}
else
{
array[k] = rArray[j];
j = j + 1;
}
}
return array;
}
}
我们使用Junit测试:
@Test
public void testMerge()
{
DivideAndConquer dac = new DivideAndConquer();
int[] a = {2, 4, 5, 7, 1, 2, 3, 6};
int[] resultA = {1, 2, 2, 3, 4, 5, 6, 7 };
assertArrayEquals(dac.merge(a,0,3,7), resultA);
}
6.总结
我们通过上面提到的分治的步骤,首先,将一个数组进行分解成两个部;其次就是将每个部分进行排序,笔者给出的例子是已经排序好的,这就是解决问题;最后将排序好的两个数组合并成一个排序好的数组。