算法手记(6)归并排序

今天我主要学习基于分治思想的归并排序算法,这是分治法的典型应用。废话不多说,下面直切正题。

概述:

将两个有序数组归并成一个更大的有序数组,我们称之为归并,人们根据这一操作发明了一种简单的递归排序算法:归并排序。

归并排序最吸引人的是它能够保证任意长度为N的数组排序所需的时间和NlogN成正比;它的主要缺点是需要额外占用的内存空间与N成正比。

分析:
实现归并的一种最简单的方法是将两个不同的有序数组归并到第三个数组中,实现的方法很简单,创建一个适当的数组然后将两个数组中的元素一个个从小到大放入这个数组。但这样带来的问题是排序大数组时,需要多次归并,每次归并都创建新的数组,这无疑会带来很大的问题。因此,我们需要一种能够实现原地归并的方法,这样就可以先将左边排序,再将右边排序,然后在数组中移动元素而不需要额外的内存空间,我们将在实现部分描述这种方法。

实现:
原地归并方法:

private static void merge(IComparable[] a, int lo, int mid, int hi)
        {
            int i = lo, j = mid + 1;
            for (int k = lo; k <= hi; k++)
                aux[k] = a[k];
            for (int k = lo; k <= hi; k++)
            {
                if (i > mid)                       a[k] = aux[j++];
                else if (j > hi)                   a[k] = aux[i++];
                else if (less(aux[j], aux[i]))     a[k] = aux[j++];
                else                               a[k] = aux[i++];
            }
        }

下面分别是两种不同的最终实现算法,它们都是应用高效算法设计中分治思想的典型例子。

自顶向下的归并实现:

public class Merge
    {
        private static IComparable[] aux;
        public static void sort(IComparable[] a)
        {
            aux = new IComparable[a.Length];
            sort(a, 0, a.Length - 1);
        }
        private static void sort(IComparable[] a,int lo,int hi)
        {
            if (hi <= lo) return;
            int mid = lo + (hi - lo) / 2;
            sort(a, lo, mid);
            sort(a, mid + 1, hi);
            merge(a, lo, mid, hi);
        }
        public static bool less(IComparable v, IComparable w)
        {
            return v.CompareTo(w) < 0;
        }
        public static void exch(IComparable[] a, int i, int j)
        {
            IComparable temp = a[i];
            a[i] = a[j];
            a[j] = temp;
        }
        public static void show(IComparable[] a)
        {
            int N=a.Length;
            for (int i = 0; i < N; i++)
                Console.WriteLine(a[i]);
        }
        private static void merge(IComparable[] a, int lo, int mid, int hi)
        {
            int i = lo, j = mid + 1;
            for (int k = lo; k <= hi; k++)
                aux[k] = a[k];
            for (int k = lo; k <= hi; k++)
            {
                if (i > mid)                       a[k] = aux[j++];
                else if (j > hi)                   a[k] = aux[i++];
                else if (less(aux[j], aux[i]))     a[k] = aux[j++];
                else                               a[k] = aux[i++];
            }
        }
        public static void test(int size)
        {
            DateTime startDate = DateTime.Now;
            Data[] data = new Data[size];
            Random rd = new Random();
            for (int i = 0; i < size; i++)
            {
                data[i] = new Data { value = rd.Next(10000000) };
            }
            Console.WriteLine("After sort:");
            Merge.sort(data);
            DateTime endDate = DateTime.Now;
            double time = (endDate.Hour - startDate.Hour) * 3600 * 1000 + (endDate.Minute - startDate.Minute) * 60 * 1000 + (endDate.Second - startDate.Second) * 1000 + (endDate.Millisecond - startDate.Millisecond);
            if (time > 1000) time /= 1000;
            Console.WriteLine("time:" + time);
        }
        public static void Main(string[] args)
        {
            test(1000);
        }
    }

自底向上的归并排序:

 public static void sort(IComparable[] a)
        {
            int N = a.Length;
            aux = new IComparable[N];
            for(int sz=1;sz<N;sz++)
                for(int lo=0;lo<N-sz;lo+=sz+sz)
                    merge(a,lo,lo+sz-1,System.Math.Min(lo+sz+sz-1,N-1));
            //sort(a, 0, a.Length - 1);
        }

用自顶向下或者自底向上实现分治类算法都很自然。归并排序算法的实现说明,当能够用一种方式解决一个问题时,也应该试试另一种方法。其中自底向上的方式适合于组织链表结构,能够将链表原地排序。

实际上,我目前对归并算法理解的并不深,但是其代码精炼程度和执行速度很让我惊叹。我将它和希尔排序及.NET封装的Array.Sort()进行对比试验后,发现归并排序速度和原生API速度最接近,排序10000000个随机数只需要15.672s,希尔排序需要53.937s,原生方法需要15.062s.如果大家能有讲得好的相关文章,欢迎推荐给我,不胜感激ing。

时间: 2024-12-24 13:41:40

算法手记(6)归并排序的相关文章

疯狂的Java算法——插入排序,归并排序以及并行归并排序

从古至今的难题 在IT届有一道百算不厌其烦的题,俗称排序.不管是你参加BAT等高端笔试,亦或是藏匿于街头小巷的草根笔试,都会经常见到这样一道百年难得一解的问题. 今天LZ有幸与各位分享一下算法届的草根明星,排序届的领衔大神——插入排序以及归并排序.最后,在头脑风暴下,LZ又有幸认识了一位新朋友,名叫并行归并排序.接下来,咱们就一一认识一下,并且在最后来一次“算林大会”吧. 插入排序简介 插入排序,算林称最亲民的排序算法,插入排序采用最简单的插入方式对一个整数数组进行排序.它循环数组中从第二个开始

【数据结构与算法】二路归并排序

二路归并排序的时间复杂度是O(n*log2n),空间复杂度是O(n). 代码如下: /** * 源码名称:MergeSort.java * 日期:2014-08-11 * 程序功能:合并排序 * 版权:[email protected] * 作者:A2BGeek */ public class MergeSort { public void mergeSort(int[] in) { int length = in.length; int tmp[] = new int[length]; mer

白话经典算法系列之五 归并排序的实现(转)

归并排序是建立在归并操作上的一种有效的排序算法.该算法是采用分治法(Divide and Conquer)的一个非常典型的应用. 首先考虑下如何将将二个有序数列合并.这个非常简单,只要从比较二个数列的第一个数,谁小就先取谁,取了后就在对应数列中删除这个数.然后再进行比较,如果有数列为空,那直接将另一个数列的数据依次取出即可. //将有序数组a[]和b[]合并到c[]中 void MemeryArray(int a[], int n, int b[], int m, int c[]) { int

算法笔记之归并排序

4.归并排序 4.1算法思想-- 将数组分为两半,对每部分递归地应用归并排序,直到最后的子数组只包含一个元素.在每部分都排好序后,对它们进行合并. 4.2 时间复杂度-- 假如用T(n)表示使用归并排序对n个元素构成的数组进行排序而使用的时间,用mergeTime来表示将两个子分组合并起来而花费的时间.那么 T(n) = T(n/2)+T(n/2) + mergetime 而megeTime就是归并两个子数组所耗费的时间,以最大时间来算,最多需要n-1次来比较两个子数组的元素,然后n次移动到临时

算法入门之归并排序(自顶向下方法)

归并排序原理: 归并排序用到的是分治思想,即把一个大问题分成两个小问题,然后把一个小问题再分为两个更小的小问题,从最小的问题开始解决,然后把小问题的结果进行整合,最终解决大问题,这种思想是自顶向下的方法,特点是先进行递归,最终进行排序,在之后的快速排序中可以看到,快速排序特点是先进行排序,后进行递归 算法实现如下: function less($m, $n) { return $m < $n; } function merge(&$a, $lo, $mid, $hi) { $i = $lo;

Hark的数据结构与算法练习之归并排序

算法说明: 归并排序的思路就是分而治之,将数组中的数字递归折半进行排序. 递归到最底层就只剩下有两个数字进行比较,再从底层往下进行排序合并.最终得出结果. 同样,语言描述可能对于不知道这个算法的人来说,理解的比较吃力,所以还是举个例子来简单说明一下. 首先,测试数据是int[] arrayData = { 5, 9, 6, 7, 4, 1, 2, 3, 8 }; 一共是9个元素. 然后拿visio画图,来对于归并排序的分而治之进行一下简单的剖析. 整体排序流程大概就是如上图了. 首先先是递归拆分

图解排序算法(四)之归并排序

基本思想 归并排序(MERGE-SORT)是利用归并的思想实现的排序方法,该算法采用经典的分治(divide-and-conquer)策略(分治法将问题分(divide)成一些小的问题然后递归求解,而治(conquer)的阶段则将分的阶段得到的各答案"修补"在一起,即分而治之). 分而治之 可以看到这种结构很像一棵完全二叉树,本文的归并排序我们采用递归去实现(也可采用迭代的方式去实现).分阶段可以理解为就是递归拆分子序列的过程,递归深度为log2n. 合并相邻有序子序列 再来看看治阶段

【算法导论】归并排序

递归与分治 许多有用的算法在结构上是递归的:为了解决一个给定的问题,算法一次或多次递归地调用其自身以解决紧密相关的若干子问题.这些算法典型地遵循分治法的思想:将原问题分解为几个规模较小但类似于原问题的子问题,递归地求解这些子问题,然后再合并这些子问题的解来建立原问题的解. 分治模式在每层递归时都有三个步骤: 分解原问题为若干子问题,这些子问题是原问题的规模较小的实例. 解决这些子问题,递归地求解各子问题.然而,若子问题的规模足够小,则直接求解. 合并这些子问题的解成原问题的解. 归并排序 归并排

算法三之归并排序

快速排序,应用到分治法. 下面先了解一下什么是分治法? 分治法,顾名思义,分而治之.先将问题进行分解,然后将分离的问题进行求解,最后将所有分离的解进行合并,得到最终解. 分治法,“大事化小,小事化了,了后一合,大事得解”嗯哪,就是这样.... 那么了解了分治法以后,再来解决问题,归并排序.(其实算法中有那么多排序一直搞不清楚,也分不清,他怎么就叫这个名字,虽然知道了名字,但是还是不知道是怎样解决问题的,怎么解决这个问题呢?这是费解.....还没有想出来很好 的办法来记住.只好先死记硬背了) 好了