一、算法设计思想
分治法 设计思想是将一个难以解决的大问题分解成若干个规模较小的问题,然后逐个解决分而治之。分治法经常和递归结合运用,那么什么问题适合使用分治法来求解呢?一般来说满足下列3个条件:
(1)一个大问题可以分解成若干个子问题
(2)分解后的各个子问题互相独立
(3)子问题的解可以合并为原问题的解
分治法求解的步骤:
(1)分解问题:将待解决的问题分解为若干个相互独立、与原问题形式相同的子问题
(2)分治子问题:求解各个子问题。由于各个子问题与原问题形式相同,用尽可能简单的办法求解子问题
(3)合并:按照原问题的要求,将子问题合并成原问题的解
二、分治算法的案例
1、归并排序
归并排序将待排序数组a[1..n]分成两个各含n/2个元素的子序列,然后对这个两个子序列进行递归排序,最后将这两个已排序的子序列进行合并,即得到最终排好序的序列
数组排序时,如果只有1个数,那么它本身就是有序的;如果有2个数,那么比较1次就完成排序。也就是说数越少排序越容易。如果一个数据文件比较大很难快速完成排序,这时候我们可以将很大的数据文件分解成较小的数列,直到剩下一个数时候其本身就有序,再把这些有序的数列合并在一起从而完成排序。假设有个待排序的数组{8,4,5,7,1,3,6,2}如下图所示:
![af276eda82fa3fd37987c232992e3750.png](https://i-blog.csdnimg.cn/blog_migrate/42f6d67bf448b592f1f1bc0b90bf9291.jpeg)
由上图可知,首先将待排序的数组分成大小相同的子序列,然后再把子序列分解成大小相同的两个子序列,如此做法直到分解成一个元素为止。然后进行归并操作,将两个子序列合并成一个子序列并排序,如此循环直到所有元素都归并为一个有序序列为止
(1)问题分析
本案例有2个重要的操作:分解操作和归并操作,也就是说需要设计2个主要的方法来实现分解操作和归并操作。其中分解操作情况比较好处理一些,可以通过递归对自身不断的调用分解。这里先主要说下归并的方法
【1】方法的定义及其说明
方法:void merge(int a[] , int low , int mid ,int high)
参数说明:
a[] :数组对象
low:待合并的两个子序列的下届
heigh:待合并的两个子序列的上届
mid:中间位置
【2】方法的实现过程
首先有个临时数组b[]将排好序的数据放到临时数组: int *b = new int[a.length];然后设计3个变量辅助工作:int i = low,j = mid + 1 ,index = 0,i和j指向两个待排序子序列中当前比较的元素,index指向辅助数组b[]中待放置元素的位置。比较a[ i ]和a[ j ]将较小的赋值给b[index],同时相应的索引i、j、k后移,循环处理直到所有元素处理完成,最后把排好序的数组b的元素复制到数组a中
假设有个待排序的数组:{3,9,16,23,66,2,6,19,20},数据的初始化如下所示:
![62ca1729c6b6f8a0f4e5971594a84306.png](https://i-blog.csdnimg.cn/blog_migrate/9f378ed29be417e1d68497c1043c2dbb.jpeg)
现在开始比较a[i]和a[ j ],将较小的元素放在数组b,相应的i , j ,index指针后移,直到 i > mid 或者j > high结束,代码如下:
while(i > mid || j > high ){ //也可 i<=mid && j<=high
if (a[i] < = a[ j ]){
b[index++] = a [ i++];
}else{
b[index++] = a [ i++];
}
}
下面详细描述下归并的过程:
第1次比较: a[ i] = 3 a[ j] = 2,将小的元素2放到数组b,指针移动j++,index++,大的那个数i指针就不需要移动了
初始化:
![94643886a482aeab1648733b41878364.png](https://i-blog.csdnimg.cn/blog_migrate/0fcec0625f77b7bc520909d5c782cd6a.jpeg)
比较结果:
![6b2ad511eaa3a60f80eda8eef9da6570.png](https://i-blog.csdnimg.cn/blog_migrate/1c99e97bcac0abafd9d36d3733b11841.jpeg)
第2次比较: a[ i] = 3 a[ j] =6,将小的元素3放到数组b,指针移动i++,index++,大的那个数j指针就不需要移动了
初始化:
![326ab3cb5f8c4839b8978e7f365f4f1b.png](https://i-blog.csdnimg.cn/blog_migrate/3aee147da2f89709ccc0396099e1c45b.jpeg)
比较结果:
![196dab37f157aba2a495c2bbb169e3f1.png](https://i-blog.csdnimg.cn/blog_migrate/42d2885c08aaa7550cd15aa56c46c593.jpeg)
第3次比较结果:
![aca5f3ae395f2598b107315133ff16f2.png](https://i-blog.csdnimg.cn/blog_migrate/d5abc301a001f87d810b9a62f74aa035.jpeg)
第4次比较结果:
![6804c9d01423a427d8529447dd8f2077.png](https://i-blog.csdnimg.cn/blog_migrate/dae3d12c9c6e47ffbd8f01f3af3cbb8f.jpeg)
第5次比较结果:
![942cf9e96efbb0fbe0922054f719c2d1.png](https://i-blog.csdnimg.cn/blog_migrate/5f5633c6f5dbb8803c759171a57d4301.jpeg)
第6次比较结果:
![1a2b43d22439c61ed059a69ce181b12e.png](https://i-blog.csdnimg.cn/blog_migrate/2c5ab1d6db3c6846b2d895fd9a384189.jpeg)
第7次比较结果:
![a4838284c41febfa9379b5537452e633.png](https://i-blog.csdnimg.cn/blog_migrate/efbf01c119246fc8001a2f6a59ca6b54.jpeg)
这时候,j > high,while循环结束,但是a数组还有2个剩余元素(i<=mid),直接将其放到b数组即可:
while(i<=mid){
b[ index++ ] = a[ i++]
}
这时候完成了归并过程,现在把辅助数组b中的元素复制到原来的a数组里即可:
for( int I = low, j = 0;i <= high ;i++)
{
a[i] = b[j++];
}
该算法的时间复杂度为 O(n log n)
(2)c++语言实现的代码:
#include
#include
#include
using namespace std;
void merge(int a[],int low,int mid,int high){
//定一个辅助数组
int *b = new int[high - low +1];
int i = low;
int j = mid + 1;
int index = 0;//辅助数组的索引
//将数组a的数据按升序排序放在辅助数组b
while(i <= mid && j <= high)
{
if(a[i] <= a[j])
{
b[index++] = a[i++];
}else{
b[index++] = a[j++];
}
}
//将剩下的数据放到数组b中
while(i <= mid ) {
b[index++] = a[i++];
}
while(j <= high ){
b[index++] = a[j++];
}
//将辅助数组b的数据拷贝到a
for(i = low,index = 0;i <= high;i++){
a[i] = b[index++];
}
}
void mergeSort(int a[],int low,int high){
if(low < high){
int mid = (low + high)/2;
//对a数组从low---mid元素归并排序
mergeSort(a,low,mid);
//对b数组从mid+1---high元素归并排序
mergeSort(a,mid+1,high);
merge(a,low,mid,high);//合并方法
}
}
int main()
{
int a[9] = {3,9,16,23,66,2,6,19,20};
mergeSort(a,0,8);
for(int i=0;i<9;i++){
cout<
cout<
} return 0;
}
(3)Java语言实现归并排序
import java.util.Arrays;
public class MergeSort {
public static void main(String []args){
int []arr = {9,8,7,6,5,4,3,2,1};
sort(arr);
System.out.println(Arrays.toString(arr));
}
public static void sort(int []arr){
int []temp = new int[arr.length];//辅助数组temp
sort(arr,0,arr.length-1,temp);
}
private static void sort(int[] arr,int left,int right,int []temp){
if(left
int mid = (left+right)/2;
sort(arr,left,mid,temp);//左边归并排序,使得左子序列有序
sort(arr,mid+1,right,temp);//右边归并排序,使得右子序列有序
merge(arr,left,mid,right,temp);//将两个有序的数组合并操作
}
}
private static void merge(int[] arr,int left,int mid,int right,int[] temp){
int i = left;//左序列指针
int j = mid+1;//右序列指针
int t = 0;//临时数组指针
while (i<=mid && j<=right){
if(arr[i]<=arr[j]){
temp[t++] = arr[i++];
}else {
temp[t++] = arr[j++];
}
}
while(i<=mid){//将左边剩余元素填充进temp中
temp[t++] = arr[i++];
}
while(j<=right){//将右序列剩余元素填充进temp中
temp[t++] = arr[j++];
}
t = 0;
//将temp中的元素全部拷贝到原数组中
while(left <= right){
arr[left++] = temp[t++];
}
}
}