排序(归并排序,非比较排序)

归并排序

归并排序(MERGE-SORT)是建⽴在归并操作上的⼀种有效的排序算法,该算法是采⽤分治法(Divide and Conquer)的⼀个⾮常典型的应⽤。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成⼀个有序表,称为⼆路归并。

算法分析

分解: 利用mid=(left+right)/2求出中间值,利用左侧[left,mid],右侧[mid+1,right]的划分,分解的结束条件是数组的长度为1时。

**递归合并:**数组递归的结束条件时当left>=right,类似与二叉树的尾插,然后对左右两侧的数组进行比大小后合并,依次上返回。

算法特性

‧ 时间复杂度为 O(n log n):划分产生高度为 log n 的递归树,每层合并的总操作数量 为 n ,因此总体时间复杂度为 O(n log n) 。

‧ 空间复杂度为 O(n):递归深度为 log n,使用 O(log n) 大小的栈帧空间。合并操作需 要借助辅助数组实现,使用 O(n) 大小的额外空间。

‧ 稳定排序:在合并过程中,相等元素的次序保持不变。

源代码

cpp 复制代码
//归并排序
void _MergeSort(int* arr, int left, int right, int* tem)
{
	//递归
	if (left >= right)
	{
		return;
	}
	int mid = (left + right) / 2;
	_MergeSort(arr, left, mid, tem);
	_MergeSort(arr, mid + 1, right, tem);
	//合并
	int bagan1 = left, bagan2 = mid + 1;
	int end1 = mid, end2 = right;
	int index = bagan1;
	while (bagan1 <= end1 && bagan2 <= end2)
	{
		if (arr[bagan1] < arr[bagan2])
		{
			tem[index++] = arr[bagan1++];
		}
		else
		{
			tem[index++] = arr[bagan2++];
		}
	}
	//越界情况
	//2越界的情况
	while (bagan1<=end1)
	{
		tem[index++] = arr[bagan1++];
	}
	//1越界的情况
	while (bagan2 <= end2)
	{
		tem[index++] = arr[bagan2++];
	}
	for (int i = left; i <=right; i++)
	{
		arr[i] = tem[i];
	}
}
void MergeSort(int* arr, int n)
{
	//开辟空间
	int* tem = (int*)malloc(sizeof(int) * n);
	_MergeSort(arr, 0, n-1,  tem);
	free(tem);
}

计数排序

算法分析

计数排序⼜称为鸽巢原理,是对哈希直接定址法的变形应⽤。操作步骤:

1)统计相同元素出现次数

2)根据统计的结果将序列回收到原来的序列中

如何解决空间浪费的情况:

因为,我记数排序要先开辟空间,把值放进去,但是当最大值较大,数据所在的区间又较为密集的时候,就会出现上述状况,造成空间的浪费。因此我们利用max-min算出所要开辟的空间的范围,从而避免空间的浪费。

如何进行存储负数:

我们知道负数在数组中是无法表示的,这时我们可能会想到,取绝对值,但是如果出现一个负数的绝对值后的值正好和原数组中某一个数一样是不是就会出现错误。解决方案:让最小的负数减去本身即可。

时间复杂度,空间复杂度

空间复杂度:O(range)

时间复杂度:O(N +range),但是有同学会想难度不是时间复杂度:O(N*range)?

这里面内层循环就是n个数据循环n次,外层是一次遍历所以这里的时间复杂度是n+range,而不是range*n;

计数排序的特性

  1. 计数排序在数据范围集中时,效率很⾼,但是适⽤范围及场景有限。
  2. 只使用整数不适应小数。
  3. 稳定性高

源代码

cpp 复制代码
//计数排序
void CountSort(int* arr, int n)
{
	//先确定范围
	int min = arr[0], max = arr[0];
	for (int i = 1; i < n; i++)
	{
		if (arr[i] < min)
		{
			min = arr[i];
		}
		if (arr[i] > max)
		{
			max = arr[i];
		}
	}
	int range = max - min + 1;
	//开辟空间
	int* tem = (int*)malloc(sizeof(int) * range);
	if (tem == NULL)
	{
		perror("malloc fail!!!");
		exit(1);
	}
	//初始化,利用memset函数
	memset(tem, 0, sizeof(int) * range);
	//
	for (int i = 0; i < n; i++)
	{
		tem[arr[i] - min]++;
	}
	int index = 0;
	for (int i = 0; i < range; i++)
	{
		//解决数据的重复出现,只要不为零就会输出,直到变为零位置
		while (tem[i]--)
		{
			arr[index++] = i + min;
		}
	}
}

总结

稳定性:假定在待排序的记录序列中,存在多个具有相同的关键字的记录,若经过排序,这些记录的 相对次序保持不变,即在原序列中,r[i]=r[j],且r[i]在r[j]之前,⽽在排序后的序列中,r[i]仍在r[j]之 前,则称这种排序算法是稳定的;否则称为不稳定的。

数据结构结构初阶的排序基本上总结完毕了,这里我学了冒泡,插入,选择,快排,希尔,堆,归并,计数排序,这几种排序在时间复杂度,空间复杂度以及稳定性上各有各的优点。下面我们用一张图进行总结。

相关推荐
算法歌者6 分钟前
[算法]入门1.矩阵转置
算法
林开落L21 分钟前
前缀和算法习题篇(上)
c++·算法·leetcode
远望清一色22 分钟前
基于MATLAB边缘检测博文
开发语言·算法·matlab
tyler_download24 分钟前
手撸 chatgpt 大模型:简述 LLM 的架构,算法和训练流程
算法·chatgpt
SoraLuna44 分钟前
「Mac玩转仓颉内测版7」入门篇7 - Cangjie控制结构(下)
算法·macos·动态规划·cangjie
我狠狠地刷刷刷刷刷1 小时前
中文分词模拟器
开发语言·python·算法
鸽鸽程序猿1 小时前
【算法】【优选算法】前缀和(上)
java·算法·前缀和
九圣残炎1 小时前
【从零开始的LeetCode-算法】2559. 统计范围内的元音字符串数
java·算法·leetcode
YSRM1 小时前
Experimental Analysis of Dedicated GPU in Virtual Framework using vGPU 论文分析
算法·gpu算力·vgpu·pci直通
韭菜盖饭2 小时前
LeetCode每日一题3261---统计满足 K 约束的子字符串数量 II
数据结构·算法·leetcode