小小c#算法题 - 8 - 归并排序 (Merging Sort)
“归并”的含义是将两个或两个以上的有序序列组合成一个新的有序序列。这个“归并”可以在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)的更多相关文章
- 小小c#算法题 - 9 - 基数排序 (Radix Sort)
基数排序和前几篇博客中写到的排序方法完全不同.前面几种排序方法主要是通过关键字间的比较和移动记录这两种操作来实现排序的,而实现基数排序不需要进行记录项间的比较.而是把关键字按一定规则分布在不同的区域, ...
- 小小c#算法题 - 7 - 堆排序 (Heap Sort)
在讨论堆排序之前,我们先来讨论一下另外一种排序算法——插入排序.插入排序的逻辑相当简单,先遍历一遍数组找到最小值,然后将这个最小值跟第一个元素交换.然后遍历第一个元素之后的n-1个元素,得到这n-1个 ...
- 归并排序(Merging Sort)
body, table{font-family: 微软雅黑; font-size: 13.5pt} table{border-collapse: collapse; border: solid gra ...
- 数据结构 - 归并排序(merging sort)
归并排序(merging sort): 包含2-路归并排序, 把数组拆分成两段, 使用递归, 将两个有序表合成一个新的有序表. 归并排序(merge sort)的时间复杂度是O(nlogn), 实际效 ...
- 小小c#算法题 - 11 - 二叉树的构造及先序遍历、中序遍历、后序遍历
在上一篇文章 小小c#算法题 - 10 - 求树的深度中,用到了树的数据结构,树型结构是一类重要的非线性数据结构,树是以分支关系定义的层次结构,是n(n>=0)个结点的有限集.但在那篇文章中,只 ...
- 数据结构 - 归并排序(merging sort) 具体解释 及 代码
归并排序(merging sort) 具体解释 及 代码 本文地址: http://blog.csdn.net/caroline_wendy 归并排序(merging sort): 包括2-路归并排序 ...
- 排序算法二:归并排序(Merge sort)
归并排序(Merge sort)用到了分治思想,即分-治-合三步,算法平均时间复杂度是O(nlgn). (一)算法实现 private void merge_sort(int[] array, int ...
- 小小c#算法题 - 6 - 快速排序 (QuickSort)
快速排序是排序算法中效率比较高的一种,也是面试常被问到的问题. 快速排序(Quick Sort)是对冒泡排序的一种改进.它的基本思想是,通过一趟排序将待排记录分割成独立的两部分,其中一部分记录的关键字 ...
- 小小c#算法题 - 12 - Joseph Circle(约瑟夫环)
约瑟夫环是一个数学的应用问题:已知n个人(以编号1,2,3...n分别表示)围坐在一张圆桌周围.从编号为k的人开始报数(从1开始报数),数到m的那个人出列:他的下一个人又从1开始报数,数到m的那个人又 ...
随机推荐
- BeetleX高性能通讯开源组件
net core高性能通讯开源组件BeetleX https://www.cnblogs.com/smark/p/9617682.html BeetleX beetleX是基于dotnet core实 ...
- LeetCode 688. Knight Probability in Chessboard
原题链接在这里:https://leetcode.com/problems/knight-probability-in-chessboard/description/ 题目: On an NxN ch ...
- hdu5542 The Battle of Chibi[DP+BIT]
求给定序列中长度为M的上升子序列个数.$N,M<=1000$. 很容易想到方法.$f[i,j]$表示以第$i$个数结尾,长度为$j$的满足要求子序列个数.于是转移也就写出来了$f[i][j]+= ...
- webpack 开发环境
当项目逐渐变大,webpack 的编译时间会变长,可以通过参数让编译的输出内容带有进度和颜色. $ webpack --progress --colors 如果不想每次修改模块后都重新编译,那么可以启 ...
- laravel的批量插入
在日常开发中,用到批量插入的操作还是挺多的.记得很早很早以前,我还是在循环中写sql插入,结果被项目经理按在地上摩擦.好吧,性能这东西,用不到的时候还好,万一性能成为瓶颈,那代码优化,数据库优化就首当 ...
- 怎么设置myeclipse的格式化键格式每行的字符长度
MyEclipse 格式化代码调整每行长度默认每行是80~对于咱初学者,非敏捷型编码者来说~有点短以至于Ctrl+Shift+F 格式化代码后总把一行拆成两行或者更多有点不适应 所以按个人爱好 更改他 ...
- Cassandra 学习二
Cassandra的架构 Cassandra的设计目的是处理跨多个节点的大数据工作负载,而没有任何单点故障.Cassandra在其节点之间具有对等分布式系统,并且数据分布在集群中的所有节点之间. 1 ...
- java ----获取路径的各种方法(总结)
Java Web开发中路径问题小结 (1) Web开发中路径的几个基本概念 假设在浏览器中访问了如下的页面,如图1所示: 那么针对这个站点的几个基本概念表述如下: 1. web站点的根目录:http: ...
- jdbcTemplate学习(二)
前面讲了增加.删除.更新操作,这节讲一下查询. 查询操作: (一)查询一个值(不需要注入参数) queryForObject(String sql, Class<T> requiredTy ...
- NT AUTHORITY\IUSR登录失败解决方法
NT AUTHORITY\IUSR登录失败解决方法 1 今天打开网站时,突然报这个错误,平时都好好的 Cannot open database "aslkm" requested ...