zoukankan      html  css  js  c++  java
  • 8.5 归并排序

    “归并”一词的中文含义就是合并、并入的意思,归并排序(Merge)是将两个(或两个以上)有序表合并成一个新的有序表,即把待排序序列分为若干个子序列,每个子序列是有序的。然后再把有序子序列合并为整体有序序列。

    归并排序是建立在归并操作上的一种有效的排序算法。该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。 将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为2-路归并。

    归并排序算法稳定,数组需要O(n)的额外空间,链表需要O(log(n))的额外空间,时间复杂度为O(nlog(n)),算法不是自适应的,不需要对数据的随机读取。

    归并排序的步骤是:

    1、对于分成两部分的数组,先左侧部分排好序,再右侧部分排好序,然后准备一个辅助数组;

    2、用外排的方式,哪边小就将谁填进辅助数组;

    3、当其中的一个数组转移完毕后,即指针指向的最后一个元素也进入辅助数组了,将另一个数组的剩余元素全部移入辅助数组。

         例如:对于数组(5,2,0,1,4,3),我们将其分成两部分(5,2,0)和(1,4,3),两个数组分别进行排序为(0,2,5)和(1,3,4),index1指向数组1中的元素“0”,index2指向数组2中的元素“1”,此时index1指向的数组比较小,故“0”元素进入辅助数组,index1向后移动一位指向数组1中的元素“2”,再将index1和index2指向的元素进行比较,较小的进入辅助数组,当其中的一个数组转移结束后(在这里是数组2),将另一个数组(这里是数组1)剩下的元素全部移进辅助数组。

    对于它的时间复杂度:

    由于归并排序是递归过程,因此可以使用master公式计算复杂度

    master公式:
    T(N) = a*T(N/b) + O(N^d)
    1) log(b,a) > d -> 复杂度为O(N^log(b,a))
    2) log(b,a) = d -> 复杂度为O(N^d * logN)
    3) log(b,a) < d -> 复杂度为O(N^d)

    在这里,将数组均分成2份,左侧部分排好序,右侧部分排好序,数据量减半,故b = 2,发生了两回,故a = 2,两侧都排好序之后,进行外排,两个下标都要动,一共滑过了n

    个数,然后将这些数放进辅助数组,故这里的d = 1 。所以,最后归并排序的复杂度是 2 * log(N/2)+ O(N) 。

     用php实现该算法:

    <?php
    header("content-type:text/html;charset=utf-8");
    /*
     * 归并法排序:
     * master公式的使用
     * T(N) = a*T(N/b) + O(N^d)
     *  1) log(b,a) > d -> 复杂度为O(N^log(b,a))
     *  2) log(b,a) = d -> 复杂度为O(N^d * logN)
     *  3) log(b,a) < d -> 复杂度为O(N^d)
     *
     * 时间复杂度:O(N * logN);额外空间复杂度(help辅助数组)O(N)
     * */
    
    function mergeSort(&$arr){
        if ($arr == null || count($arr) < 2) {
            return;
        }
        sortProcess($arr,0,count($arr) - 1);  //对0到终点的数据进行排序
    }
    
    //函数功能:执行排序递归过程
    function sortProcess(&$arr,$l,$r){
        if ($l == $r) {                    //这个范围只有一个数,表明已经排好了
            return true;
        }
    
        $mid = floor(($l + $r)/2);         //求中点的位置
    
        sortProcess($arr,$l,$mid);        //左部分进行排序, T(N/2)
        sortProcess($arr,$mid+1,$r);      //右部分进行排序, T(N/2)
        merge($arr,$l,$mid,$r);               //整体归并排序,传参的意义:从l到min已经排好序,mid+1到r已经排好序, O(N)
        //T(N) = 2T(N/2) + O(N)
        //根据master公式O(N^d * logN),复杂度为O(N * logN)
    }
    
    //函数功能:从l到min已经排好序,mid+1到r已经排好序,如何让它整体有序
    function merge(&$arr,$l,$mid,$r){
        $help = array();                  //初始化辅助数组
        $i = 0;                            //合并数组的索引
        $p1 = $l;                          //p1指向左边数组的起始位置,相当于index1
        $p2 = $mid + 1;                    //p2指向右边数组的起始位置,相当于index2
        while($p1<=$mid && $p2<=$r){      //这是谁小填谁的过程
            $help[$i++] = $arr[$p1] < $arr[$p2] ? $arr[$p1++] : $arr[$p2++]; //谁小填谁的过程中还伴随着指针的右移
        }
        //两个必有且只有一个越界,即使这两个while是顺序执行的,但是只会执行一个
        while($p1<=$mid){                //p2越界,p1数组还有值,那就要把p1剩下的拷贝进辅助数组,此时不会执行下一个while循环
            $help[$i++] = $arr[$p1++];
        }
    
        while($p2<=$r){                //进入这个while的时候,前一个while肯定没有执行,p1越界,p2数组还有值,那就要把p2剩下的拷贝进辅助数组
            $help[$i++] = $arr[$p2++];
        }
    
        for($i = 0;$i<count($help);$i++){ //把辅助数组的数据全部填进原数组
            $arr[$l+$i] = $help[$i];
        }
    }
    
    $arr = [2,33,45,22,64,67,12,1,0,9];
    mergeSort($arr);
    print_r($arr);
  • 相关阅读:
    20210603
    20210602
    20210601
    20210531-已编辑
    2021053101
    操作系统笔记(一)
    尘埃落定,扬帆起航
    RTL级低功耗设计
    关于毛刺
    电路级拾珍
  • 原文地址:https://www.cnblogs.com/xlzfdddd/p/10478258.html
Copyright © 2011-2022 走看看