• 算法与数据结构——排序(八)归并排序的非递归实现


         前面我们用递归的方法实现了归并排序,递归比较占用内存空间,能不能用非递归的方法来实现归并排序呢。答案当然是可以的。

         使用非递归排序的思路如下(以序列{0,4,8,9,7,1,3,9,2}为例):

    image

    1.首先s=1,把数组中数两两归并,并放入一个新数组中(一般就拿它本身的那个数组来放),如果最后多一个数(例如最后一个数字2单独为一组),那么直接把这个数加入到新数组中去。这步完成后,每相邻的两个数内部是排好了序的,在这一步里面,会对9个序列(每个数是一个序列)进行归并,最终得到5个序列。

    2.第二步s=2,把第1到第4个数,第5到第8个数进行归并,最后一个数字2又是单独的一个序列,不参与归并。第二步完成后,从第一个数开始,每相邻的4个数是排好序了的,在这一步里面会对5个序列(每2个数一个序列,最后一个数单独一个序列)进行归并,最终得到3个序列。

    3.第三步s=4,把第1到8个数进行归并,最后一个数字2又是单独的一个序列,归并完成后,前面8个数内部是有序的,这一步里面会对3个序列(每4个数一个序列,最后一个数单独一个序列)进行归并,最终得到2个序列。

    4第四步s=8,把第1到16个数进行归并,这时候最后一个数字2就参与到归并中去了,归并完成后,整个序列就都是有序的了,这一步里面会对两个序列(每8个数一个序列,最后一个数单独一个序列)进行归并,最终得到一个序列。

          下面看具体的代码实现:

          首先是通用的合并两个有序list的方法:

    /// <summary>
    /// 把两个有序的序列A和B合并到一个有序列temp里面
    /// </summary>
    /// <param name="sortListA">有序序列A</param>
    /// <param name="sortListB">有序序列B</param>
    /// <param name="tempList">有序序列Temp</param>
    public void Merge(List<int> sortListA, List<int> sortListB, List<int> tempList)
    {
        int countB = 0;
        int countA = 0;
        //1.以某个序列为原始序列,把它的每一个数,与另外的一个序列做比较
        for (int i = 0; i < sortListA.Count; i++)
        {
            //1.1循环遍历序列B的每一个序列(因为B序列是有序列的,所以第一个元素是最小的)
            for (int j = countB; j < sortListB.Count; j++)
            {
                //1.2如果A序列中的某个元素比B中的小,就把A中的这个元素加到temp中去
                if (sortListA[i] < sortListB[j])
                {
                    tempList.Add(sortListA[i]);
                    countA++;//记录加到temp中去的A序列中元素的数量 
                    break;
                }
                    //1.2否则把B中的元素加到temp中去
                else
                {
                    tempList.Add(sortListB[j]);
                    countB++;//记录加到temp中去的B序列中元素的数量 
                }
            }
        }//注意,此for循环结束后,一定有一个序列中的元素全部加到了temp中去了。
     
        //如果此处不相等,代表B中的元素还没有加完,那么把B中剩下的元素加入到Temp中去
        if (countB != sortListB.Count)
        {
            for (int k = countB; k < sortListB.Count; k++)
            {
                tempList.Add(sortListB[k]);
            }
        }
        //否则的话,代表A中的元素还没有加完,那么把A中剩下的元素加入到Temp中去
        else
        {
            for (int k = countA; k < sortListA.Count; k++)
            {
                tempList.Add(sortListA[k]);
            }
        }
    }

        然后来看主方法,在这个方法里面采用循环,实现上面的四步归并:

    public List<int> SortByMergeNew(List<int> sortList)
    {
        List<int> resultList=new List<int>();
        int i = 1;
        while (i<sortList.Count)
        {
            resultList.Clear();
            MergePass(sortList,resultList,i,sortList.Count);
            i = 2*i;
            sortList.Clear();
            MergePass(resultList, sortList, i, resultList.Count);
            i = 2*i;
        }
        return sortList;
    }

       里面调用了MergePass方法,这个方法的作用就是具体实现上面的每一步,在这个例子里面,这个方法会被调用4遍。

    public void MergePass(List<int> sortList,List<int> resultList,int s,int listLength )
    {
        int i = 0;
        int sumCount = listLength - 2*s;
     
        while (i<=sumCount)
        {
            MergeList(sortList, resultList, i, i + s, i + 2 * s);
            i = i + 2*s;//i每次要增加2s
        }
        if (i < listLength - s)
        {
            //如果有单数的话,把最后的两个序列归并,(如果有9个数的话,这句话就是把前面8个数与最后一个数归并)
            MergeList(sortList, resultList, i, i + s, listLength);
        }
        else//如果后面有剩余的数,则追加到数组中,如果没有,则不追加
        {
            for (int k = i; k < listLength; k++)
            {
                resultList.Add(sortList[k]);
            }
        }
    }

       在上面的4个步骤里面,每一步,实现上是对多对序列进行归并的,每对序列的归并是通过MergeList进行实现的,MergeList的作用是产生每个序列,产生后调用最前面的那个Merge方法来实现真正的归并的。

    private void MergeList(List<int> sortList, List<int> resultList, int start, int mid,int end)
    {
        var listA = new List<int>();
        var listB = new List<int>();
     
        //如果s等于1的时候,下面的代码是把第0,1//4,5,//8,9装入到一个List中去
        for (int j = start; j < mid; j++)
        {
            listA.Add(sortList[j]);
        }
        //如果s等于1的时候,下面的代码是把第2,3//6,7装入到一个List中去
        for (int k = mid; k < end; k++)
        {
            listB.Add(sortList[k]);
        }
        Merge(listA, listB, resultList);//把两个List进行合并
    }

       通过非递归的方法来实现归并排序,减少了空间的复杂度,空间复杂度为O(n),在时间性能上也有一定的提升。

  • 相关阅读:
    CodeForces 745C Hongcow Builds A Nation 并查集
    hdu 1542 Atlantis 矩形面积并
    CodeForces 741C Arpa’s overnight party and Mehrdad’s silent entering
    上海五校联赛 H 调和序列
    C++学习笔记之泛型算法
    hdu 6016 Count the Sheep
    操作系统 银行家算法
    计蒜之道复赛 B D F
    hdu 2966 In case of failure kdtree模板题
    poj 3468 A Simple Problem with Integers 降维线段树
  • 原文地址:https://www.cnblogs.com/xiaoxiangfeizi/p/2775984.html
Copyright © 2020-2023  润新知