zoukankan      html  css  js  c++  java
  • 算法与数据结构——排序(七)归并排序

          归并算法其实理解起来很容易,我们以前在学习C++的时候,肯定遇到过这样一道题目,给两个有序的列表,请把他们合并成一个列表,并且有序。相信叫大家写一个函数实现这个功能并不难。但是我们当时怎么没有想到,用这个思想,可以发明出一种排序的算法出来呢,它就是我们要学习的归并排序。

          归并排序的原理就是,把一个序列拆分成两个小的序列,把这两个 小的序列排好序好,然后再把它们合并起来。对于每个小的序列,我们又可以进行拆分成更小的两个序列,把更小的两个序列排序好后,合并起来。对于更小的序列,我们又可以进行拆分,一直这样拆分,直到拆分到每个序列里面只有一个数的时候,这个时候,就把这个数当作一个列表,跟另外一个列表实现合并,并有序。

    image

          比如我们要排序的序列有10个数,我们可以把这10个数当成10个序列,对每两个序列进行排序并合并成一个序列,这时候,就有了5个序列,然后再把这一个序列里面的两两排序合并,一直到最后合并成了一个序列,那么这个序列也就变成了有序的了。

    image

            上面的就是归并排序的基本原理示意图.最重要的一步就是给你两个有序的列表,把它合并成一个有序的列表,这个方法实现如下:

    /// <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 void MSort(List<int > sortList, List<int > resultList,int start,int end)
    {
        List<int> tempList1 = new List<int>();
        List<int> tempList2 = new List<int>();
        int mid = (start + end) / 2;
     
        if (start == end)
        {
            resultList.Add(sortList[start]);
        }
        else
        {
            MSort(sortList,  tempList1, start, mid);//递归调用,把SortList里面,第start到mid之间的数,进行归并,结果放在tempList1里面
            MSort(sortList,  tempList2, mid+1, end);//递归调用,把SortList里面,第mid+1到end之间的数,进行归并,结果放在tempList2里面
            //resultList = Merge(tempList1, tempList2);//不能这样调用,
            Merge(tempList1, tempList2, resultList);//把TempList1和TempList2中的数合并到resultList中去
        }
     
    }

          最后实现的排序方法是:

    public List<int> SortByMerge(List<int> sortList)
    {
        List<int> resultList=new List<int>();
        MSort(sortList, resultList, 0, sortList.Count - 1);
        return resultList;
    }
    最后我们来看看归并排序的时间复杂度,由完全二叉树的深度可以知道,归并排序需要进行[log2n次],每趟排序还需要把序列中的每一个元素归并到另外一个序列中去,耗费时间复杂度是O(n),所以总的时间复杂度是O(nlog2n).
  • 相关阅读:
    MySql cmd下的学习笔记 —— 引擎和事务(engine,transaction)
    MySql cmd下的学习笔记 —— 有关视图的操作(algorithm)
    MySql cmd下的学习笔记 —— 有关视图的操作(建立表)
    MySql cmd下的学习笔记 —— 有关常用函数的介绍(数学函数,聚合函数等等)
    MySql cmd下的学习笔记 —— 有关多表查询的操作(多表查询练习题及union操作)
    MySql 在cmd下的学习笔记 —— 有关多表查询的操作(内连接,外连接,交叉连接)
    MySql cmd下的学习笔记 —— 有关子查询的操作(where型,from型,exists型子查询)
    MySql cmd下的学习笔记 —— 有关select的操作(order by,limit)
    剑指Offer--第21题 调整数组顺序使奇数位于偶数前面;
    剑指Offer--和为s的连续正数序列
  • 原文地址:https://www.cnblogs.com/xiaoxiangfeizi/p/2758096.html
Copyright © 2011-2022 走看看