IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 数据结构与算法 -> 归并 排序 -> 正文阅读

[数据结构与算法]归并 排序


一、归并排序的基本思想

1、归并排序算法是一类不同的排序方法,归并的含义是将两个或两个以上的有序数据序列合并成一个新的有序数据序列,该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。
2、基本思想是假设数组A有N个元素,数组A是N个有序的子序列组成,每个子序列的长度为1,两两重复合并,得到一个长度为N的有序数据序列为止。
3、合并算法的核心操作就是将一维数组中前后相邻的两个两个有序序列合并成一个有序序列,合并算法也可以采用递归算法来实现。

归并排序的核心步骤:

在这里插入图片描述

在这里插入图片描述

二、递归实现归并

代码如下(示例):

void _MergeSort(int* a, int* tmp, int left, int right)//left为需要归并数组的左下标,tight同理
{
	if (left >= right)//当满足条件时,表示只有一个元素,或者left已经在right的右边了,此时就不要再归并了
	{
		return;
	}
	int mid = left + (right - left) / 2;//每次我们都取中间下标,从而归并左区间和右区间,从而才能保证这次归并的左右区间是有序的
	_MergeSort(a, tmp, left, mid);//归并左
	_MergeSort(a, tmp, mid + 1, right);//归并右
	//以下是正式归并的过程
	int begin1 = left, end1 = mid;//begin1为这个区间的起点,end1为终点,being2和end2同理
	int begin2 = mid + 1, end2 = right;
	int index = left;//临时数组的下标,这里给第1个区间的下标,因为要保证拷贝数据时一一对应,否则将会出现数据覆盖的现象,归并将会失败
	while (begin1 <= end1 && begin2 <= end2)//当一个区间没有数据了就结束
	{
		if (a[begin1] < a[begin2])
		{
			tmp[index++] = a[begin1++];
		}
		else
		{
			tmp[index++] = a[begin2++];
		}
	}
	//上面的while循环,只有一个区间没有数据了,另一个还有,但我们不知道是哪一个,所以就对这两个区间都进行处理,以下的while循环只会进一个
	while (begin1 <= end1)//区间1没结束,就将区间1剩下的数据放到临时数组
	{
		tmp[index++] = a[begin1++];
	}
	while (begin2 <= end2)//区间2没结束,就将区间2剩下的数据放到临时数组
	{
		tmp[index++] = a[begin2++];
	}
	for (int i = left; i <= right; i++)//归并时,是从原数组左下标为left,右下标为right开始归并的
	{                                 //存放数据时,在临时数组中,也是从左下标为left开始,直到右下标为right时结束
		a[i] = tmp[i];//将临时数组的元素拷贝到原数组
	}
}



void MergeSort(int* a, int n)//a为需要进行排序的数组的数组名,n为数组中元素的个数
{
	int* tmp = (int*)malloc(sizeof(int)*n);//需要开辟新空间,用来临时存放归并好的数据。并在每次归并好后,拷会到原数组
	_MergeSort(a, tmp, 0, n - 1);
}

三、非递归实现归并

非递归的实现方式,采用自底向上的思路。
对数组进行
1个一组进行拆分-排序合并;
2个一组进行拆分-排序合并;
4个一组进行拆分-排序合并;
8个一组的方式进行拆分…

代码如下(示例):

void MergeSortNonR(int* a, int n)
{
	int* tmp = (int*)malloc(sizeof(int)*n);
	if (!tmp)
	{
		printf("malloc fail\n");
		exit(-1);
	}

	int  gap = 1;
	while (gap < n)
	{
		for (int i = 0; i < n; i += 2 * gap)
		{
			//每次都分为左右区间 [i,i + gap - 1] 和 [i + gap,i + 2 * gap -1]
			int begin1 = i, end1 = i + gap - 1;
			int begin2 = i + gap, end2 = i + 2 * gap - 1;

			//控制边界
			//end1、begin2、end2都可能越界  所以分三种情况  begin1不可能越界->i < n

			//当end1越界,[begin2, end2]不存在
			if (end1 >= n)//进行边界修正
			{
				end1 = n - 1;
			}

			//当[begin1, end1]存在  [begin2, end2]存在     
			if (begin2 >= n)                               
			{                                             
				begin2 = n - 1;                            
				end2 = n - 1;                              
			}                                              

			//当end1和begin2没有越界,但end2越界
			if (end2 >= n)
			{
				end2 = n - 1;
			}

			int index = i;
			while (begin1 <= end1 && begin2 <= end2)
			{
				if (a[begin1] < a[begin2])
				{
					tmp[index++] = a[begin1++];
				}
				else
				{
					tmp[index++] = a[begin2++];
				}
			}
			while (begin1 <= end1)
			{
				tmp[index++] = a[begin1++];
			}
			while (begin2 <= end2)
			{
				tmp[index++] = a[begin2++];
			}
		}
		for (int i = 0; i < n; i++)
		{
			a[i] = tmp[i];
		}
		gap *= 2;
	}
}

//这种写法比上面的稍微好理解
void MergeSortNonR(int* a, int n)
{
	int* tmp = (int*)malloc(sizeof(int)*n);
	if (!tmp)
	{
		printf("malloc fail\n");
		exit(-1);
	}

	int  gap = 1;
	while (gap < n)
	{
		for (int i = 0; i < n; i += 2 * gap)
		{
			//每次都分为左右区间 [i,i + gap - 1] 和 [i + gap,i + 2 * gap -1]
			int begin1 = i, end1 = i + gap - 1;
			int begin2 = i + gap, end2 = i + 2 * gap - 1;

			//控制边界
			//end1、begin2、end2都可能越界  所以分三种情况  begin1不可能越界->i < n

			if (end1 >= n || begin2 >= n)
			{
				break;       // end1越界 或者 begin2 越界都不需要归并
			}

			//当end1和begin2没有越界,但end2越界
			if (end2 >= n)
			{
				end2 = n - 1;
			}

			int index = i;
			while (begin1 <= end1 && begin2 <= end2)
			{
				if (a[begin1] < a[begin2])
				{
					tmp[index++] = a[begin1++];
				}
				else
				{
					tmp[index++] = a[begin2++];
				}
			}
			while (begin1 <= end1)
			{
				tmp[index++] = a[begin1++];
			}
			while (begin2 <= end2)
			{
				tmp[index++] = a[begin2++];
			}
			//每次归并小区间都拷贝回原数组
			for (int j = i; j <= end2; j++)
			{
				a[j] = tmp[j];
			}
		}
		gap *= 2;
	}
}

总结

1. 归并的缺点在于需要O(N)的空间复杂度,归并排序的思考更多的是解决在磁盘中的外排序问题。
2. 时间复杂度:O(N*logN)
3. 空间复杂度:O(N)
4. 稳定性:稳定

  数据结构与算法 最新文章
【力扣106】 从中序与后续遍历序列构造二叉
leetcode 322 零钱兑换
哈希的应用:海量数据处理
动态规划|最短Hamilton路径
华为机试_HJ41 称砝码【中等】【menset】【
【C与数据结构】——寒假提高每日练习Day1
基础算法——堆排序
2023王道数据结构线性表--单链表课后习题部
LeetCode 之 反转链表的一部分
【题解】lintcode必刷50题<有效的括号序列
上一篇文章      下一篇文章      查看所有文章
加:2021-12-10 11:18:39  更:2021-12-10 11:18:58 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2025年1日历 -2025/1/10 2:55:25-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码