问题引入:
前文说到,叶天帝集结天庭众人攻打生命禁区,在此之前发生了一个小插曲,大黑狗偷了叶天帝的空间戒指,使得叶天帝无法携带大量的资源。为此,叶天帝闭关九九八十一天,创出了 0-1背包大法 ,这才顺利启程,一场大战缓缓拉开帷幕。
这一日,叶天帝与众位黑暗至尊展开了白热化的战斗,叶天帝虽强,但终归是双拳难敌四手,战况岌岌可危,叶天帝险象环生。在这千钧一发之际,只见大黑狗施展“行”字秘术,来到高空,一声大喝“叶小子,分治啊,逐个击破”,叶天帝突然顿悟,施展法门,将空间划分为若干个部分,后施展一气化三清秘术,化出分身去各个空间逐个击破。
此一战,叶天帝在战斗中濒临死境,却也创出了旷古奇法 分治。
分治的基本概念:
把一个任务分成形式和原任务相同,但规模更小的几个部分,然后逐个击破,分别完成,然后再处理完成后的完成后的子任务,将子任务的解进行合并,得到原任务的解。
此算法是许多高效算法的基础,如排序算法(快速排序,归并排序),傅立叶变换(快速傅立叶变换)等
分治法所能解决的问题一般具有以下几个特征:
(1)当问题的规模缩小到一定的程度时,就可以使用很简单的方法解决;
(2)该问题可以分解为若干个规模较小的、相同形式的子问题问题;
(3)通过子问题的解可以合并得出为该问题的解;
(4)该问题所分解出的各个子问题是相互独立的,即子问题之间不包含公共的子问题。
如:斐波那契数列的求解过程,它虽然也可以采用类似“分而治之”的思路,但是它的多个子问题之间都会有交集,所以就不能采用普通的分治算法解决。
分治法解决问题的一般思路:
(1)分解。
(2)解决子问题,若不能解决,则再分解。
(3)合并,由子问题的解推出原问题的解。
几个典型分治算法的应用:
(1)排序(快排,归并)
(2)二分查找
(3)二叉树遍历(前序,中序,后序)
(4)大整数乘法
(5)几何问题(最近点对,凸包)
……
归并排序的思想:
(1)把前一半数组进行排序
(2)把后一半数组进行排序
(3)将前后两部分归并到一个新的有序数组,然后将这个有序数组再“赋值”给原来的数组,归并排序完成。
设计算法:
需要一个辅助数组T,用来存放排序的中间结果。 设定三个指针,po指向辅助数组首元素的位置,p1指向前半个数组的首元素的位置,p2指向后半个数组的首元素的位置。 扫描前后两个已经排好序的半数组,每次将较小的元素放在辅助数组的前面。 经过步骤 3 ,必将会有一个数组先放完,而另外一个数组中还有元素没有放进辅助数组。这时,只需将那个半数组没有放完的元素直接放进辅助数组即可。 将辅助数组拷贝给原数组。归并排序完成。 为什么不直接就输出辅助数组的元素,还费时间将它再拷贝给原数组?图解核心算法:
第一步:p1,p2,po都指向各自数组的首部C++实现:
#include
using namespace std;
void Merge(int *A,int s,int mid,int e,int *T)
{
int po = 0;//指向T数组的第一个
int p1=s, p2=mid+1;//分别指向数组前后两个部分
while (p1 <= mid && p2 <= e)
{
if (A[p1] < A[p2])
{
T[po++] = A[p1++];
}
else
{
T[po++] = A[p2++];
}
}
while (p1 <= mid)
{
T[po++] = A[p1++];
}
while (p2 <= e)
{
T[po++] = A[p2++];
}
for (int i = 0; i < e-s+1; i++)//将归并之后的数组拷贝给原数组
{
A[i+s] = T[i];
}
}
void MergeSort(int *A,int s,int e,int *T)//递归进行归并排序,T为辅助数组
{
if (s < e)
{
int mid = s + (e-s) / 2;
MergeSort(A,s, mid,T);//排序前半部分
MergeSort(A,mid + 1,e,T);//后半部分
Merge(A,s, mid, e,T);//两个部分归并到一起
}
}
int main()
{
int n;
int i;
int A[8] = { 3,0,4,1,7,8,5,2};
n = sizeof(A) / sizeof(int);
int Temp[10] = { 0 };
MergeSort(A,0, n - 1,Temp);
for (i = 0; i < n; i++)
{
cout << A[i]<<" ";
}
cout << endl;
return 0;
}
输出:
0 1 2 3 4 5 7 8
Java实现:
package lu;
public class Test1 {
public static void main(String[] args) {
int n;
int i;
int []A = { 3,0,4,1,7,8,5,2};
n = A.length;
int []Temp = { 0,0,0,0,0,0,0,0 };
MergeSort(A,0, n - 1,Temp);
for (i = 0; i < n; i++)
{
System.out.println(A[i]);
};
}
public static void Merge(int A[],int s,int mid,int e,int T[])
{
int po = 0;//指向T数组的第一个
int p1=s, p2=mid+1;//分别指向数组前后两个部分
while (p1 <= mid && p2 <= e)
{
if (A[p1] < A[p2])
{
T[po++] = A[p1++];
}
else
{
T[po++] = A[p2++];
}
}
while (p1 <= mid)
{
T[po++] = A[p1++];
}
while (p2 <= e)
{
T[po++] = A[p2++];
}
for (int i = 0; i < e-s+1; i++)//将归并之后的数组拷贝给原数组
{
A[i+s] = T[i];
}
}
public static void MergeSort(int []A,int s,int e,int []T)//递归进行归并排序,T为辅助数组
{
if (s < e)
{
int mid = s + (e-s) / 2;
MergeSort(A,s, mid,T);//排序前半部分
MergeSort(A,mid + 1,e,T);//后半部分
Merge(A,s, mid, e,T);//两个部分归并到一起
}
}
}
博客主页