美文网首页
常见排序算法(5)--归并排序(递归版)

常见排序算法(5)--归并排序(递归版)

作者: Jack_deng | 来源:发表于2019-05-18 14:39 被阅读0次

    基本思想

    归并排序(MERGE-SORT)是利用归并的思想实现的排序方法,该算法采用经典的分治(divide-and-conquer)策略(分治法将问题分(divide)成一些小的问题然后递归求解,而治(conquer)的阶段则将分的阶段得到的各答案"修补"在一起,即分而治之)。

    分而治之

    可以看到这种结构很像一棵完全二叉树,本文的归并排序我们采用递归去实现(也可采用迭代的方式去实现)。分阶段可以理解为就是递归拆分子序列的过程,递归深度为log2n。

    合并相邻有序子序列

    再来看看阶段,我们需要将两个已经有序的子序列合并成一个有序序列,比如上图中的最后一次合并,要将[4,5,7,8]和[1,2,3,6]两个已经有序的子序列,合并为最终序列[1,2,3,4,5,6,7,8],来看下实现步骤。


    "治" 代码实现

    图看完了,现在来看代码吧。

    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++];
        }
    }
    

    这个函数有点难理解,我们还是拿上图的具体例子来说吧。执行merge(arr, 0, 4, 8, temp),其实就是把{4,5,7,8}(暂时称为左子数组)和{1,2,3,6}(右子数组)两个已经有序的子序列,合并为最终序列{1,2,3,4,5,6,7,8}。先比较左右子数组的第一个元素的大小,把较小的存到临时数组的第一个元素,然后把对应的子数组的指针加1,继续比较。直到左右子数组中某一个子数组完全被复制到了temp中,接下来的while循环做的事情就是把剩下的子数组中的剩余元素都复制进temp。由于不知道是左子数组还是右子数组剩余元素,所以都要写一遍。最后把temp数组的元素拷贝到arr中,arr就排序好了。

    "分" 代码实现
    void MSort(int *arr, int left, int right, int *temp)
    {
        // if (left = right) return; 
        if (left < right)
        {
            int mid = (left + right) / 2;
            MSort(arr, left, mid, temp);//左边归并排序,使得左子序列有序
            MSort(arr, mid+1, right, temp);//右边归并排序,使得右子序列有序
            merge(arr, left, mid, right, temp);//将两个有序子数组合并操作
        }
    }
    

    "分"采用了递归的方法,采用了折半的策略,先归并左子数组,再归并右子数组,再把左右子数组合并。有的读者可能觉得MSort函数没有终止条件,其实它是有的,隐含的条件是这个if (left = right) return;,因为当left = right时,其实只有一个元素,肯定是有序的。

    归并排序递归版 代码实现
    // 归并排序递归版
    void MergeSort(int *arr, int length)
    {
        int *temp = (int *)malloc(sizeof(int) * length);//在排序前,先建好一个长度等于原数组长度的临时数组,避免递归中频繁开辟空间
        MSort(arr, 0, length-1, temp);
        free(temp);
    }
    

    归并排序是稳定排序,它也是一种十分高效的排序,能利用完全二叉树特性的排序一般性能都不会太差。从上文的图中可看出,每次合并操作的平均时间复杂度为O(n),而完全二叉树的深度为|log2n|。总的平均时间复杂度为O(nlogn)。而且,归并排序的最好,最坏,平均时间复杂度均为O(nlogn)。

    相关文章

      网友评论

          本文标题:常见排序算法(5)--归并排序(递归版)

          本文链接:https://www.haomeiwen.com/subject/mvanaqtx.html