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

    一、归并排序

    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("" + i + "次比较:{0}...", string.Join(",", list.Take(10)));
    
            Stopwatch watch = new Stopwatch();
            watch.Start();
            var res = list.OrderBy(n => n).ToList();
            watch.Stop();
            Console.WriteLine("
    快速排序耗费时间:" + 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("
    归并排序耗费时间:" + watch.ElapsedMilliseconds);
            Console.WriteLine("输出前是十个数:" + string.Join(",", listA.Take(10).ToList()));
        }
    }

     

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

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

     参考:

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

  • 相关阅读:
    正向代理和反向代理
    轮询和长轮询
    偏函数 方法与函数的区别
    pipreqs 生成项目依赖的第三方包
    git安装与使用
    自动生成接口文档
    上线
    Android APK加固-完善内存dex
    Android APK加固-内存加载dex
    替换ClassLoader
  • 原文地址:https://www.cnblogs.com/elvinle/p/6673004.html
Copyright © 2011-2022 走看看