归并排序的递归实现

Posted 汪神

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了归并排序的递归实现相关的知识,希望对你有一定的参考价值。

思路图来自:https://www.cnblogs.com/fanwencong/p/5910503.html

这里我们依然用顺序表来实现这个排序算法。

顺序表一样是0号位不用。

这里我们的归并排序是二路归并,思路就是把序列一直分解成两份,直至分到子序列的长度为1,那么显然子序列已经有序,然后再不停地将有序序列归并起来,最后实现排序。

 

下面上代码:

先是顺序表的接口与声明:

#define OVERFLOW 0
#define ERROR 0
#define FALSE 0
#define OK 1
#define TRUE 1

typedef struct {
    int * elem;//基址空间
    int length;//当前长度
    int size;//储存容量
    int increment;//扩容的增量
}SqList;
/*顺序表的接口*/
int InitSqList(SqList &L, int size, int inc);//初始化顺序表
int DestroySqList(SqList &L);//销毁顺序表L
int ClearSqList(SqList &L);//将顺序表清空
int SqListIsEmpty(SqList L);//判空函数
int GetElemSqList(SqList L, int i, int &e);//用e返回顺序表L中第i号元素
int SearchSqList(SqList L, int e);//在顺序表中查找元素e,若成功时返回该元素第一次出现的位置,否则返回-1
int PutElemSqList(SqList &L, int i, int e);//将L中第i个元素改为e
int AppendSqList(SqList &L, int e);//在L表添加元素e
int DeleteLastSqList(SqList &L, int &e);//删除L表的尾元素,并用参数e作为返回值
void TraverseSqList(SqList L);//遍历元素,并打印
                              /*随机数生成函数*/
int * RandomGenerate(int n);//生成个n个随机数的数组
                            /*测试函数*/

 

看算法:

void Merge(SqList & L1,SqList L2 , int i, int m, int n);//2路归并的归并操作,将L1的相邻有序区间i->m和m+1->n归并成i->n的有序序列储存到L2中。
void MSort(SqList L1, SqList L2, int i, int s, int t);//递归归并排序的递归排序操作,如果i为奇数,排序后的记录储存在L2否则存进L1里

int main() 
{
      srand((unsigned)time(NULL));//加一句srand((unsigned)time(NULL));  打开随机触发器 与时钟频率同步 
    SqList l;
    InitSqList(l,6,5);//默认加多一个内存
    int * testArray = RandomGenerate(6);
    int i;
    for (i = 0 ; i < 6; i++) {
        AppendSqList(l, testArray[i]);//默认从1号位开始加数字
    }
    printf("the original list:\\n");
    TraverseSqList(l);

      //排序模块:
         SqList l2;
    InitSqList(l2, 6, 5);//辅助顺序表
    MSort(l, l2, 0, 1, l.length);//因为是排顺序表l,所以传入的i是偶数0
    DestroySqList(l2);               
}    



void Merge(SqList & L1, SqList L2, int i, int m, int n) {//2路归并的归并操作,将L1的相邻有序区间i->m和m+1->n归并成i->n的有序序列储存到L2中。
    int j = i, k = m + 1, h = i;
    for (; j <= m, k <= n; h++) {
        if (L1.elem[j] <= L1.elem[k]) {//这个如果不加=就是不稳定的排序喔
            L2.elem[h] = L1.elem[j++];//哪个小就把哪个放过来
        }
        else {
            L2.elem[h] = L1.elem[k++];
        }
    }
    while (j <= m) {
        L2.elem[h++] = L1.elem[j++];
    }
    while (k <= n) { 
        L2.elem[h++] = L1.elem[k++];
    }
    
}


void MSort(SqList L1, SqList L2, int i, int s, int t) { //递归归并排序的递归排序操作。这里是对L1进行排序如果i为奇数,排序后的记录储存在L2否则存进L1里
    int m;
    if (s == t) {  //终结条件
        if (i%2 == 1) {//奇数的话
            L2.elem[s] = L1.elem[s];
        }
    }
    else {
        m = (s + t) / 2;
        MSort(L1, L2, i + 1, s, m);//对前半部分递归排序
        MSort(L1, L2, i + 1, m+1, t);//对后半部分递归排序
        if (i % 2 == 1) {
            Merge(L1, L2, s, m, t);
        }
        else {
            Merge(L2, L1, s, m, t);
        }
    }
}

 

 

 

贴一个Java实现归并排序的代码:

package www.com.leetcode.specificProblem;

public class MergeSort {
    
    /**
     * 归并排序的方法
     * 对a数组中的begin到end元素进行归并排序,如果i是奇数则排序后的数组合并到help数组中去(help[begin-end]有序);否则就存到a数组中去(a[begin-end]有序)
     * 之所以这样做是因为可以避免利用了辅助数组后还有复制元素到原来数组的操作;
     * 如果不这样操作,就应该是begin-mid还有mid+1-end先合并到help中,然后再把help中的begin-end的有序序列复制到a数组中去,覆盖a的begin-end的无序序列
     * @param a 要排序的数组
     * @param begin 要排序的第一个元素的index
     * @param end 要排序的最后一个元素的index
     * @param help 辅助数组
     * @param i i如果是奇数,那么合并的结果是放在help数组中的;如果i是偶数,合并的结果是放在a数组中的。所以如果a是一开始要排序的数组,也就是说最后的结果应该是
     * 合并到a数组中去的,所以应该一开始传一个偶数
     */
    public void mergeSort(int[] a, int begin, int end, int[] help, int i){
        if(begin == end) {
            if(i % 2 == 1) 
                help[begin] = a[begin];
            /*else 
                a[begin] = help[begin]; 
                //如果是偶数不用动拉,不然你想想第一次到这里,如果是偶数,不是把help中的0
                 * 覆盖了这里吗……
             */
            return;
        }

        int mid = (begin + end)/2;
        mergeSort(a, begin, mid, help, i + 1);//记得这个i+1
        mergeSort(a, mid + 1, end, help, i + 1);

        if(i % 2 == 0) {
            merge(a, begin, mid, end, help);
        } else {
            merge(help, begin, mid, end, a);
        }
    }


    /**
     * 将help数组中[begin-mid]还有[mid+1-end]两个序列合并在一起,合并的结果放在a数组中去
     * @param a 放合并结果的数组
     * @param begin 
     * @param mid
     * @param end
     * @param help
     */
    public void merge(int[] a, int begin, int mid, int end, int[] help) {
        int i = begin, j = mid + 1,k = begin;
        while(i <= mid && j <= end) {
            if(help[i] < help[j]) {
                a[k++] = help[i++];
            } else {
                a[k++] = help[j++];
            }
        }

        while(i <= mid) {
            a[k++] = help[i++];
        }
        while(j <= end) {
            a[k++] = help[j++];
        }
    }
    
    public static void main(String[] args) {
        int[] a = {3,2,5,12,55,1,34,23,11};
        int[] help = new int[a.length];
        
        MergeSort mergeSort = new MergeSort();
        mergeSort.mergeSort(a, 0, a.length - 1, help, 0);
        
        for(int temp : a) {
            System.out.print(temp + "  ");
        }System.out.println();
    }
}

 

tips:

1. 记住,控制主数组和help数组的流程是在sort方法中的,不是在合并方法中的,合并方法就只是做合并两个有序数组,sort决定哪个合并到哪里去。

2. sort流程递归结束标志是begin==end,记得虽然只有一个元素,但也要根据i来判断要不要把结果复制到help数组去!!!这个很重要!!

 

以上是关于归并排序的递归实现的主要内容,如果未能解决你的问题,请参考以下文章

[ 数据结构 -- 手撕排序算法第六篇 ] 归并排序(下)-- 非递归方法实现

排序3-选择排序与归并排序(递归实现+非递归实现)

javascript实现非递归--归并排序

归并排序算法的实现

归并排序算法的实现

手把手教你写归并排序算法 (Java代码)