快速排序的非递归实现、归并排序的递归和非递归实现、基数排序、排序算法的时间复杂度

文章目录

  • 快速排序的非递归
    • 三数取中法选取key
    • 快速排序三路划分
  • 归并排序的递归
  • 归并排序的非递归
  • 计数排序
  • 稳定性
  • 排序算法的时间复杂度

快速排序的非递归

我们使用一个栈来模拟函数的递归过程,这里就是在利用栈分区间。把一个区间分为 [left,keyi-1][key][keyi+1,right]递归下去,直至区间不存在或left > right。
如图所示:
在这里插入图片描述
先把整体的区间压进去,然后出栈,处理完毕后找到keyi再分为左右两个区间。然后往栈里压有区间,压左区间,就像树的后续遍历一样先把叶子区间处理,再处理分支节点的区间。
代码如下所示:

void QuickSortNonS(int* a, int left, int right)
{
	ST st;
	STInit(&st);
	STPush(&st, left);
	STPush(&st, right);
	while (!STEmpty(&st))
	{
		int right = STTop(&st);
		STPop(&st);
		int left = STTop(&st);
		STPop(&st);
		int keyi = PastSort1(a, left, right);
		//先入右区间
		if (keyi + 1 < right)
		{
			STPush(&st, keyi + 1);
			STPush(&st, right);
		}
		//再入左区间
		if (keyi - 1 > left)
		{
			STPush(&st, left);
			STPush(&st, keyi - 1);
		}
	}
	STDestory(&st);
}

三数取中法选取key

还可以选择三数取中法来选取key的值,原理是选取不大不小的数使得快速排序的交换次数变少。
代码如下:

//三数取中法选取Key
int GetMidKey(int* a, int left, int right)
{
	int mid = (left + right) / 2;
	if (a[left] <= a[right]&&a[left] <= a[mid] && a[mid] <= a[right])
	{
		return mid;
	}
	else if(a[left]<=a[mid]&&a[left]<=a[right]&&a[left]<=a[mid])
	{
		return right;
	}
	else if (a[right]<=a[mid]&&a[right]<=a[left]&&a[left]<=a[mid])
	{
		return left;
	}
	else if (a[mid] <= a[left] && a[mid] <= a[right] && a[right] <= a[left])
	{
		return right;
	}
	else if (a[mid] <= a[right] && a[mid] <= a[left] && a[left] <= a[right])
	{
		return left;
	}
	else if (a[right] <= a[left] && a[right] <= a[mid] && a[mid] <= a[left])
	{
		return mid;
	}
	else
	{
		return left;
	}
}

快速排序三路划分

原理:规定左指针,cur指针,右指针。当a[cur] < key时,把a[cur]和a[left]的值交换cur++,left++。当a[cur] > key时,把a[cur]和a[right]的值交换,right–。当a[cur] > key时,cur++,具体的操作过程如下图所示:
在这里插入图片描述
代码如下:

//三路划分
//1、最小的在最左边
//2、最大的在最右边
//3、相等的在中间

void QuickSort1(int* a, int begin, int end)
{
	if (begin > end)
	{
		return;
	}
	//三路划分
	int keyi = GetMidKey(a, begin, end);
	Swap(&a[begin], &a[keyi]);
	int key = a[begin];
	int left = begin;
	int right = end;
	int cur = left + 1;
	while (cur <= right)
	{
		if (a[cur] < key)
		{
			Swap(&a[cur], &a[left]);
			left++;
			cur++;
		}
		else if (a[cur] > key)
		{
			Swap(&a[cur], &a[right]);
			right--;
		}
		else
		{
			cur++;
		}
	}
	//[begin,left-1][left,right][right+1,end]
	QuickSort(a, begin, left-1);
	QuickSort(a, right + 1, end);
}

归并排序的递归

基本思想:
归并排序(MERGE-SORT)是建立在归并操作上的一种有效的排序算法,该算法是采用分治法(Divide andConquer)的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为二路归并。 归并排序核心步骤:
在这里插入图片描述
递归代码如下所示:

void _MergeSort(int* a, int begin, int end, int* temp)
{
	//递归结束条件
	if (begin >= end)
	{
		return;
	}
	//我们需要分区间进行,把区间可以分为
	//[begin,mid][mid+1,end]
	int mid = (begin + end) / 2;
	//通过后序遍历使得最小的区间有序[0,0][1,1][2,2]……[end-1,end-1][end,end]
	_MergeSort(a, begin, mid, temp);
	_MergeSort(a, mid+1, end, temp);
	//开始处理归并排序,也就是处理二叉树根的排序问题 左右根
	//这里相当于合并两个有序数组
	int begin1 = begin, end1 = mid;
	int begin2 = mid + 1, end2 = end;
	int i = begin;
	while (begin1<=end1&&begin2<=end2)
	{
		if (a[begin1] < a[begin2])
		{
			temp[i++] = a[begin1++];
		}
		else
		{
			temp[i++] = a[begin2++];
		}
	}
	while (begin1 <= end1)
	{
		temp[i++] = a[begin1++];
	}
	while (begin2 <= end2)
	{
		temp[i++] = a[begin2++];
	}
	//再把数据拷回去
	memcpy(a+begin, temp+begin,sizeof(int)*(end-begin+1));
}

//归并排序递归算法
void MergeSort(int* a, int n)
{
	//我们需要开辟一个数组
	int* temp = (int*)malloc(sizeof(int) * n);
	//进行递归需要一个子函数
	_MergeSort(a, 0, n - 1, temp);
	free(temp);
}

递归展开图:
在这里插入图片描述

归并排序的非递归

原理:如下图所示:
在这里插入图片描述
划分为一个数一个数一组归并排序后再划分为两个数一组,然后再归并直至数组有序
代码如下:

//归并排序非递归
void MergeSortNonS(int* a, int n)
{
	int gap = 1;
	int* temp = (int*)malloc(sizeof(int) * n);
	while (gap < n)
	{
		int j = 0;
		for (int i = 0; i < n; i += gap * 2)
		{
			//第一趟归并排序1数据归并成两个数,
			//第二趟2个数归为4个数
			//第三趟4个数,4和4归并成8个数
			int begin1 = i, end1 = i + gap - 1;
			int begin2 = i + gap, end2 = i + gap * 2 - 1;
			//一块一块进行拷贝
			/*if (end1 >= n || begin2 >= n)
			{
				break;
			}
			if (end2 >= n)
			{
				end2 = n - 1;
			}*/
			//直接修正
			if (end1 >= n)
			{
				end1 = n - 1;
				//不存在的区间
				begin2 = n;
				end2 = n-1;
			}
			else if (end2 >= n)
			{
				end2 = n - 1;
			}
			else if (begin2 >= n )
			{
				begin2 = n;
				end2 = n - 1;
			}
			printf("[%d %d][%d %d]\n", begin1, end1, begin2, end2);
			while (begin1 <= end1 && begin2 <= end2)
			{
				if (a[begin1] < a[begin2])
				{
					temp[j++] = a[begin1++];
				}
				else
				{
					temp[j++] = a[begin2++];
				}
			}
			while (begin1 <= end1)
			{
				temp[j++] = a[begin1++];
			}
			while (begin2 <= end2)
			{
				temp[j++] = a[begin2++];
			}
			//一块一块进行拷贝
			//memcpy(a+i, temp+i, sizeof(int)*(end2-i)+1);
		}
		//直接拷贝
		memcpy(a, temp, sizeof(int) * n);
		gap *= 2;
	}
	free(temp);
}

这里需要注意溢出的三种情况
在这里插入图片描述
消除溢出的方法有两种,均已在代码注释中标出。

计数排序

计数排序应用了相对映射的方法
如下图所示:
在这里插入图片描述
代码实现:

//计数排序
void CountSort(int* a, int n)
{
	//首先找出最大值和最小值
	int max = a[0];
	int min = a[0];
	//相对映射
	for (int i = 0; i < n; i++)
	{
		if (max < a[i])
		{
			max = a[i];
		}
		if (min > a[i])
		{
			min = a[i];
		}
	}
	//求出范围
	int range = max - min;
	//开辟一个range大小的数组
	int* temp = (int*)malloc(sizeof(int) * range+1);
	for (int i = 0; i < n;i++)
	{
		temp[i] = 0;
	}
	//统计次数
	for (int i = 0; i < n; i++)
	{
		temp[a[i] - min]++;
	}
	//排序
	for (int i = 0; i < n; i++)
	{
		while (temp[i]--)
		{
			a[i] = i + min;
		}
	}
	free(temp);
}

稳定性

稳定的排序算法:冒泡排序,计数排序,归并排序,直接插入排序。
不稳定的排序算法 :堆排序,快速排序,选择排序,希尔排序。

排序算法的时间复杂度

基数排序
时间复杂度O(N+范围)
空间复杂度O(范围)

在这里插入图片描述

数据结构的初阶到此为止,高阶数据结构将用C++来描述。

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

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

相关文章

用百度地图api获取当前定位,获取经纬度——前端笔记

问题&#xff1a; 做一个按钮&#xff0c;点击后可以获取到当前位置的经纬度&#xff0c;并渲染地图。 效果如下: 代码如下: <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head><title>获取当前定位测试<…

【笔记MD】

https://editor.csdn.net/md/?not_checkout1&articleId131798584 这里写自定义目录标题 https://editor.csdn.net/md/?not_checkout1&articleId131798584欢迎使用Markdown编辑器新的改变功能快捷键合理的创建标题&#xff0c;有助于目录的生成如何改变文本的样式插入…

行业数据和报告到底应该如何去找?

信息时代&#xff0c;经常要对行业信息进行分析。这时首先就是要进行信息收集和筛选&#xff0c;如果我们懂得构建自己的工作工具和数据来源&#xff0c;效率会蹭蹭往上涨。 找行业报告、了解行业趋势&#xff0c;提高效率。 1. 国家权威 国家统计局&#xff1a;这个网站覆盖…

谋合作、创新境 | 百度参观图为科技生产全链路

当代科技的发展不断催生出新的变革和机遇&#xff0c;百度作为全球顶尖的高科技公司&#xff0c;凭借其强大的创新基因&#xff0c;一直处于人工智能领域的最前沿。   近日&#xff0c;百度公司派出了一支专业团队来到了图为科技&#xff0c;对图为的研发技术及生产线进行了全…

基于MSP432P401R跟随小车【2022年电赛C题】

文章目录 一、赛前准备1. 硬件清单2. 工程环境 二、赛题思考三、软件设计1. 路程、时间、速度计算2. 距离测量3. 双机通信4. 红外循迹 四、技术交流 一、赛前准备 1. 硬件清单 主控板&#xff1a; MSP432P401R测距模块&#xff1a; GY56数据显示&#xff1a; OLED电机&#x…

基于单片机智能台灯坐姿矫正器视力保护器的设计与实现

功能介绍 以51单片机作为主控系统&#xff1b;LCD1602液晶显示当前当前光线强度、台灯灯光强度、当前时间、坐姿距离等&#xff1b;按键设置当前时间&#xff0c;闹钟、提醒时间、坐姿最小距离&#xff1b;通过超声波检测坐姿&#xff0c;当坐姿不正容易对眼睛和身体腰部等造成…

Git 使用笔记

Git使用笔记 1 版本控制 1.1 什么是版本控制 ​ 版本控制&#xff08;Revision control&#xff09;是一种在开发的过程中用于管理我们对文件、目录或工程等内容的修改历史&#xff0c;方便查看更改历史记录&#xff0c;备份以便恢复以前的版本的软件工程技术。简单说就是用…

大模型开发(六):OpenAI Completions模型详解并实现多轮对话机器人

全文共8500余字&#xff0c;预计阅读时间约17~30分钟 | 满满干货(附代码)&#xff0c;建议收藏&#xff01; 代码下载点这里 一、 Completions与Chat Completions基本概念 经过海量文本数据训练的大模型会在全量语义空间内学习语法关系和表达风格&#xff0c;并通过某些微调过…

postgresql部署及优化

目录 一、postgresql概念 二、PostgreSQL 特征 三、postgres安装与登录 3.1、postgres安装 3.2、postgres使用 3.3.1、postgres登录 3.3.2、修改postgres用户密码 四、PostgreSQL 命令 4.1、PostgreSQL 创建数据库 4.2、删除数据库 4.3、创建表格 4.4、删除表格 一、…

Python爬虫学习笔记(一)————网页基础

目录 1.网页的组成 2.HTML &#xff08;1&#xff09;标签 &#xff08;2&#xff09;比较重要且常用的标签&#xff1a; ①列表标签 ②超链接标签 &#xff08;a标签&#xff09; ③img标签&#xff1a;用于渲染&#xff0c;图片资源的标签 ④div标签和span标签 &…

【MySQL】MySQL在Centos7环境下安装

目录 一、卸载不要的环境 1.1、查看是否有安装mysql 1.2、关闭运行的程序 1.3、卸载安装 二、配置yum 源 2.1、下载yum 源 2.2 安装yum源 2.3 查看是否已经生效 三、安装mysql服务 四、启动服务 五、登录方法 方法一&#xff08;不行就下一个&#xff09; 方法二&#xff08;不…

【Tauri + React 实战】VCluster - 了解技术选型与开发环境配置

VCluster A React Tauri App as visualizer of apps cluster on windows. 背景介绍 VCluster是一个在开发环境下&#xff0c;用以对一系列应用集群&#xff08;如分布式、微服务&#xff09;进行可视化管理的桌面应用程序&#xff0c;目标是实现类似 docker-compose 那样的集…

怎么解决亚马逊跟卖?为何卖家总是举报不成功?

以前大家都是从跟卖的时代走向现在的品牌化运营之路&#xff0c;但是现在跟卖已经从大家都模仿的对象变成了大部分卖家厌恶的对象&#xff0c;那么怎么解决这个跟卖问题呢&#xff1f;目前最直接的方法就是进入亚马逊后台进行举报&#xff0c;但是大概率是失败的。 一、举报违…

Spring Cloud 之 Gateway 网关

&#x1f353; 简介&#xff1a;java系列技术分享(&#x1f449;持续更新中…&#x1f525;) &#x1f353; 初衷:一起学习、一起进步、坚持不懈 &#x1f353; 如果文章内容有误与您的想法不一致,欢迎大家在评论区指正&#x1f64f; &#x1f353; 希望这篇文章对你有所帮助,欢…

elasticsearch基本操作

elasticsearch 下面参数详细解释 java 搜索查询看官方文档 https://www.elastic.co/guide/en/elasticsearch/client/java-api-client/8.8/connecting.html#_your_first_request{"name" : "Tom Foster","cluster_name" : "elasticsearch&q…

Kafka 入门到起飞 - 核心概念(术语解释)

在kafka之旅&#xff0c;我们会大量讨论Kafka中的术语&#xff0c;那么就让我们先来了解一下这些核心概念 消息(Message)&#xff1a; kafka的数据单元称为消息&#xff0c;相当于DB里的一行数据或一条记录 消息由字节数组组成 批次&#xff1a; 生产者组一批数据再向kafka推送…

消息重试框架 Spring-Retry 和 Guava-Retry

一 重试框架之Spring-Retry 1.Spring-Retry的普通使用方式 2.Spring-Retry的注解使用方式 二 重试框架之Guava-Retry 总结 图片 一 重试框架之Spring-Retry Spring Retry 为 Spring 应用程序提供了声明性重试支持。它用于Spring批处理、Spring集成、Apache Hadoop(等等)。…

MySQL高阶语句

目录 一、常用查询 1、按关键字排序 2、区间判断及查询不重复记录 3、限制结果条目 4、设置别名&#xff08;alias ——》as&#xff09; 5、通配符 一、常用查询 &#xff08;增、删、改、查&#xff09; 对 MySQL 数据库的查询&#xff0c;除了基本的查询外&#xff0c;…

R语言forestploter包优雅的绘制孟德尔随机化研究森林图

在既往文章中&#xff0c;我们对孟德尔随机化研究做了一个简单的介绍。我们可以发现&#xff0c;使用TwoSampleMR包做出来的森林图并不是很美观。今天我们使用R语言forestploter包优雅的绘制孟德尔随机化研究森林图。 使用TwoSampleMR包做出来的森林图是这样的 而很多SCI文章…

$.getScript()方法获取js文件

通过$.getScript(‘xxxx.js’)获取xxxx.js文件&#xff0c;这时的ajax是一个get请求的状态&#xff0c;如果进行了入参data的赋值那么他就会跟在url后面,同理获取json文件&#xff0c;css文件。 一开始没想起这茬。。。