算法导论—排序算法学习(2)

来源:互联网 发布:淘宝买家退货率 编辑:程序博客网 时间:2024/05/16 07:45

二.归并排序

   归并排序算法在思想上是借鉴了分治策略来解决问题的。而分治模式在算法结构上是递归的。
   分治模式在每层递归时都有三个步奏:
   分解原问题为若干子问题,这些子问题是原问题的规模较小的实例;
   解决这些子问题,递归地求解各子问题。然而,若子问题的规模足够小,则直接求解;
   合并这些子问题的解成原问题的解。

   归并排序算法完全遵循分治模式。直观上也可以分成三个步奏。
   分解:分解待排序的n个元素的序列成两个具有n/2个元素的子序列。
   解决:使用归并排序递归的解决这两个子序列。
   合并:将已经排序好的两个子序列进行整理排序,合并成一个序列。
   当待排序列不断分解,直到两个子序列都为一个元素时,递归就可以“开始回升”了,在这种情况下不在需要做任何工作,因为长度为1的每个序列都已排好序。

   下面首先给出合并代码,即把已经排序好的两个子序列合并成一个有序序列:

// 默认Array为带排序数组,[p,q]与[q+1,r]为两个待排子序列void Merge(int *Array,int p,int q,int r)     int i,j,k;    int l_length = q - p + 1; //下标较小子序列的长度    int r_length = r - q;     //下标较大子序列的长度    int left[l_length+1],right[r_length+1];//新建两数组用于存放子序列    for(i=0;i<l_length;i++)        left[i] = Array[p+i];    for(i=0;i<r_length;i++)        right[i] = Array[q+1+i];    left[l_length] = right[r_right] = Inf;// Inf为无穷大//两数组最后一个元素作为哨兵,见到任何一个数组的哨兵牌表示该数组元素已经排序完毕    i = j = 0;    for(k=0;k<r-p+1;k++)    {        if(left[i] >= right[j])        {               Array[p+k] = right[j];            j++;        }        else         {            Array[p+k] = left[i];            i++;        }    }}

    合并过程比较容易理解,即每次从两个有序子序列中抽取一个进行比较,选择较小的元素放进待排序数组。当一个数组全部元素都被选走之后,此时数组剩下的最后一个元素为哨兵元素,肯定比另外一个数组任意未选取元素要大,所以剩下需要的操作就是对仍有未排序元素放入待排序数组即可。

    下面给出归并排序的代码:

void MergeSort(int *Array,int p,int r){    if(p<r)    {        int q = (r + p) / 2;  //q为将待排序列一分为2的中点        MergeSort(Array,p,q);        MergeSort(Array,q+1,r);        Merge(Array,p,q,r);    }}

    如上所述,归并排序在于首先不断利用递归的特性将待排元素一分为二,直到待排子序列都只包含一个元素时,递归开始回升,然后合并,跳出递归时就完成了对数组的排序。

    归并排序在的时间复杂度可以分为两个部分,一个是将数组一分为二的过程,这个过程的时间复杂度为o(log n),而合并的过程需要对每一个元素进行一次操作,所以时间复杂度为o(n)综合起来时间复杂度为o(nlog n)。从时间复杂度上来看,归并排序是优于插入排序的,但是需要注意的是,由于归并排序的核心是采用了递归的方式,所以需要进行大量进栈和出栈的过程,这个过程需要大量的空间,所以当需要排序的元素个数较多时,采用归并排序并不是一个好的方法。

0 0
原创粉丝点击