数据结构之排序

目录

1.常见的排序算法

 2.插入排序

直接插入排序

希尔排序

3.交换排序

冒泡排序

快速排序

hoare版本

挖坑法

前后指针法

 非递归实现

4.选择排序

直接选择排序

堆排序

5.归并排序

6.排序总结


一起去,更远的远方 

1.常见的排序算法

排序:所谓排序,就是使一串记录,按照其中的某个或某些关键字的大小,递增或递减的排列起来的操作。
稳定性:假定在待排序的记录序列中,存在多个具有相同的关键字的记录,若经过排序,这些记的相对次序保持不变,即在原序列中,r[i]=r[j],且r[i]在r[j]之前,而在排序后的序列中,r[i]仍在r[j]之前,则称这种排 序算法是稳定的;否则称为不稳定的。
内部排序:数据元素全部放在内存中的排序。
外部排序:数据元素太多不能同时放在内存中,根据排序过程的要求不能在内外存之间移动数据的排序。

 2.插入排序

直接插入排序

void InsertSort(int* a, int n)
{
	for (int i = 1; i < n; i++)
	{
		int end = i - 1;
		int tmp = a[i];

		while (end >= 0)
		{
			if (a[end] > tmp)
			{
				a[end+1] = a[end];
				--end;
			}
			else
			{
				break;
			}
		}
		a[end + 1] = tmp;
	}

}

  直接插入排序的特性总结:
1. 元素集合越接近有序,直接插入排序算法的时间效率越高
2. 时间复杂度:O(N^2)
3. 空间复杂度:O(1),它是一种稳定的排序算法
4. 稳定性:稳定 

希尔排序

希尔排序法又称缩小增量法。希尔排序法的基本思想是:先选定一个整数,把待排序文件中所有记录分成个组,所有距离为的记录分在同一组内,并对每一组内的记录进行排序。然后,取,重复上述分组和排序的工 作。当到达=1时,所有记录在统一组内排好序
希尔排序的特性总结:
1. 希尔排序是对直接插入排序的优化。
2. 当gap > 1时都是预排序,目的是让数组更接近于有序。当gap == 1时,数组已经接近有序的了,这样就会很快。这样整体而言,可以达到优化的效果。
3. 希尔排序的时间复杂度不好计算,因为gap的取值方法很多,导致很难去计算,因此在好些树中给出的希尔排序的时间复杂度都不固定

 

当gap==1时就是直接插入排序,可以赋值对比一下

//希尔排序
void ShellSort(int* a, int n)
{
	int gap = n;
	while (gap > 1)
	{
		gap = gap / 3 + 1;
		for (int i = 0; i < n - gap; i++)
		{
			int end = i;
			int tmp = a[end + gap];

			while (end >= 0)
			{
				if (a[end] > tmp)
				{
					a[end + gap] = a[end];
					end = end - gap;
				}
				else
				{
					break;
				}
			}
			a[end + gap] = tmp;
		}
	}
}

 

 希尔排序的时间复杂度我们直接记住一个结论   o(n^1.3)

3.交换排序

冒泡排序

前一个和后一个数循坏比较大的数字放最后面,然后再循环比剩下的数字

void BubbleSort(int* a, int n)
{
	for (int j = 0; j < n; j++)
	{
		for (int i = 1; i < n-j; i++)
		{
			if (a[i-1] > a[i ])
			{
				int tmp = a[i];
				a[i] = a[i -1];
				a[i - 1] = tmp;
			}

		}
	}
}

冒泡排序的特性总结:
1. 冒泡排序是一种非常容易理解的排序
2. 时间复杂度:O(N^2)
3. 空间复杂度:O(1)
4. 稳定性:稳定

快速排序


hoare版本

hoare排序思想: 我们默认序列左起第一个数为key,我们定义两个下标left和right分别从序列的左边和右边去找值,left找比key大的值,right找比key小的值,找到之后,交换left和right的值,等到left和right相遇的时候,此时的值一定是比key小的值,我们再把key和这个相遇位置的值进行交换,这样key就回到它应有的正确位置上,我们再递归处理key的左边区间和右边区间,递归结束之后,快排也就结束了。

//单趟排序,
int PartSort1(int* a, int left, int right)
{
	int key = left;
	while (left < right)
	{
	   //左边做key,先走右边
		while (left<right&& a[right] >= a[key])
		{
			right--;
		}

		//左边找大交换,找大的数,小的数就继续走,大的数是结束循环条件
		while (left < right && a[left] <= a[key])
		{
			left++;
		}

		Swap(&a[left],&a[right]);

	}

	Swap(&a[key],&a[left]);  //最后把key交换一下

	return left;//相遇的位置,就是key中间的位置

}

void QuickSort(int* a, int begin, int end)
{
	if (begin >= end) //当左边区间为空或者左边为1个就结束了
		return;

	int key = PartSort1(a, begin, end);
	//begin   key-1   key   key+1   end

	QuickSort(a, begin, key - 1);
	QuickSort(a, key + 1, end);
}

 

注意两个问题

a. 如果key后面的每个数都比key小或大的话,那left向后面找或right向前面找,就会产生越界访问的问题,为了防止这样情况的产生,我们选择在if语句的判断部分加个逻辑与&&保证left小于right,以免产生越界访问的问题。
b. 我们在if语句的判断部分,找的数一定得比key小或大,连相等都是不可以的,为什么呢?因为会产生死循环。

一旦序列中的左右出现相等的数字的时候,我们if语句如果写成>或<而不是>=或<=,程序就废了。


我们用的是分而治之的思想,第一趟排序找到了中间的key,然后再让begin,key-1和key+1,end再继续递归(begin,key-1,,,key,,,,,,key+1,end)

挖坑法

挖坑法思想: 挖坑法的思想还是要比hoare大佬的思想更加容易理解的,整体是一个什么思想呢?


我们先在序列的最左边挖一个坑,然后这个坑位的值就是key值,然后从右往左找比key小的值填到坑位上面去,这个时候坑位就变成right位置了,我们再从左向右找比key大的值,把这个值填到坑位上面去,再将坑位更新为left,循环进行这个工作,直到left和right相遇,他们相遇的位置也就是最后一次hole的位置,我们将key填到hole的位置,这样key就回到它本身的位置了。
剩下的工作我们还是交给递归,递归处理左区间和右区间。

 

//挖坑法
int PartSort1(int* a, int left, int right)
{
	int key = a[left];
	int hole = left;
	while (left < right)
	{
		//先走右边
		while (left < right && a[right] >= key)
		{
			right--;
		}
		a[hole] = a[right];
		hole = right;

		//左边找大交换,找大的数,小的数就继续走,大的数是结束循环条件
		while (left < right && a[left] <= key)
		{
			left++;
		}

		a[hole] = a[left];
		hole = left;

	}
	a[hole] = key;

	return left;//相遇的位置,就是key中间的位置

}

void QuickSort(int* a, int begin, int end)
{
	if (begin >= end) //当左边区间为空或者左边为1个就结束了
		return;

	int key = PartSort1(a, begin, end);
	//begin   key-1   key   key+1   end

	QuickSort(a, begin, key - 1);
	QuickSort(a, key + 1, end);
}

前后指针法

前后指针办法,这个简单方便

int PartSort3(int* a, int left, int right)
{
	int prev = left;
	int cur = left + 1;
	int keyi = left;

	while (cur<=right)
	{
		if (a[cur] < a[keyi])
		{
			prev++;
			Swap(&a[cur],& a[prev]);
		}
		cur++;
	}

	Swap(&a[keyi], &a[prev]);

	keyi = prev;
	
	return keyi;

}

void QuickSort(int* a, int begin, int end)
{
	if (begin >= end) //当左边区间为空或者左边为1个就结束了
		return;

	int key = PartSort3(a, begin, end);
	//begin   key-1   key   key+1   end

	QuickSort(a, begin, key - 1);
	QuickSort(a, key + 1, end);
}

1. 快速排序整体的综合性能和使用场景都是比较好的,所以才敢叫快速排序
2. 时间复杂度:O(N*logN)
3. 空间复杂度:O(logN)
4. 稳定性:不稳定

 非递归实现

void QuickSortNonR(int* a, int begin, int end)
{
	ST st;
	STInit(&st);
	STPush(&st, end);
	STPush(&st, begin);

	while (!STEmpty(&st))
	{
		int left = STTop(&st);
		STPop(&st);

		int right = STTop(&st);
		STPop(&st);

		//int keyi = PartSort3(a, left, right);
		int keyi = PartSort1(a, left, right);

		// [left, keyi-1] keyi [keyi+1, right]

		if (keyi + 1 < right)
		{
			STPush(&st, right);
			STPush(&st, keyi + 1);
		}

		if (left < keyi-1)
		{
			STPush(&st, keyi-1);
			STPush(&st, left);
		}
	}

	STDestroy(&st);
}

4.选择排序

直接选择排序

相当于玩扑克牌的时候,一把把全部的牌抓起来,按大小顺序排序


void Swap(int* p1, int* p2)
{
	int tmp = *p1;
	*p1 = *p2;
	*p2 = tmp;
}


void SelectSort(int* a, int n)
{
	int begin = 0, end = n - 1;

	while (begin < end)
	{

		int maxi = begin, mini = begin;

		for (int i = begin; i <= end; i++)
		{
			if (a[i] > a[maxi])
			{
				maxi = i;
			}

			if (a[i] < a[mini])
			{
				mini = i;
			}
		}

		Swap(&a[begin], &a[mini]);

		if (begin == maxi)
		{
			maxi = mini;
		}

		Swap(&a[end], &a[maxi]);

		++begin;
		--end;
	}


}

直接选择排序的特性总结:
1. 直接选择排序思考非常好理解,但是效率不是很好。实际中很少使用
2. 时间复杂度:O(N^2)
3. 空间复杂度:O(1)
4. 稳定性:不稳定

堆排序

向下调整算法

建堆+向下调整算法 


//向下调整
void AdjustDwon(int* a, int n, int parent)
{
	int child = parent * 2 + 1;
	while (child < n)
	{

		//if (child + 1 < n && a[child + 1] < a[child])  //小堆

		if (child + 1 < n && a[child + 1] > a[child])  //升序改成大堆
		{
			++child;
			//默认左孩子最小,这样可以找出最小的那个孩子
		}

		//if (a[child] < a[parent])   //小堆是小于
		if (a[child] > a[parent])     //大堆改成大于, 升序改成大堆
		{
			Swap(&a[child], &a[parent]);
			parent = child;
			 child = parent * 2 + 1;
		}
		else
		{
			break;
		}

	}

}


//堆排
//升序建大堆,降序建小堆
void HeapSort(int* a, int n)
{
	//建堆,向下调整建堆
	for (int i = (n - 1 - 1) / 2; i >=0; i--)
	{
		AdjustDwon(a,n,i);
	}

	int end = n - 1;
		while (end > 0)
		{

			Swap(&a[end], &a[0]);
			AdjustDwon(a, end, 0);
			end--;
		}

}

详情可以看二叉树和堆的章节

堆排序
1. 堆排序使用堆来选数,效率就高了很多。
2. 时间复杂度:O(N*logN)
3. 空间复杂度:O(1)
4. 稳定性:不稳定

5.归并排序

void _MergeSort(int* arr, int begin, int end, int* tmp)
{
	if (begin >= end)
	{
		return;
	}
	int mid = (begin + end) / 2;
	_MergeSort(arr, begin, mid , tmp);
	_MergeSort(arr, mid + 1, end, tmp);
	int i = begin;
	int begin1 = begin, end1 = mid;
	int begin2 = mid + 1, end2 = end;
	while (begin1 <= end1 && begin2 <= end2)
	{
		if (arr[begin1] <= arr[begin2])//等于时我们取前一个,保证算法的稳定性
		{
			tmp[i++] = arr[begin1++];
		}
		else
		{
			tmp[i++] = arr[begin2++];
		}
	}
	while (begin1 <= end1)
	{
		tmp[i++] = arr[begin1++];
	}
	while (begin2 <= end2)
	{
		tmp[i++] = arr[begin2++];
	}
	memcpy(arr+begin, tmp+begin, sizeof(int) * (end - begin + 1));
}
void MergeSort(int* arr, int begin, int end)
{
	int* tmp = (int*)malloc(sizeof(int) * (end - begin));
	_MergeSort(arr, begin, end-1, tmp);

	free(tmp);
	tmp = NULL;
}

6.排序总结

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/251443.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

积分球均匀光源遥感器如何保持稳定

积分球的亮度均匀性取决于其内部涂层的反射率和分布情况。当光源通过积分球时&#xff0c;光会被内部的涂层多次反射&#xff0c;最终从出光口均匀地散射出去。为了提高亮度均匀性&#xff0c;可以采用具有高反射率和均匀分布的光源&#xff0c;同时选择合适的涂层材料和涂层厚…

NXP应用随记(五):eMios功能点阅读随记

目录 1、概念点 2、eMios功能点 2.1、eMIOS - Single Action Input Capture (SAIC) 2.2、eMIOS - Single Action Output Compare (SAOC) 2.3、eMIOS - Double Action Output Compare (DAOC) 2.4、eMIOS - Pulse/Edge Counting (PEC) – Single Shot 2.5、eMIOS - Pulse/E…

算法:程序员的数学读书笔记

目录 ​0的故事 ​一、按位计数法 二、不使用按位计数法的罗马数字 三、十进制转二进制​​​​​​​ ​四、0所起到的作用​​​​​​​ 逻辑 一、为何逻辑如此重要 二、兼顾完整性和排他性 三、逻辑 四、德摩根定律 五、真值表 六、文氏图 七、卡诺图 八、逻…

【算法Hot100系列】最长回文子串

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…

向华为学习:基于BLM模型的战略规划研讨会实操的详细说明,含研讨表单(二)

上一篇文章&#xff0c;华研荟结合自己的经验和实践&#xff0c;详细介绍了基于BLM模型的战略规划研讨会的设计和组织流程&#xff0c;提高效率的做法。有朋友和我私信沟通说&#xff0c;其实这个流程不单单适合于BLM模型的战略规划研讨会&#xff0c;实际上&#xff0c;使用其…

Linux centos7安装redis 6.2.14 gz并且使用systemctl为开机自启动 / 彻底删除 redis

1.下载 && 减压 wget http://download.redis.io/releases/redis-6.2.14.tar.gz tar -zvxf redis-6.2.14.tar.gz 2.编译&#xff08;分开运行&#xff09; cd redis-6.2.14 make cd src make install 安装目录展示 3.redis.conf 配置更改 daemonize yes supervised s…

【STM32入门】4.1中断基本知识

1.中断概览 在开展红外传感器遮挡计次的实验之前&#xff0c;有必要系统性的了解“中断”的基本知识. 中断是指&#xff1a;在主程序运行过程中&#xff0c;出现了特定的中断触发条件&#xff08;中断源&#xff09;&#xff0c;使得CPU暂停当前正在运行的程序&#xff0c;转…

交友网站的设计与实现(源码+数据库+论文+开题报告+说明文档)

项目描述 临近学期结束&#xff0c;还是毕业设计&#xff0c;你还在做java程序网络编程&#xff0c;期末作业&#xff0c;老师的作业要求觉得大了吗?不知道毕业设计该怎么办?网页功能的数量是否太多?没有合适的类型或系统?等等。这里根据疫情当下&#xff0c;你想解决的问…

听力健康“吃”出来

大多数的研究报告都指出&#xff0c;听力下降的最常见原因是年龄和噪音暴露。然而&#xff0c;近年来越来越多的文章开始探讨其他因素对听力的影响。食物不仅是维持人类基本生存的必需品&#xff0c;随着营养学的进步&#xff0c;人们也逐渐认识到食物中的营养与保持健康之间存…

Unity中Shader URP 简介

文章目录 前言一、URP&#xff08;Universal Render Pipeline&#xff09;由名字可知&#xff0c;这是一个 通用的 渲染管线1、Universal&#xff08;通用性&#xff09;2、URP的由来 二、Build-in Render Pipeline&#xff08;内置渲染管线&#xff09;1、LWRP&#xff08;Lig…

产品经理在项目周期中扮演的角色Axure的安装与基本使用

目录 一.项目周期流程 二.Axure是什么 三.Axure安装 3.1 一键式安装 3.2 汉化 3.3 授权登录 四.Axure的界面介绍及基本使用 4.1 菜单栏的使用 4.2 工具栏的使用 4.3 页面概要的使用及组件的使用 4.4 组件的样式设计 一.项目周期流程 在一般的项目周期中包含的工作内容有&…

Tektronix泰克TCP303示波器电流探头

主要特点和优点&#xff1a; ● 交流/直流测量功能 ● DC~100MHz电流探头放大器&#xff08;TCPA300&#xff09;&#xff0c;当使用&#xff1a; - DC~100MHz, 30A DC&#xff08;TCP312&#xff09; - DC~50MHz, 50A DC&#xff08;TCP305&#xff09; - DC~5MHz, 150A DC&a…

VC++项目的32位、64位的配置和链接问题

新建一个项目&#xff0c;默认是x86配置&#xff1b; 添加包含目录、库目录&#xff0c;之后可以编译通过&#xff1b; 但是链接会出错&#xff0c;因为链接的dll是64位&#xff1b; 把项目配置改为x64&#xff1b; 需要把包含目录和库目录针对x64重新添加&#xff0c;否则会…

CSS学习笔记整理

CSS 即 层叠样式表/CSS样式表/级联样式表&#xff0c;也是标记语言&#xff0c; 用于设置HTML页面中的文本内容&#xff08;字体、大小、对齐方式等&#xff09;、图片的外形&#xff08;宽高、边框样式、边距&#xff09;以及版面的布局和外观显示样式 目录 准备工作 Chrome调…

【JavaWeb学习笔记】10 - 手写Tomcat底层,Maven的初步使用

一、Maven 1.Maven示意图 类似Java访问数据库 2.创建Maven案例演示 配置阿里镜像 找到setting目录 但一开始配置不存在该文件 需要去Maven主目录下的conf拿到settings拷贝到上述目录 拷贝到admin/.m2后打开该settings 在<mirrors>内输入镜像地址 <mirror> …

SpringBoot中处理处理国际化

SpringBoot中处理处理国际化 1. 创建SpringBoot项目2. resource下创建i18n目录3. 右键i18n新建资源包4. 弹框中添加需要支持的国际化语言5. messages.properties中添加需要国际化的键6. application.yaml添加配置7. 国际化工具8. 使用功能9 场景问题 1. 创建SpringBoot项目 2.…

【Flink-cdc-Mysql-To-Kafka】使用 Flinksql 利用集成的 connector 实现 Mysql 数据写入 Kafka

【Flink-cdc-Mysql-To-Kafka】使用 Flinksql 利用集成的 connector 实现 Mysql 数据写入 Kafka 1&#xff09;环境准备2&#xff09;准备相关 jar 包3&#xff09;实现场景4&#xff09;准备工作4.1.Mysql4.2.Kafka 5&#xff09;Flink-Sql6&#xff09;验证 1&#xff09;环境…

毅速:3D打印随形水路 提高良品率和生产效率的新利器

随着科技的不断发展&#xff0c;3D打印技术已经成为模具制造领域的一种重要技术。其中&#xff0c;模具随形水路的设计和制造是提高注塑产品良品率和生产效率的关键环节。 模具随形水路是一种根据产品形状设计的水路&#xff0c;可以更靠近产品&#xff0c;并在模具内热点集中区…

这一篇就够了!全套SpringBoot教程02

SpringBoot运维实用篇 基础篇发布以后&#xff0c;看到了很多小伙伴在网上的留言&#xff0c;也帮助超过100位小伙伴解决了一些遇到的问题&#xff0c;并且已经发现了部分问题具有典型性&#xff0c;预计将有些问题在后面篇章的合适位置添加到本套课程中&#xff0c;作为解决方…

【docker 】Compose 使用介绍

Docker Compose Docker Compose文档 Docker Compose GitHub地址 Docker Compose 是用于定义和运行多容器 Docker 应用程序的工具。通过 Compose&#xff0c;您可以使用 YML 文件来配置应用程序需要的所有服务。然后&#xff0c;使用一个命令&#xff0c;就可以从 YML 文件配…