分冶法之归并排序

来源:互联网 发布:汽车配件管理销售软件 编辑:程序博客网 时间:2024/06/07 04:03

1.分冶策越中的两种典型的划分案例

(1).黑盒划分策越:合并排序  逆序对问题

 根据问题的规模对原问题进行划分,而不考虑划分对象的属性值,所以形象的称之为黑盒策越(就是说我们归并排序的时候,划分为,两个小模块的时候,并没有按照其值的属性进行划分,而只是简单的对于我们的数组内的大小进行区分)

(2)白盒策越:快速排序,最接近点对

根据划分对象的特定的属性值,把对象划分为若干个子集(白盒问题划分时,有的问题不可能出现解或者这些子问题可以忽视而无需求解,这类问题称为减冶策越,如二分,指数运算)


2.何为分冶思想?

就是将一个大规模的问题划分为子问题,然后分别求这些子问题的结果,得到子问题的结果,我们就可以得到原问题的结果,分冶并不是一种算法,而已求解问题的一种思路和策越,与分冶不可分割的就是递归,没有递归分冶也无法落地!

递归:

 (1: 递归的边界 ,递归的关系式,递归的边界处理部分,递归的调用部分

如:n!

  inf facorical(int n)

{

      if(n==0)//边界

        return 1;//边界的处理

   return n*(n-1);//递归的调用部分

}


3.分冶的一般的执行过程

分冶算法何以概括为:分冶合三个字
(1): 设计特定的adhoc(P)子程序,直接求解比较小的规模的问题,从程序的逻辑上来看,他是递归的出口,也可以认为adhoc程序是对最简单的子问题的冶的过程,毕竟任何的子问题的求解都将通过递归的方式归结到adhoc程序处理
   (2):设计划分的策略:把原问题划分为P,K个规模较小的子问题,这个是分冶法的基础和关键的步骤,在设计策略的时候,往往遵循两个基本的原则,一个是平衡子问题(规模相当的子问题),一个是独立子问题原则,K个子问题之间的重叠不存在!分解数目k 越小,子问题的规模越小(对应的m)越大,时间效率更低logm(N) 


4.归并排序合并的过程如何实现(黑盒策略,最后合并的时候是一个有序的)

首先考虑下如何将将二个有序数列合并。这个非常简单,只要从比较二个数列的第一个数,谁小就先取谁,取了后就在对应数列中删除这个数。然后再进行比较,如果有数列为空,那直接将另一个数列的数据依次取出即可。

[cpp] view plaincopy
  1. //将有序数组a[]和b[]合并到c[]中  
  2. void MemeryArray(int a[], int n, int b[], int m, int c[])  
  3. {  
  4.     int i, j, k;  
  5.   
  6.     i = j = k = 0;  
  7.     while (i < n && j < m)  
  8.     {  
  9.         if (a[i] < b[j])  
  10.             c[k++] = a[i++];  
  11.         else  
  12.             c[k++] = b[j++];   
  13.     }  
  14.   
  15.     while (i < n)  
  16.         c[k++] = a[i++];  
  17.   
  18.     while (j < m)  
  19.         c[k++] = b[j++];  
  20. }  

可以看出合并有序数列的效率是比较高的,可以达到O(n)。

解决了上面的合并有序数列问题,再来看归并排序,其的基本思路就是将数组分成二组A,B,如果这二组组内的数据都是有序的,那么就可以很方便的将这二组数据进行排序。如何让这二组组内数据有序了?

5.看哈我们的实际的处理过程



首先是分成小问题,不由按照其值进行,直接按照序号,然后解决小问题,最后合并我们的小问题


解决了上面的合并有序数列问题,再来看归并排序,其的基本思路就是将数组分成二组A,B,如果这二组组内的数据都是有序的,那么就可以很方便的将这二组数据进行排序。如何让这二组组内数据有序了?

可以将A,B组各自再分成二组。依次类推,当分出来的小组只有一个数据时,可以认为这个小组组内已经达到了有序,然后再合并相邻的二个小组就可以了。这样通过先递的分解数列,再合数列就完成了归并排序。

[cpp] view plaincopy
  1. //将有二个有序数列a[first...mid]和a[mid...last]合并。  
  2. void mergearray(int a[], int first, int mid, int last, int temp[])  
  3. {  
  4.     int i = first, j = mid + 1;  
  5.     int m = mid,   n = last;  
  6.     int k = 0;  
  7.       
  8.     while (i <= m && j <= n)  
  9.     {  
  10.         if (a[i] <= a[j])  
  11.             temp[k++] = a[i++];  
  12.         else  
  13.             temp[k++] = a[j++];  
  14.     }  
  15.       
  16.     while (i <= m)  
  17.         temp[k++] = a[i++];  
  18.       
  19.     while (j <= n)  
  20.         temp[k++] = a[j++];  
  21.       
  22.     for (i = 0; i < k; i++)  
  23.         a[first + i] = temp[i];  
  24. }  
  25. void mergesort(int a[], int first, int last, int temp[])  
  26. {  
  27.     if (first < last)  //划分为只有一个元素,最后在合并!其实非常简单的思路!
  28.     {  
  29.         int mid = (first + last) / 2;  
  30.         mergesort(a, first, mid, temp);    //左边有序  
  31.         mergesort(a, mid + 1, last, temp); //右边有序  
  32.         mergearray(a, first, mid, last, temp); //再将二个有序数列合并  
  33.     }  
  34. }  
  35.   

归并排序的效率是比较高的,设数列长为N,将数列分开成小数列一共要logN步,每步都是一个合并有序数列的过程,时间复杂度可以记为O(N),故一共为O(N*logN)。因为归并排序每次都是在相邻的数据中进行操作,所以归并排序在O(N*logN)的几种排序方法(快速排序,归并排序,希尔排序,堆排序)也是效率比较高的。

 

对20000个随机数据进行测试:

对50000个随机数据进行测试:

再对200000个随机数据进行测试:

 

6.非递归的归并思路

归并排序的非递归实现如下,思想和递归正好相反,原来的递归过程是将待排序集合一分为二,直至排序集合就剩下一个元素位置,然后不断的合并两个排好序的数组。所以非递归思想为,将数组中的相邻元素两两配对。用merge函数将他们排序,构成n/2组长度为2的排序好的子数组段,然后再将他们排序成长度为4的子数组段,如此继续下去,直至整个数组排好序。

非递归(迭代,循环展开)--自底向上





0 0
原创粉丝点击