“归并”的含义是将两个或两个以上的有序序列组合成一个新的有序序列。这个“归并”可以在O(n+m)的数量级上实现,但这同时也需要O(n+m)的空间复杂度。具体为:首先分配一个新的长度为n+m的空序列,然后对于序列1(长度为n),序列2(长度为m),从每个序列的第一个元素开始比较,将较小的元素放入新的序列,然后较小元素原来所在序列的下标加1,这样一直比较下去,最终把这两个序列有序的放入新的序列中。在2-路归并排序中,这里的序列1,序列2表现为待排序列的两个相邻子序列。

2-路归并排序的定义:假设初始序列含有n个记录,则可看成是n个有序的子序列,每个子序列的长度为1,然后两两归并,得到n/2(上取整)个长度为2或1的有序子序列;再两两归并,......,如此重复,直至得到一个长度为n的有序序列为止,这种排序方法称为2-路归并排序。

那么可以看出,2-路归并排序中的核心操作是将一维数组中相邻的两个有序子序列归并为一个有序序列,代码如下:

传入一个数组,将这个数组中的两个有序子序列(low ~ middle, middle + 1 ~high)合并成一个有序的子序列。先把序列copy一份,用来后面比较元素使用。

        static void Merge(int[] numbers, int low, int middle, int high)
{
int[] temp = new int[numbers.Length];
numbers.CopyTo(temp, );
int index = low;
int m = middle + ;
while (low <= middle && m <= high)
{
if (temp[low] <= temp[m])
{
numbers[index++] = temp[low++];
}
else
{
numbers[index++] = temp[m++];
}
} // 比较循环结束后,左边(低位)子序列有剩余元素的情况
while (low <= middle)
{
numbers[index++] = temp[low++];
} // 比较循环结束后,右边(高位)子序列有剩余元素的情况
while (m <= high)
{
numbers[index++] = temp[m++];
}
}

上面核心操作的算法已经实现了,下面就是怎么通过调用上述算法来对序列进行排序了。这里采用了递归的思想,当对一个序列进行排序时,分别对其左,右子序列进行排序(一般从中间划分),左,右子序列有序后,调用Merger方法合并这两个子序列,得到整个的有序序列。递归停止的条件是序列长度只为1的时候。代码如下:

        static void MergeSort(int[] numbers, int low, int high)
{
// low == high时序列长度为1,是停止递归的条件
if (low != high)
{
int middle = (low + high) / ;
MergeSort(numbers, low, middle);
MergeSort(numbers, middle + , high);
Merge(numbers, low, middle, high);
}
}

为了方便调用,对MergeSort又封装一次:

        static void MSort(int[] numbers)
{
MergeSort(numbers, , numbers.Length - );
}

下面是对MergeSort方法的调用(这个例子使用控件台应用程序类型):

        static void Main(string[] args)
{
int[] numbers = { , , , , , , , , , , , , };
MSort(numbers); foreach (int i in numbers)
{
Console.Write(i.ToString() + " ");
}
}

归并排序是稳定的排序,Merge方法的时间复杂度为O(n), 要实现排序,需调用logn次Merge方法,所以归并排序的时间复杂度为O(n*logn)。由于排序时需要额外的临时序列的copy,所以空间复杂度为O(n),这也是归并排序的缺点。

一个可以优化的地方:

Merger方法中,每次都要copy一整份序列。但其实只要copy其中的要比较的两个子序列中的元素即可,所以有优化代码如下:

        static void Merge(int[] numbers, int low, int middle, int high)
{
int[] temp = new int[high - low + ];
for (int i = low, j = ; i <= high; i++)
{
temp[j++] = numbers[i];
} int index = low;
middle -= low;
high -= low;
low = ;
int m = middle + ;
while (low <= middle && m <= high)
{
if (temp[low] <= temp[m])
{
numbers[index++] = temp[low++];
}
else
{
numbers[index++] = temp[m++];
}
} while (low <= middle)
{
numbers[index++] = temp[low++];
} while (m <= high)
{
numbers[index++] = temp[m++];
}
}

小小c#算法题 - 8 - 归并排序 (Merging Sort)的更多相关文章

  1. 小小c#算法题 - 9 - 基数排序 (Radix Sort)

    基数排序和前几篇博客中写到的排序方法完全不同.前面几种排序方法主要是通过关键字间的比较和移动记录这两种操作来实现排序的,而实现基数排序不需要进行记录项间的比较.而是把关键字按一定规则分布在不同的区域, ...

  2. 小小c#算法题 - 7 - 堆排序 (Heap Sort)

    在讨论堆排序之前,我们先来讨论一下另外一种排序算法——插入排序.插入排序的逻辑相当简单,先遍历一遍数组找到最小值,然后将这个最小值跟第一个元素交换.然后遍历第一个元素之后的n-1个元素,得到这n-1个 ...

  3. 归并排序(Merging Sort)

    body, table{font-family: 微软雅黑; font-size: 13.5pt} table{border-collapse: collapse; border: solid gra ...

  4. 数据结构 - 归并排序(merging sort)

    归并排序(merging sort): 包含2-路归并排序, 把数组拆分成两段, 使用递归, 将两个有序表合成一个新的有序表. 归并排序(merge sort)的时间复杂度是O(nlogn), 实际效 ...

  5. 小小c#算法题 - 11 - 二叉树的构造及先序遍历、中序遍历、后序遍历

    在上一篇文章 小小c#算法题 - 10 - 求树的深度中,用到了树的数据结构,树型结构是一类重要的非线性数据结构,树是以分支关系定义的层次结构,是n(n>=0)个结点的有限集.但在那篇文章中,只 ...

  6. 数据结构 - 归并排序(merging sort) 具体解释 及 代码

    归并排序(merging sort) 具体解释 及 代码 本文地址: http://blog.csdn.net/caroline_wendy 归并排序(merging sort): 包括2-路归并排序 ...

  7. 排序算法二:归并排序(Merge sort)

    归并排序(Merge sort)用到了分治思想,即分-治-合三步,算法平均时间复杂度是O(nlgn). (一)算法实现 private void merge_sort(int[] array, int ...

  8. 小小c#算法题 - 6 - 快速排序 (QuickSort)

    快速排序是排序算法中效率比较高的一种,也是面试常被问到的问题. 快速排序(Quick Sort)是对冒泡排序的一种改进.它的基本思想是,通过一趟排序将待排记录分割成独立的两部分,其中一部分记录的关键字 ...

  9. 小小c#算法题 - 12 - Joseph Circle(约瑟夫环)

    约瑟夫环是一个数学的应用问题:已知n个人(以编号1,2,3...n分别表示)围坐在一张圆桌周围.从编号为k的人开始报数(从1开始报数),数到m的那个人出列:他的下一个人又从1开始报数,数到m的那个人又 ...

随机推荐

  1. andriod&linux&c函数原型

    1.dlopen 功能:打开一个动态链接库,并返回动态链接库的句柄 包含头文件: #include <dlfcn.h> 函数定义: void * dlopen( const char * ...

  2. asp.net 打印控件使用方法

    打印的效果及控制性虽然不是很好,但是也能勉强使用,应付一般的打印还是 可以的了.代码如下所示: 代码 复制代码 代码如下: //调用PrintControl.ExecWB(?,?)实现直接打印和打印预 ...

  3. Sentry深入

    Sentry的架构 内部架构 核心就是规则引擎以及Metadata Store:记录格式有两种,一种policy file记录授权内容,另外一种是通过命令方式进行授权:前者记录在策略文件中,保存形式是 ...

  4. HTML(超文本标记语言)

    学习地址:https://developer.mozilla.org/zh-CN/docs/Web/Html

  5. walle部署系统的使用

    在项目开发的时候要管理各种开发 测试 线上环境的代码 部署 回滚等操作 这里可以使用walle walle官网:http://www.walle-web.io/ 学习安装:https://blog.c ...

  6. python-snappy的安装小记

    在弄个dota2的replay parser玩玩,在github上找到了几个,都是基于V社的demoinfo2(https://developer.valvesoftware.com/wiki/Dot ...

  7. [转]ubuntu11.04配置nfs--解决mount.nfs: access denied问题

    总算通过了nfs的localhost测试. 配置很简单,下面摘自网络,并且整理下: 1 安装nfs #apt-get install nfs-kernel-server #apt-get instal ...

  8. Spring线程池由浅入深的3个示例

    作者博客主页:http://blog.csdn.net/chszs 本文提供了三个Spring多线程开发的例子,由浅入深,由于例子一目了然,所以并未做过多的解释.诸位一看便知. 前提条件: 1)在Ec ...

  9. .netcore 在linux 上运行 Helllo World 例子

    要想在linux上运行netcore ,需要在linux 安装netcore SKD 下面分两步完成, 首先我的操作系统是 Ubuntu 14.04 ,不同版本可以参考 官网给出的连接 https:/ ...

  10. unittest添加测试用例方法

    1. suite=unittest.TestLoader().loadTestsFromTestCase(changedTestHJ)unittest.TextTestRunner(verbosity ...