分治算法的基本思想是:
分(divide):递归求解子问题,即:分解+求解,将问题分解为k个方便求解的小问题。
为什么说是递归求解呢,这里可以看作将一个问题分2个子问题,如果2个子问题还是大,再继续分成4个子问题,直到分解到能方便求解的小问题。也就是说分治算法是含有2个以上的递归运算,只有一个递归的例程不能算做分治算法。
治(conquer):从子问题构建原问题的解。
对于分治,最长用到的复杂度分析情况为:
T(N) = aT(N/b) + O(N^k)
当a=b^k时, T(N)=O(N^k*logN)
比如,非常常见的二分法: T(N) = 2T(N/2) + O(N) ,此时a=2, b=2, k=1 即a=b^k,所以 该算法的复杂度为 O(NlogN)
从另一个角度,反向分析问题,如果我们希望得到一个O(NlogN)的算法,那么就需要保证附加工作为O(N),这是一个非常非常关键的利用分治算法解决问题的入手点!!!
而且这个复杂度也是大多数分治算法问题的情况。 当然还有另外两种>, < 情况,就参考书上的详细讲解吧。
下面以一个例子详细介绍如何应用分治算法。
最近点对问题:给定平面上的N个点,找出距离最近的两个点。
对于该问题,算法过程并不算复杂,但要想编程实现,需要克服不少细节问题。
首先应该实现Point类:
Point.h:
#include <iostream>
class
Point
{
public
:
Point (
double
x
,
double
y );
double
getx ()
const
{
return
m_x ;
}
double
gety ()
const
{
return
m_y ;
}
friend
std
::
ostream
&
operator
<< (
std
::
ostream
&
os
,
const
Point
& p);
private
:
double
m_x ;
double
m_y ;
};
Point.cpp:
#include "Point.h"
Point
::
Point (
double
x
,
double
y )
:
m_x (
x
),
m_y (
y )
{
}
std
::
ostream
&
operator
<< (
std
::
ostream
&
os
,
const
Point
& p)
{
os
<<
"P("
<< p
.
getx ()
<<
","
<< p
.
gety ()
<<
")" ;
return
os ;
}
这里有几点要注意:重载<<,友元函数的应用,初始化的位置。不多介绍了,代码很简单,上点心看看就好。
接下来,是解决该问题的具体过程:
1. 最简单的解法就是蛮力法:把每两个点的距离求出来,然后找出最小值即可。
虽然,该算法很简单,但编程实现时,从该笨方法起步会有一个很好的过渡,不至于编码难度过陡。
于是我们需要:计算两点距离的函数
Distance , 求最近点对函数
FindShortPair , 一组点
Point p
[] 用以测试, 打印点函数
PrintPoints 用于观察。
代码如下:
上面的代码完成了很多该问题算法的外围工作,最重要的是提供了测试环境。注意const的应用,double,int型的定义。
#include "Point.h"
#include <cmath>
double
Distance (
const
Point
& s
,
const
Point
&
t )
{
double
squarex
= ( s
.
getx ()
-
t
.
getx () )
* ( s
.
getx ()
-
t
.
getx () );
double
squarey
= ( s
.
gety ()
-
t
.
gety () )
* ( s
.
gety ()
-
t
.
gety () );
return
sqrt (
squarex
+
squarey );
}
void
FindShortPair (
const
Point
* p
,
int
num )
{
//can only find one of the shortest path
double
distance
=
Distance (p
[
0
], p
[
1
]);
int
start
=
0 ;
int
end
=
1 ;
for (
int
i
=
0 ;
i
<
num ;
i
++ )
{
for (
int
j
=
i
+
1 ;
j
<
num ;
j
++ )
&nbs