代码改变世界

算法与数据结构——排序(七)归并排序

2012-11-07 08:13  左眼微笑右眼泪  阅读(330)  评论(0编辑  收藏  举报

      归并算法其实理解起来很容易,我们以前在学习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).