合并排序:归并排序

一、归并排序

1. 思想

如果有两个集合, listA={2,4,6,8}, listB={1,3,5,7}.

现在要将这两个数组合并成listC, 且必须保证listC中的数是有序的, 该咋弄呢?

第一步. 使listA, listB都变成有序的集合. 然后再合并的时候, 才会变得更好处理.

第二步. 合并. 只用比较listA, listB最左侧的数, 谁小就先放谁进listC, 放完之后, 还得把listA,listB中的对应的数删除掉.

循环执行第二步, 知道listA,listB中都不再有数据位置, listC就合并成功了.

这种方式, 是不是也挺好理解的? 而且, 速度应该不会慢哦.

但是, 这里还牵涉到一个问题, 就是对listA,listB集合的排序问题, 该怎么搞? 先对两个集合排序, 有没有搞错, 这不是搞了三个排序了?

listA,listB集合的排序, 我是用什么方式好? 快速排序? 真是麻烦事啊.

我不想对listA, listB再进行排序了, 太麻烦了, 我是来追求排序速度的, 不是来自找麻烦的. 

那如果listA, listB中, 都只有一个数, 他们是不是就不需要排序了呢? 当listA, listB合并之后, 他们肯定就是一个有序集合了吧. 

这里就是一个集合拆分成原子, 再将原子两两合并,再合并,再合并......的过程

     

 过程好像不复杂, 不知道实现起来怎么样.

///<summary>
/// 数组的划分
///</summary>
///<param name="array">待排序数组</param>
///<param name="temparray">临时存放数组</param>
///<param name="left">序列段的开始位置,</param>
///<param name="right">序列段的结束位置</param>
static void MergeSort(int[] array, int[] temparray, int left, int right)
{
    if (left < right)
    {
        //取分割位置
        int middle = (left + right) / 2;

        //递归划分数组左序列
        MergeSort(array, temparray, left, middle);

        //递归划分数组右序列
        MergeSort(array, temparray, middle + 1, right);

        //数组合并操作
        Merge(array, temparray, left, middle + 1, right);
    }
}

///<summary>
/// 数组的两两合并操作
///</summary>
///<param name="array">待排序数组</param>
///<param name="temparray">临时数组</param>
///<param name="left">第一个区间段开始位置</param>
///<param name="middle">第二个区间的开始位置</param>
///<param name="right">第二个区间段结束位置</param>
static void Merge(int[] array, int[] temparray, int left, int middle, int right)
{
    //左指针尾
    int leftEnd = middle - 1;

    //右指针头
    int rightStart = middle;

    //临时数组的下标
    int tempIndex = left;

    //数组合并后的length长度
    int tempLength = right - left + 1;

    //先循环两个区间段都没有结束的情况
    while ((left <= leftEnd) && (rightStart <= right))
    {
        //将两个区间数组中比较小的值存入临时数组, 一直到有一方数组结束
        if (array[left] < array[rightStart])
            temparray[tempIndex++] = array[left++];
        else
            temparray[tempIndex++] = array[rightStart++];
    }

    //判断左序列是否结束, 将没有结束的数据全部放入临时数组中
    while (left <= leftEnd)
        temparray[tempIndex++] = array[left++];

    //判断右序列是否结束, 将没有结束的数据全部放入临时数组中
    while (rightStart <= right)
        temparray[tempIndex++] = array[rightStart++];

    //将临时数组中的数据覆盖回去
    for (int i = 0; i < tempLength; i++)
    {
        array[right] = temparray[right];
        right--;
    }
}

 

二、快速排序 vs 归并排序 

static void Test()
{
    //五次比较
    for (int i = 1; i <= 5; i++)
    {
        List<int> list = new List<int>();
        int[] listA = new int[10000];
        //插入2k个随机数到数组中
        for (int j = 0; j < 10000; j++)
        {
            Thread.Sleep(1);
            list.Add(new Random((int)DateTime.Now.Ticks).Next(0, 100000));
        }

        listA = list.ToArray();
        Console.WriteLine("\n第" + i + "次比较:{0}...", string.Join(",", list.Take(10)));

        Stopwatch watch = new Stopwatch();
        watch.Start();
        var res = list.OrderBy(n => n).ToList();
        watch.Stop();
        Console.WriteLine("\n快速排序耗费时间:" + watch.ElapsedMilliseconds);
        Console.WriteLine("输出前是十个数:" + string.Join(",", res.Take(10).ToList()));

        watch.Restart();
        MergeSort(listA, new int[10000], 0, listA.Length - 1);
        watch.Stop();
        Console.WriteLine("\n归并排序耗费时间:" + watch.ElapsedMilliseconds);
        Console.WriteLine("输出前是十个数:" + string.Join(",", listA.Take(10).ToList()));
    }
}

 

从这里看归并排序还是很快的.

归并排序的时间复杂度都是O(nlog2n), 很稳定

 

 参考:

算法系列15天速成——第三天 七大经典排序【下】

 

posted @ 2017-04-06 13:29  Sniper_ZL  阅读(1557)  评论(1编辑  收藏  举报