归并排序:二路归并

归并排序(Merge
Sort)是将两个(或两个以上)有序表合并成一个新的有序表,即把待排序序列分为若干个有序的子序列,再把有序的子序列合并为整体有序序列。

归并排序的具体做法:

  1. 把原序列不断地递归等分,直至每等份只有一个元素,此时每等份都是有序的。
  2. 相邻等份合并,不断合并,直至合并完全。

二路归并

归并排序是建立在归并操作上的一种有效的排序算法。该算法是采用分治法(Divide
and Conquer)的一个非常典型的应用。归并排序最常用的是二路归并,即把两个小的有序的序列和并成一个大的有序序列:合二为一。

一个二路归并的流程图是这样的:

多路归并无非是多个有序的小序列合并成一个大的有序序列,道理和二路归并一样。

先来看下如何把两个有序的序列合并成一个大的有序序列,代码如下:

/*
 *把有序序列a和b,合并成c
 *该算法成立前提: a和b已经有序
 */
void merge(int a[], int na, int b[], int nb, int c[])
{
	if(a && b && c && na >0 && nb >0)
	{
		int i,j,k;
		i = j = k = 0;
		//不断地比较a和b的头部元素,较小的存入c
		while(i < na && j < nb)
		{
			if(a[i] <= b[j]) // <= 保持算法的稳定性
				c[k++] = a[i++];
			else
				c[k++] = b[j++];
			/*另一种更有效的做法是这样的
			while(i < na && a[i] <= b[j])
				c[k++] = a[i++];
			while(j < nb && b[j] < a[i])
				c[k++] = b[j++];
			*/
		}
		//把a或b中剩余的元素直接存入c
		/*  也可以这样:
	     *  memcpy(c+k, a+i, (na-i)sizeof(int));
	     * 下同
	     */
		while(i < na)
			c[k++] = a[i++];
		while(j < nb)
			c[k++] = b[j++];
	}
}

可以看出,二路归并的时间复杂度是O(n),n是原序列的数据规模。以上代码是归并排序的基础,弄懂了它,就很好写归并排序了,看下归并排序的流程图:

可以看出,上半部分不断地递归深入:不断地均分原序列,直到每一部分只含有一个元素。下半部分,开始递归返回,通过反复调用二路归并算法,把相邻的有序子序列合并成一个规模更大的序列。

理解了这些,相信就很容易写出归并排序的代码了:

//把[first, mid]和[mid+1, last]范围内的数据合并
void mergeArray(int a[], int b[], int first, int mid, int last)
{
	int i, j, k;
	i = first, j = mid + 1, k = 0;
	while (i <= mid && j <= last)
	{
		while(i <= mid && a[i] <= a[j])
			b[k++] = a[i++];
		while(j <= last && a[j] < a[i])
			b[k++] = a[j++];
	}
	/*  也可以这样:
	 *  memcpy(b+k, a+i, (mid-i+1)sizeof(int));
	 * 下同
	 */
	while (i <= mid)
		b[k++] = a[i++];
	while (j <= last)
		b[k++] = a[j++];
	//[first,last]范围内的数据已有序,则写回原数组
	for (i = 0; i < k; i++)
		a[first + i] = b[i];
}
void mergesort(int a[], int b[], int first, int last)
{
	if (first < last)
	{
		int mid = first + ((last - first) >> 1);
		mergesort(a, b, first, mid);
		mergesort(a, b, mid + 1, last);
		mergeArray(a, b, first, mid, last);
	}
}
void MergeSort(int a[], int n)
{
	if (a && n > 1)
	{
		int *b = new int[n];  //构建辅助数组
		mergesort(a, b, 0, n - 1);
		delete[]b;
	}
}

在排序过程中,我们使用了一个相同大小的临时辅助数组。

算法分析:

1.算法的复杂度

对数组长度为n的序列进行归并排序,则大约要进行logn次归并,每一次合并都是线性时间O(n)。故粗略的计算出归并排序的时间复杂度是O(nlogn)(最好、最差都是这样)。空间复杂度是O(n)。详细的时间复杂度分析是这样的:

对长度为n的序列归并排序,需要递归的对长度为n/2的子序列进行归并排序,最后把两段子序列二路归并。递推关系是这样的:T(n)=2T(n/2)+O(n),显然T(1)=O(1),解得T(n)=o(nlogn)。

2.稳定性

归并排序是稳定的,并且是时间复杂度为o(nlogn)的几种排序(快速排序堆排序)中唯一稳定的排序算法。

3.存储结构

顺序存储和链式存储都行。

另外,归并排序多用于外排序中。

转载请注明出处,本文地址:http://blog.csdn.net/zhangxiangdavaid/article/details/34463409

若是有所帮助,顶一个哦!

专栏目录:数据结构与算法目录

归并排序:二路归并

时间: 2024-10-13 03:36:13

归并排序:二路归并的相关文章

排序(文献摘要)

<排序思想> 博客分类: 数据结构 排序插入排序交换排序选择排序归并排序 一.介绍 排序是我们工作中经常碰到的一件事,基本每个项目都涉及到排序运算.一般,排序操作在数据处理过程中要话费许多时间.为了提高计算机的运行效率,人们提出不断改进各种各样的排序算法,而这些算法也从不同角度展示了算法设计的某些重要原则和技巧. 排序就是将一组对象按照规定的次序重新排列的过程,排序往往是为检索服务的.例如,学生档案系统里面的学生成绩信息就是按照学号.年龄或入学成绩等排序后的结果,在排好序的结果里面检索学生成绩

洛谷 P1309 瑞士轮

题目背景 在双人对决的竞技性比赛,如乒乓球.羽毛球.国际象棋中,最常见的赛制是淘汰赛和循环赛.前者的特点是比赛场数少,每场都紧张刺激,但偶然性较高.后者的特点是较为公平,偶然性较低,但比赛过程往往十分冗长. 本题中介绍的瑞士轮赛制,因最早使用于1895年在瑞士举办的国际象棋比赛而得名.它可以看作是淘汰赛与循环赛的折衷,既保证了比赛的稳定性,又能使赛程不至于过长. 题目描述 2*N 名编号为 1~2N 的选手共进行R 轮比赛.每轮比赛开始前,以及所有比赛结束后,都会按照总分从高到低对选手进行一次排

两路归并排序

链表两路归并 #include<iostream> #include<assert.h> using namespace std; struct node { int val; node * next; node(int v) { val=v; next=NULL; } }; node * merge(node* list1 , node * list2) { assert(list1!=NULL&&list2!=NULL);//括号中是希望出现的正确的情况  no

归并排序_逆序数

归并排序求逆序数 在一个排列中,如果一对数的前后位置与大小顺序相反,即前面的数大于后面的数,那么它们就称为一个逆序.一个排列中逆序的总数就称为这个排列的逆序数.一个排列中所有逆序总数叫做这个排列的逆序数.也就是说,对于n个不同的元素,先规定各元素之间有一个标准次序(例如n个 不同的自然数,可规定从小到大为标准次序),于是在这n个元素的任一排列中,当某两个元素的先后次序与标准次序不同时,就说有1个逆序.一个排列中所有逆序总数叫做这个排列的逆序数. 1 #include<cstdio> 2 #in

[算法]——归并排序(Merge Sort)

归并排序(Merge Sort)与快速排序思想类似:将待排序数据分成两部分,继续将两个子部分进行递归的归并排序:然后将已经有序的两个子部分进行合并,最终完成排序.其时间复杂度与快速排序均为O(nlogn),但是归并排序除了递归调用间接使用了辅助空间栈,还需要额外的O(n)空间进行临时存储.从此角度归并排序略逊于快速排序,但是归并排序是一种稳定的排序算法,快速排序则不然. 所谓稳定排序,表示对于具有相同值的多个元素,其间的先后顺序保持不变.对于基本数据类型而言,一个排序算法是否稳定,影响很小,但是

c++链表归并排序的迭代版本

之前用js写了个归并排序非递归版,而这一次,c++封装链表的时候也遇到了一个归并排序的接口.邓老师实现了递归版本的归并排序,但是递归的调用函数栈的累积是很占内存空间的.于是乎,那试试在链表结构上实现以下归并排序吧.但是一旦开始,就遇到难题了,在链表下,我们无法按索引访问,所以,在迭代过程中,左右序列就无法很好的用o(1)时间就解决.先看看我实现的代码吧,初步测试没问题,如果有什么问题,希望大神指出,不知为何,用c++写东西总觉得哪里有问题,即使程序可以运行. template<typename

17. 蛤蟆的数据结构进阶十七排序实现之归并排序

17. 蛤蟆的数据结构进阶十七排序实现之归并排序 本篇名言:"人生不是一种享乐 ,而是一桩十分沉重的工作.-- 列夫 . 托尔斯泰" 我们来看下归并排序. 欢迎转载,转载请标明出处:http://blog.csdn.net/notbaron/article/details/47790163 1.  归并排序 归并排序是建立在归并操作上的一种有效的排序算法,该算法是采用分治法(Divide and Conquer)的一个非常典型的应用.将已有序的子序列合并,得到完全有序的序列:即先使每个

算法导论笔记(二)二路归并排序

二路归并排序 归并排序采用了一种”分而治之“的策略:将原问题分解成N个规模较小而结构与原问题相似的子问题:递归求解这些子问题,然后合并其结果,从而得到原问题的解. 分治模式一般遵循以下三个步骤: 分解(Divide):将原问题分解成若干子问题: 解决(Conquer):递归地求解各子问题.若子问题足够小,则直接求解: 合并(Combine):将子问题的解合并成原问题的解. ”二路归并"的算法也遵循以下三个步骤: 分解:将原序列中拥有的N个元素分解各含N / 2个元素的子序列: 解决:用合并排序法

归并排序的实现

归并排序也是一种很优越的排序方式,并且时间复杂度为O(nlogn),而且归并排序的思想很有意思很有启发,包括排序过程和时间复杂度的推导等等,具体可以google一下.下面给出二路归并的实现代码. #include<iostream> using namespace std; void merge(int data[],int low,int high,int mid) { int a[mid-low+1],b[high-mid]; int i,j,k; for(int m = 0;m <