*快排延伸-自省排序

news2025/5/14 19:48:58

此节是学有余力的人去看,如果没时间,不看也没关系,只要知道代码就可以了!

自省排序的思路是自我侦测和反省,快速排序如果递归深度太深,其算法的效率可能被大幅度削弱,这就需要借助其他的算法进行排序了,所以我们需要把之前所有算法都要使用上,至于思想大家可以去搜搜资料,这个算法的代码过长,所以实现起来很麻烦,但是算法思路我之前都已经讲过,里面有些难以理解的地方,这只是一个代码分享的过程,没必要追究如何得到的这个结论,我只在适当地方进行注释。我改变的代码在第465行。代码如下:

#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <stdlib.h>
#include <assert.h>
#include <ctype.h>
#include<stdbool.h>
#include<stddef.h>//NULL
#include<time.h>
//交换函数
void Swap(int* a, int* b)
{
	int p = *a;
	*a = *b;
	*b = p;
}
//直接插入排序(升序)
void InsertSort1(int* arr, int n)
{
	for (int i = 0; i < n - 1; i++)
	{
		//我们把需要比较的数放入tmp中,然后先与第i个数据比较,依次往前进行比较插入
		int end = i;
		int tmp = arr[end + 1];
		while (end >= 0)
		{
			if (arr[end] > tmp)
			{
				arr[end + 1] = arr[end];
				end--;
			}
			else
			{
				break;
			}
		}
		//如果end=-1我们不能进行数组越界,所以要进行+1操作
		arr[end + 1] = tmp;
	}
}
//希尔排序
void ShellSort1(int* arr, 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 = arr[end + gap];
			while (end >= 0)
			{
				if (arr[end] > tmp)
				{
					arr[end + gap] = arr[end];
					end -= gap;
				}
				else
				{
					break;
				}
			}
			arr[end + gap] = tmp;
		}
	}
}
//直接选择排序
void SelectSort1(int* arr, int n)
{
	int begin = 0, end = n - 1;
	while (begin < end)
	{
		int max, min;
		max = min = begin;
		for (int i = begin + 1; i <= end; i++)
		{
			//我们从开始位置后面的数据进行遍历,原因不用解释
			if (arr[i] < arr[min])
			{
				min = i;
			}
			if (arr[i] > arr[max])
			{
				max = i;
			}
		}
		if (max == begin)
		{
			max = min;
		}
		Swap(&arr[min], &arr[begin]);
		Swap(&arr[max], &arr[end]);
		begin++;
		end--;
	}
}//堆的结构
//能存储的数据有很多种

typedef int HPDataType;
typedef struct Heap
{
	HPDataType* arr;
	int size;//有效数据的个数
	int capacity;//容量
}HP;
//堆的初始化
void HPInit(HP* php)
{
	assert(php);
	php->arr = NULL;
	php->size = php->capacity = 0;
}
//堆的销毁
void HPDesTory(HP* php)
{
	//判断是否为NULL
	if (php->arr)
	{
		free(php->arr);
	}
	php->arr = NULL;
	php->size = php->capacity = 0;
}
//向上调整算法
void AdjustUp(HPDataType* arr, int child)
{
	HPDataType parent = (child - 1) / 2;
	while (child > 0)
	{
		if (arr[child] > arr[parent])
		{
			Swap(&arr[child], &arr[parent]);
			//或者可以加个continue,之后把else去掉,只留break;
			child = parent;
			parent = (child - 1) / 2;
		}
		else
		{
			break;
		}
	}
}
//插入数据
void HPPush(HP* php, HPDataType x)
{
	assert(php);
	if (php->size == php->capacity)
	{
		int newcapacity = php->capacity == 0 ? 4 : 2 * php->capacity;
		//增容
		HPDataType* tmp = (HPDataType*)realloc(php->arr, sizeof(HPDataType) * (newcapacity));
		//增容失败
		if (tmp == NULL)
		{
			perror("realloc fail!\n");
			exit(1);
		}
		php->arr = tmp;
		php->capacity = newcapacity;
	}
	php->arr[php->size] = x;
	//第二个传的参数是php->size,因为下标这个时候最大已经是size,有size+1个数据,我们还没有执行++的操作
	AdjustUp(php->arr, php->size);
	++php->size;
}
//判空
bool HPEmpty(HP* php)
{
	assert(php);
	return php->size == 0;
}
//向下调整算法
void AdjustDown(HPDataType* arr, int parent, int n)
{
	int child = parent * 2 + 1;
	while (child < n)
	{
		//先找最小的
		if (child + 1 < n && arr[child] > arr[child + 1])
		{
			child++;
		}
		if (arr[parent] > arr[child])
		{
			Swap(&arr[child], &arr[parent]);
			parent = child;
			child = parent * 2 + 1;
		}
		else
		{
			break;
		}
	}
}
//删除栈顶数据
void HPpop(HP* php)
{
	assert(!HPEmpty(php));
	Swap(&php->arr[0], &php->arr[php->size - 1]);
	--php->size;
	AdjustDown(php->arr, 0, php->size);
}
//取堆顶元素
HPDataType HPTop(HP* php)
{
	assert(!HPEmpty(php));
	return php->arr[0];
}
//向下调整算法建堆
void HeapSort(HPDataType* arr, int n)
{
	for (int i = (n - 2) / 2; i >= 0; i--)
	{
		AdjustDown(arr, i, n);
	}
	//堆排序
	int end = n - 1;
	while (end > 0)
	{
		Swap(&arr[0], &arr[end]);
		AdjustDown(arr, 0, end);
		end--;
	}
}
//向上调整算法建堆
void HeapSort01(HPDataType* arr, int n)
{
	for (int i = 0; i < n; i++)
	{
		AdjustUp(arr, i);
	}
	//堆排序
	int end = n - 1;
	while (end > 0)
	{
		Swap(&arr[0], &arr[end]);
		AdjustDown(arr, 0, end);
		end--;
	}
}
//冒泡排序
void BubbleSort(int* arr, int n)
{
	for (int i = 0; i < n - 1; i++)
	{
		int exchange = 0;
		for (int j = 0; j < n - i - 1; j++)
		{
			if (arr[j] > arr[j + 1])
			{
				exchange = 1;
				Swap(&arr[j], &arr[j + 1]);
			}
		}
		if (exchange == 0)
		{
			break;
		}
	}
}
typedef int STDataType;
//栈的定义
typedef struct Stack
{
	STDataType* arr;//所存储的数据
	int size;//有效数据的个数
	int capacity;//所能存储的数据个数
}ST;
//栈的初始化
void SLInit(ST* st)
{
	assert(st);
	st->arr = NULL;
	st->size = st->capacity = 0;
}
//入栈
void StackPush(ST* ps, STDataType x)
{
	assert(ps);
	if (ps->size == ps->capacity)
	{
		int newcapacity = ps->capacity == 0 ? 4 : 2 * ps->capacity;
		STDataType* tmp = (STDataType*)realloc(ps->arr, newcapacity * sizeof(STDataType));
		//我们这个元素的扩容是在数组上进行操作的,所以我们要这样写
		if (tmp == NULL)
		{
			//增容失败
			perror("realloc fail!\n");
			exit(1);
		}
		ps->arr = tmp;
		ps->capacity = newcapacity;
	}
	ps->arr[ps->size++] = x;
}
//判断栈是否为空
bool StackEmpty(ST* ps)
{
	assert(ps);
	return ps->size == 0;
	//有效数据为0个时,栈就会为空
}
//出栈
void StackPop(ST* ps)
{
	assert(!StackEmpty(ps));
	//这个!一定要加上去,如果为true,则栈中没有数据
	ps->size--;
}
//取栈顶元素
STDataType STTop(ST* ps)
{
	assert(!StackEmpty(ps));
	return ps->arr[ps->size - 1];
}
//销毁栈
void STDestroy(ST* ps)
{
	//如果栈本来就没申请空间,就不需要销毁了
	//所以我们要加个判断条件
	if (ps->arr != NULL)
	{
		//也可以写为ps->capacity!=0或ps->capacity
		free(ps->arr);
		ps->arr = NULL;
		ps->size = ps->capacity = 0;
	}
}
bool isValid(char* s)
{
	ST st;
	//初始化
	SLInit(&st);
	char* pi = s;
	//用于遍历字符串
	while (*pi != '\0')
	{
		if (*pi == '(' || *pi == '[' || *pi == '{')
		{
			//入栈
			StackPush(&st, *pi);
			//记得把这个int改为char
		}
		else
		{
			//判断栈是否为空
			if (StackEmpty(&st))
			{
				//如果为空,直接返回为false
				//相当于出栈没有了
				return false;
			}
			//可以加个else语句
			//如果前面条件不成立,直接就跳出了这个函数了
			//取栈顶元素
			char top = STTop(&st);
			//记得出栈,否则之后取的都是一个元素
			StackPop(&st);
			//记得pi要解引用,之前我也写错了
			if ((top == '(' && *pi != ')') || (top == '[' && *pi != ']') || (top == '{' && *pi != '}'))
			{
				//先返回false的,只有满足所有条件才能返回true
				return false;
			}
		}
		pi++;
		//记得调整pi,否则会死循环
	}
	//如果直接返回的话,就没办法销毁链表
	bool ret = StackEmpty(&st) ? true : false;
	//销毁链表
	STDestroy(&st);
	return ret;
}
//lomuto前后指针法
int _QuickSort(int* arr, int left, int right)
{
	int prev = left;
	int cur = prev + 1;
	//key也可以不创建,之后又不会发生改变,只是方便理解而已
	int key = left;
	while (cur <= right)
	{
		if (arr[cur] < arr[key] && ++prev != cur)
		{
			//我们如果把一个位置的数进行交换是没必要的
			//并且如果把++prev放到前面就会先执行该语句会出错
			Swap(&arr[cur], &arr[prev]);
		}
		++cur;
	}
	Swap(&arr[prev], &arr[key]);
	return prev;
}
快速排序
//void QuickSort(int* arr, int left, int right)
//{
//	if (left >= right)
//	{
//		return;
//	}
//	//找基准值并进行排序
//	int key = _QuickSort(arr, left, right);
//	//把数组分为三部分: [left,key-1] key [key+1,right]
//	QuickSort(arr, left, key - 1);
//	QuickSort(arr, key + 1, right);
//}
// 之前没有在代码中实现这个函数,所以在这里添上
// 取栈顶元素
STDataType StackTop(ST* ps)
{
	assert(!StackEmpty(ps));
	return ps->arr[ps->size - 1];
}
 非递归版本的快速排序
//void QuickSort(int* arr, int left, int right)
//{
//	ST st;
//	SLInit(&st);
//	//先让right和left入栈,防止栈为空
//	StackPush(&st, right);
//	StackPush(&st, left);
//	while (!StackEmpty(&st))
//	{
//		//取栈顶元素
//		int begin = StackTop(&st);
//		//要出栈
//		StackPop(&st);
//		int end = StackTop(&st);
//		StackPop(&st);
//		//对[begin,end]使用双指针法
//		int prev = begin;
//		int cur = prev + 1;
//		int key = begin;
//		while (cur <= end)
//		{
//			if (arr[cur] < arr[key] && ++prev != cur)
//			{
//				Swap(&arr[prev], &arr[cur]);
//			}
//			++cur;
//		}
//		Swap(&arr[key], &arr[prev]);
//		//记得加上这句话,之前递归的是直接返回了prev,而这里不行,我刚刚也没发现问题
//		key = prev;
//		//数组分为以下三部分:[begin,prev-1]prev [prev+1,end]
//		//先入右子数组
//		//从右至左开始入
//		if (prev + 1 < end)
//		{
//			StackPush(&st, end);
//			StackPush(&st, prev + 1);
//		}
//		//再入左子数组
//		if (key - 1 > begin)
//		{
//			StackPush(&st, key - 1);
//			StackPush(&st, begin);
//		}
//	}
//	STDestroy(&st);
//}
//自省排序
void IntroSort(int* a, int left, int right, int depth, int defaultDepth)
{
	if (left >= right)
	{
		return;
	}
	//数组长度小于16的小数组视为插入排序,简单递归次数
	if (right - left + 1 < 16)
	{
		InsertSort1(a + left, right - left + 1);
		return;
	}
	//当深度超过2*logN,则改为堆排序
	if (depth > defaultDepth)
	{
		HeapSort(a + left, right - left + 1);
		return;
	}
	//其他情况我们直接用上一讲讲过的排序方法
	depth++;
	int begin = left;
	int end = right;
	//随机选key
	int randi = left + (rand() % (right - left));
	Swap(&a[left], &a[randi]);
	int prev = left;
	int cur = prev + 1;
	int key = left;
	while (cur <= right)
	{
		if (a[cur] < a[key] && ++prev != cur)
		{
			Swap(&a[prev], &a[cur]);
		}
		++cur;
	}
	Swap(&a[prev], &a[key]);
	key = prev;
	//[begin,key-1] [key,key] [key+1,end]
	IntroSort(a, begin, key - 1, depth, defaultDepth);
	IntroSort(a, key + 1, end, depth, defaultDepth);
}
//快速排序
void QuickSort(int* a, int left, int right)
{
	int depth = 0;
	int logn = 0;
	int N = right - left + 1;
	//这个i要从1开始,不然就死循环
	//我也是找了20分钟才发现的!
	for (int i = 1; i < N; i *= 2)
	{
		logn++;
	}
	//防止快速排序退化
	IntroSort(a, left, right, depth, logn * 2);
}
//测试
int main()
{
	int a[] = { 4,3,8,2,1,9,0,7,6,5 };
	int n = sizeof(a) / sizeof(a[0]);
	printf("排序之前: ");
	for (int i = 0; i < n; i++)
	{
		printf("%d ", a[i]);
	}
	QuickSort(a, 0, n - 1);
	printf("\n排序之后: ");
	for (int i = 0; i < n; i++)
	{
		printf("%d ", a[i]);
	}
	return 0;
}
// 测试排序的性能对⽐
void TestOP()
{
	srand(time(0));
	const int N = 100000;
	int* a1 = (int*)malloc(sizeof(int) * N);
	int* a2 = (int*)malloc(sizeof(int) * N);
	int* a3 = (int*)malloc(sizeof(int) * N);
	int* a4 = (int*)malloc(sizeof(int) * N);
	int* a5 = (int*)malloc(sizeof(int) * N);
	//int* a6 = (int*)malloc(sizeof(int) * N);
	int* a7 = (int*)malloc(sizeof(int) * N);
	for (int i = 0; i < N; ++i)
	{
		a1[i] = rand();
		a2[i] = a1[i];
		a3[i] = a1[i];
		a4[i] = a1[i];
		a5[i] = a1[i];
		//a6[i] = a1[i];
		a7[i] = a1[i];
	}
	int begin1 = clock();
	InsertSort1(a1, N);
	int end1 = clock();
	int begin2 = clock();
	ShellSort1(a2, N);
	int end2 = clock();
	int begin3 = clock();
	SelectSort1(a3, N);
	int end3 = clock();
	int begin4 = clock();
	HeapSort01(a4, N);
	int end4 = clock();
	int begin5 = clock();
	QuickSort(a5, 0, N - 1);
	int end5 = clock();
	//int begin6 = clock();
	//MergeSort(a6, N);
	//int end6 = clock();
	int begin7 = clock();
	BubbleSort(a7, N);
	int end7 = clock();
	printf("InsertSort:%d\n", end1 - begin1);
	printf("ShellSort:%d\n", end2 - begin2);
	printf("SelectSort:%d\n", end3 - begin3);
	printf("HeapSort:%d\n", end4 - begin4);
	printf("QuickSort:%d\n", end5 - begin5);
	//printf("MergeSort:%d\n", end6 - begin6);
	printf("BubbleSort:%d\n", end7 - begin7);
	free(a1);
	free(a2);
	free(a3);
	free(a4);
	free(a5);
	//free(a6);

	free(a7);
}
//int main()
//{
//	TestOP();
//}

运行结果如下:

这个代码是完全复制之前的,所以有些函数是已经没有用处的,至于有些东西和我们排序算法有区别的地方需要自己去理解了,因为这是适用于所有情况的排序算法,太长也很正常,之前的排序总有不适用的情况,需要自己去解决了哦!

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

三.微服务架构中的精妙设计:服务注册/服务发现-Eureka

一.使用注册中心背景 1.1服务远程调用问题 服务之间远程调⽤时, 我们的URL是写死的 String url "http://127.0.0.1:9090/product/" orderInfo.getProductId(); 缺点&#xff1a; 当更换机器, 或者新增机器时, 这个URL就需要跟着变更, 就需要去通知所有的相关服…

python-leetcode 63.搜索二维矩阵

题目&#xff1a; 给一个满足两条属性的m*n的整数矩阵&#xff1a; 每行中的整数从左到右按非严格递增顺序排列 每行的第一个整数大于前一行的最后一个整数 给一个整数target,如果target在矩阵中&#xff0c;返回true,否则返回false 方法一&#xff1a;两次二分查找 由于每…

音视频入门基础:MPEG2-TS专题(26)——通过FFmpeg命令使用RTP发送TS流

音视频入门基础&#xff1a;MPEG2-TS专题系列文章&#xff1a; 音视频入门基础&#xff1a;MPEG2-TS专题&#xff08;1&#xff09;——MPEG2-TS官方文档下载 音视频入门基础&#xff1a;MPEG2-TS专题&#xff08;2&#xff09;——使用FFmpeg命令生成ts文件 音视频入门基础…

blender二次元上色

前&#xff1a; 后&#xff1a;&#xff08;脸自己会发光) 参考&#xff1a;05-模型导入与材质整理_哔哩哔哩_bilibili

2025年2月一区SCI-壮丽细尾鹩莺算法Superb Fairy-wren Optimization-附Matlab免费代码

引言 本期介绍一种新的元启发式算法——壮丽细尾鹩莺优化算法Superb Fairy-wren Optimization algorithm&#xff0c;SFOA。该算法结合了壮丽细尾鹩莺群体中幼鸟的发育&#xff0c;繁殖后喂养幼鸟的行为&#xff0c;以及它们躲避捕食者的策略&#xff0c;于2025年2月最新发表在…

Hadoop•踩过的SHIT

听说这里是目录哦 ssh登录Permission denied, please try again&#x1f4a9;要发癫&#x1f972; centos7 yum报错&#xff1a;cannot find a valid baseurl for repo:base/7/x86_64&#x1f4a9;FinalShell重连失效&#x1f4a9;ssh免密登录显示 No route to host&#x1f4a…

闭环SOTA!北航DiffAD:基于扩散模型实现端到端自动驾驶「多任务闭环统一」

端到端自动驾驶目前是有望实现完全自动驾驶的一条有前景的途径。然而&#xff0c;现有的端到端自动驾驶系统通常采用主干网络与多任务头结合的方式&#xff0c;但是它们存在任务协调和系统复杂度高的问题。为此&#xff0c;本文提出了DiffAD&#xff0c;它统一了各种驾驶目标并…

Docker Registry 清理镜像最佳实践

文章目录 registry-clean1. 简介2. 功能3. 安装 docker4. 配置 docker5. 配置域名解析6. 部署 registry7. Registry API 管理8. 批量清理镜像9. 其他10. 参考registry-clean 1. 简介 registry-clean 是一个强大而高效的解决方案,旨在简化您的 Docker 镜像仓库管理。通过 reg…

JavaScript重难点突破:期约与异步函数

同步和异步 ​同步&#xff08;Synchronous&#xff09;​ ​定义&#xff1a;任务按顺序依次执行&#xff0c;前一个任务完成前&#xff0c;后续任务必须等待。 ​特点&#xff1a;阻塞性执行&#xff0c;程序逻辑直观&#xff0c;但效率较低 ​异步&#xff08;Asynchron…

蓝桥杯高频考点——高精度(含C++源码)

高精度 前言高精度加法例题思路及代码solution 1&#xff08;初阶版 40分&#xff09;solution 2&#xff08;完全体 AC&#xff09; 高精度乘法例题思路及代码solution 1&#xff08;TLE 但是代码很清晰&#xff09;solution 1的问题solution 2&#xff08;优化 AC&#xff09…

【机器人】复现 GraspNet 端到端抓取点估计 | PyTorch2.3 | CUDA12.1

GraspNet是通用物体抓取的大规模基准的基线模型&#xff0c;值得学习和复现。 本文分享使用较新版本的PyTorch和CUDA&#xff0c;来搭建开发环境。 论文地址&#xff1a;GraspNet-1Billion: A Large-Scale Benchmark for General Object Grasping 开源地址&#xff1a;https:…

视频联网平台智慧运维系统:智能时代的城市视觉中枢

引言&#xff1a;破解视频运维的"帕累托困境" 在智慧城市与数字化转型浪潮中&#xff0c;全球视频监控设备保有量已突破10亿台&#xff0c;日均产生的视频数据量超过10万PB。然而&#xff0c;传统运维模式正面临三重困境&#xff1a; 海量设备管理失序&#xff1a;…

《网络管理》实践环节03:snmp服务器上对网络设备和服务器进行初步监控

兰生幽谷&#xff0c;不为莫服而不芳&#xff1b; 君子行义&#xff0c;不为莫知而止休。 应用拓扑图 3.0准备工作 所有Linux服务器上&#xff08;服务器和Agent端&#xff09;安装下列工具 yum -y install net-snmp net-snmp-utils 保证所有的HCL网络设备和服务器相互间能…

ubuntu中使用安卓模拟器

本文这里介绍 使用 android studio Emulator &#xff0c; 当然也有 Anbox (Lightweight)&#xff0c; Waydroid (Best for Full Android Experience), 首先确保自己安装了 android studio &#xff1b; sudo apt update sudo apt install openjdk-11-jdk sudo snap install…

py数据结构day3

思维导图&#xff1a; 代码1&#xff08;完成双向循环链表的判空、尾插、遍历、尾删&#xff09;&#xff1a; class Node:def __init__(self, data):self.data dataself.next Noneself.prev Noneclass DoubleCycleLink:def __init__(self):self.head Noneself.tail None…

STM32单片机入门学习——第8节: [3-4] 按键控制LED光敏传感器控制蜂鸣器

写这个文章是用来学习的,记录一下我的学习过程。希望我能一直坚持下去,我只是一个小白,只是想好好学习,我知道这会很难&#xff0c;但我还是想去做&#xff01; 本文写于&#xff1a;2025.04.02 STM32开发板学习——第8节: [3-4] 按键控制LED&光敏传感器控制蜂鸣器 前言开…

【JavaScript】十三、事件监听与事件类型

文章目录 1、事件监听1.1 案例&#xff1a;击关闭顶部广告1.2 案例&#xff1a;随机点名1.3 事件监听的版本 2、事件类型2.1 鼠标事件2.1.1 语法2.1.2 案例&#xff1a;轮播图主动切换 2.2 焦点事件2.2.1 语法2.2.2 案例&#xff1a;模拟小米搜索框 2.3 键盘事件2.3.1 语法2.3.…

通过ansible+docker-compose快速安装一主两从redis+三sentinel

目录 示例主机列表 架构参考 文件内容 安装脚本 ansible变量&#xff0c;需修改 ansible配置文件和主机清单&#xff0c;需修改 运行方式 验证故障转移master 涉及redis镜像和完整的脚本文件 示例主机列表 架构参考 文件内容 安装脚本 #!/bin/bashset -e export pa…

mysql docker容器启动遇到的问题整理

好几个月没折腾mysql的部署&#xff0c;弄了下&#xff0c;又遇到不少问题 问题一&#xff1a;Access denied for user ‘root‘‘172.18.0.1‘ docker容器启动后&#xff0c;本地navicat 连接报这个错误 查到两个方案&#xff0c;一个貌似是要让root用户能在任意ip地址&…

长短期记忆神经网络(LSTM)基础学习与实例:预测序列的未来

目录 1. 前言 2. LSTM的基本原理 2.1 LSTM基本结构 2.2 LSTM的计算过程 3. LSTM实例&#xff1a;预测序列的未来 3.1 数据准备 3.2 模型构建 3.3 模型训练 3.4 模型预测 3.5 完整程序预测序列的未来 4. 总结 1. 前言 在深度学习领域&#xff0c;循环神经网络&…