【数据结构】八大排序 (三)

目录

前言:

快速排序

快速排序非递归实现

快速排序特性总结

归并排序

归并排序的代码实现

归并排序的特性总结

计数排序

计数排序的代码实现

计数排序的特性总结


前言:

前文快速排序采用了递归实现,而递归会开辟函数栈帧,递归的深度越深,占用栈区的空间就越大,栈区的大小一般是8M,10M,当递归深度足够深时,栈区的空间就会被用完,导致栈溢出,此时需要将递归改为非递归更加稳妥,本篇继续详细解读快排的非递归实现,归并排序,计数排序;

快速排序

快速排序非递归实现

  • 采用递归实现快速排序时,而递归就是不断调用单趟排序函数的功能,若不采用递归,什么可以实现不断调用单趟排序函数的功能?
  • 循环;
  • 循环只要满足循环条件,就会不断调用单趟排序函数的功能,但是每次递归调用时单趟排序函数的参数是变化的,而循环条件确是一成不变的,递归会在栈上建立函数栈帧,而函数栈帧里面存放下次调用该函数的参数,若采用非递归,那我们就必须把每一次循环的参数记录下来,供单趟排序使用,如何解决?
  •  使用顺序栈或者链式栈记录每次函数参数,栈的实现采用动态内存开辟,存储空间是堆区开辟的空间,堆区大小可达2G;

快速排序非递归实现步骤:

  1. 创建一个栈,将整个序列的起始位置和结束位置入栈;
  2. 当栈不为空时,弹出栈顶元素,取出该区间的起始位置 left 和结束位置 right;
  3. 对该区间进行划分,获取划分点 keyi;
  4. 如果keyi左边还有元素,将左半部分的起始位置left和结束位置keyi-1入栈;
  5. 如果keyi右边还有元素,将右半部分的起始位置keyi+1和结束位置right入栈;
  6. 重复步骤2-5,直到栈为空;
  •  栈的特性为后入先出先将待排序序列的右边界 right入栈后将待排序序列的左边界left入栈;出栈时(获取栈顶元素)就可以先取到左边界值left ,后取到右边界值right;

  •  先获取栈顶元素,然后出栈,先取到0给left,后取到7给right,进行单趟排序(hoare版本)

  •  区间被划分为【left,keyi-1】 U keyi U 【keyi+1, right】(left=0, keyi-1=3,keyi+1=5,right=7),为了先处理划分后的左子序列,先将右子区间的边界值right keyi+1分别入栈(先入right,后入keyi+1) ,然后将左子区间的边界值keyi-1,left分别入栈(先入keyi-1,  后入left);

  •  先取栈顶元素,然后出栈,先取到的元素给left ,后取到的元素给right (left=0, right=3), 进行单趟排序(hoare版本);

  •  keyi左边没有元素,keyi右边还有元素,将右半部分的起始位置keyi+1和结束位置right入栈(right先入栈,keyi+1后入栈)

  •   先取栈顶元素,然后出栈,先取到的元素给left ,后取到的元素给right (left=1, right=3), 进行单趟排序(hoare版本);

  •  keyi左边没有元素,keyi右边还有元素,将右半部分的起始位置keyi+1和结束位置right入栈(right先入栈,keyi+1后入栈)

 

  • 左子区间全部被排完,此时才可以取出5和7排右子区间,右子区间按相同流程处理即可;

顺序栈与链式栈的实现:顺序栈与链式栈_顺序栈和链栈-CSDN博客

//快排非递归实现
void QuickSortNonR(int* a, int begin, int end)
{Stack st;InitStack(&st);StackPush(&st, end);StackPush(&st, begin);while (!StackEmpty(&st)){int left = StackTop(&st);StackPop(&st);int right = StackTop(&st);StackPop(&st);int keyi = PartSort(a, left, right);//[left keyi-1] keyi [keyi+1 right]if (right > keyi + 1){StackPush(&st, right);StackPush(&st, keyi+1);}if (keyi - 1 > left){StackPush(&st, keyi - 1);StackPush(&st, left);}}DestroyStack(&st);
}

快速排序特性总结

1. 时间复杂度

因为每次排序都将待排序序列分成两部分,每部分的长度大约为原序列的一半,因此需要进行logn次排序,每次排序的时间复杂度为O(n),所以快速排序的时间复杂度为O(n*logn)

2. 空间复杂度

因为每次排序需要使用递归调用,每次调用需要使用一定的栈空间,所以快速排序的空间复杂度为O(logn)

3. 算法稳定性

快速排序的算法不稳定,这是因为在排序过程中,可能会出现相同元素的相对位置发生变化的情况;当待排序序列中存在多个相同的元素时,快速排序可能会将它们分到不同的子序列中,从而导致它们的相对位置发生变化;

归并排序

归并排序的基本思想:

将待排序的序列分成若干个子序列,每个子序列都是有序的,然后再将这些有序的子序列合并成一个大的有序序列;

具体实现过程通常采用递归的方法,将序列递归地分成两半,对每一半分别进行归并排序,最后将两个有序的子序列合并成一个有序的序列;在合并的过程中,需要开辟一个数组来存储合并后的序列,然后再将临时数组中的元素拷贝回原数组中;

归并排序的基本思想可以总结为以下三个步骤:

  1. 分割:将待排序的序列分成若干个子序列,每个子序列都是有序的;
  2. 合并:将有序的子序列归并到开辟后的数组形成一个大的有序序列;
  3. 复制:将临时数组中的元素复制回原数组中;

归并排序的实现步骤:

  1. 分割:将待排序数组从中间位置分成两个子数组,直到每个子数组只有一个元素为止;
  2. 归并:将两个有序子数组合并成一个大的有序数组;
    • 开辟一个新数组,新数组的大小与原数组大小相同,定义三个指针,分别指向两个子数组和新数组;
    • 比较两个子数组的第一个元素,将较小的元素放入新数组中,并将指向该元素的指针向后移动一位;
    • 重复上一步,直到其中一个子数组的元素全部放入新数组中;
    • 将另一个子数组中剩余的元素依次放入新数组中;

归并排序的代码实现

//归并排序(递归)
//将待排序序列不断二分,直到每个子序列只有一个元素为止,只有一个元素,序列一定有序;
//将相邻的两个子序列合并成一个有序的序列,直到所有子序列都被合并成一个完整的序列;
void SubMergeSort(int* a, int* tmp, int begin, int end)
{if (begin >= end)return;int mid = (begin + end) / 2;//划分区间为[begin,mid]U[mid+1,end]int begin1 = begin, end1 = mid;int begin2 = mid + 1, end2 = end;//递归终止的条件为区间只包含一个元素或者区间不存在;//后序遍历SubMergeSort(a, tmp, begin1, end1);SubMergeSort(a, tmp, begin2, end2);
//首先归并到tmp数组,然后拷贝到原数组;int index = begin;//tmp数组下标while (begin1 <= end1 && begin2 <= end2){if (a[begin1] < a[begin2]){tmp[index++] = a[begin1++];}else{tmp[index++] = a[begin2++];}}//begin1>end1与begin2>end2至少有一个发生while (begin1 <= end1){tmp[index++] = a[begin1++];}while (begin2 <= end2){tmp[index++] = a[begin2++];}//拷贝到原数组memcpy(a + begin, tmp + begin, (end - begin + 1)*sizeof(int));
}
void MergeSort(int* a, int n)
{int* tmp = (int*)malloc(sizeof(int)*n);if (tmp == NULL){perror("malloc failed");return;}SubMergeSort(a, tmp, 0, n - 1);
}

归并排序的特性总结

1. 时间复杂度

归并排序的时间复杂度可以通过递归树来分析;在递归树中,每个节点表示对一个区间进行排序的时间复杂度,而每个节点的子节点表示对该区间的两个子区间进行排序的时间复杂度,因此,递归树的深度为logn,每层的时间复杂度为O(n),因此归并排序的时间复杂度为O(nlogn)

2. 空间复杂度

归并排序的空间复杂度为O(n),因为在排序过程中需要创建一个长度为n的临时数组来存储排序结果;

3. 算法稳定性

归并排序是一种稳定的排序算法,因为在合并两个有序子序列的过程中,如果两个元素相等,那么先出现的元素会先被放入结果数组中,保证了排序的稳定性;

计数排序

计数排序的基本思想:

计数排序不是一个基于比较的排序算法,是记录数据出现次数的一种排序算法;计数排序使用一个额外的count数组,其中第i个元素是待排序数组中值等于i的元素的个数,然后根据count数组来将待排序数组中的元素排到正确的位置;

计数排序的实现步骤:

  1. 遍历原数组,找出原数组中的最大值max,最小值min;
  2. 创建count数组,数组大小为max-min+1,并将其元素初始化为0;
  3. 将原数组里面的值减去原数组最小值min作为count数组的下标映射下来,而count数组里面存放的值就是原数组里面值出现的次数;
  4. 从前向后依次填充数组,填充数组时,只需要加上这个最小值,就能还原出原来的值;

计数排序的代码实现

//计数排序
void CountSort(int* a, int n)
{//寻找最大值,最小值int min = a[0];int max = a[0];for (int i = 0; i < n; i++){if (a[i] < min)min = a[i];if (a[i]>max)max = a[i];}//确定新数组count的大小int range = max - min + 1;int* count = (int*)malloc(sizeof(int)*range);if (count == NULL){perror("malloc failed:");return;}//新数组全部初始化为0,方便计数memset(count, 0, sizeof(int)*range);//统计数据出现的次数for (int i = 0; i < n; i++){count[a[i] - min]++;}//从前向后依次填充原数组int j = 0;for (int i = 0; i < range; i++){while (count[i]--){a[j++] = i + min;}}free(count);
}

计数排序的特性总结

1. 时间复杂度

计数排序的时间复杂度与待排序元素的范围相关,其时间复杂度为O(n+k),其中n为元素数量,k为元素的范围(即最大的元素与最小的元素的差加1);

2. 空间复杂度

计数排序需要额外开辟的空间大小k=max+min-1,所以空间复杂度为O(k)

3. 算法稳定性

计数排序是一个非基于比较的线性时间排序算法,是一种稳定排序

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

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

相关文章

信号类型(通信)——最小频移键控(MSK)

系列文章目录 《信号类型&#xff08;通信&#xff09;——仿真》 《信号类型&#xff08;通信&#xff09;——QAM调制信号》 《信号类型&#xff08;通信&#xff09;——QPSK、OQPSK、IJF_OQPSK调制信号》 目录 前言 一、MSK信号特点 1.1、最小频移 1.2、相位连续 二…

Ubuntu16.04.4系统本地提权实验

目录 1.介绍&#xff1a; 2.实验&#xff1a; 3.总结&#xff1a; 1.介绍&#xff1a; 1.1&#xff1a;eBPF简介&#xff1a;eBPF(extendedBerkeleyPacketFilter)是内核源自于BPF的一套包过滤机制&#xff0c;BPF可以理解成用户与内核之间的一条通道&#xff0c;有非常强大的…

Python的控制流语句使用

Python的控制流语句使用 判断语句 if分支示意图语法介绍注意事项示例 for循环示意图语法介绍列表推导式示例 while循环与for的区别语法介绍示例 判断语句 if分支 示意图 单、双、多分支&#xff1a; 语法介绍 # 单分支 if condition:expression # 双分支 if condition:exp…

Spark-java版

SparkContext初始化 相关知识 SparkConf 是SparkContext的构造参数&#xff0c;储存着Spark相关的配置信息&#xff0c;且必须指定Master(比如Local)和AppName&#xff08;应用名称&#xff09;&#xff0c;否则会抛出异常&#xff1b;SparkContext 是程序执行的入口&#xf…

设计好的测试用例,6大注意事项

设计好的测试用例对于发现缺陷、验证功能、提高可靠性、降低风险和提高效率都具有重要的作用&#xff0c;是保证产品质量和稳定性的重要环节。如果测试用例有问题&#xff0c;可能会导致遗漏缺陷、功能验证不充分、测试效率低下以及误报漏报等问题&#xff0c;从而影响项目的质…

Ubuntu安装nfs服务步骤

Ubuntu安装nfs服务步骤 一、NFS&#xff1f; NFS&#xff1a;网络文件系统&#xff08;Network File system File&#xff09;缩写&#xff0c;可通过网络让不同的机器&#xff0c;不同操作系统之间可以彼此共享文件和目录。 二、安装 1.安装nfs服务器命令&#xff1a;sudo…

BUUCTF-pwn-ciscn_2019_ne_51

简单查看保护&#xff1a; 32为程序没有canary没有PIE&#xff0c;应该是简单的栈溢出。我们照着这个思路去找溢出点在哪&#xff0c;运行下程序看看什么情况&#xff1a; 程序上来是输入一个密码验证。随便输入下错误直接退出。因此我们需要到IDA中看看怎么回事&#xff1a; 主…

F. Magic Will Save the World

首先积攒了能量打了怪再积攒是没有意义的&#xff0c;可以直接积攒好&#xff0c;然后一次性进行攻击 那么怎么进行攻击了&#xff1f;可以尽量的多选怪物使用水魔法攻击剩余的再用火魔法进行攻击&#xff0c; 也就是只要存在合法的体积&#xff08;即装入背包的怪物的体积之…

qt-C++笔记之主线程中使用异步逻辑来处理ROS事件循环和Qt事件循环解决相互阻塞的问题

qt-C笔记之主线程中使用异步逻辑来处理ROS事件循环和异步循环解决相互阻塞的问题 code review! 文章目录 qt-C笔记之主线程中使用异步逻辑来处理ROS事件循环和异步循环解决相互阻塞的问题1.Qt的app.exec()详解2.ros::spin()详解3.ros::AsyncSpinner详解4.主线程中结合使用的示…

笔记63:注意力评分函数

本地笔记地址&#xff1a;D:\work_file\&#xff08;4&#xff09;DeepLearning_Learning\03_个人笔记\3.循环神经网络\第10章&#xff1a;动手学深度学习~注意力机制 a a a a a a a a a a a a a a a a a a a

Python语言学习笔记之五(Python代码注解)

本课程对于有其它语言基础的开发人员可以参考和学习&#xff0c;同时也是记录下来&#xff0c;为个人学习使用&#xff0c;文档中有此不当之处&#xff0c;请谅解。 注解与注释是不一样的&#xff0c;注解有更广泛的应用&#xff1b; 通过注解与注释都能提高代码的可读性和规…

带大家做一个,易上手的家常蒜薹炒瘦肉

首先 从冰箱那一块瘦肉 用水化一下冰 然后 那一把蒜薹 将所有蒜薹头和尾部去掉一小节 这个地方是不能吃的 然后 剩下的部分 切成如下图这样 一小条一小条的样子 然后 将蒜薹倒入盆中清水洗一下 瘦肉清洗一下 然后切片 然后 直接起锅烧油 油烧热后马上下肉翻炒 一定要大点翻…

kafka C++实现生产者

文章目录 1 Kafka 生产者的逻辑2 Kafka 的C API2.1 RdKafka::Conf2.2 RdKafka::Message2.3 RdKafka::DeliveryReportCb2.4 RdKafka::Event2.5 RdKafka::EventCb2.6 RdKafka::PartitionerCb2.7 RdKafka::Topic2.8 RdKafka::Producer&#xff08;核心&#xff09; 3 Kafka 生产者…

【代码】考虑灵活性供需平衡的电力系统优化调度模型

程序名称&#xff1a;考虑灵活性供需平衡的电力系统优化调度模型 实现平台&#xff1a;matlab-yalmip-cplex/gurobi 代码简介&#xff1a;最可再生能源发电设备和并网技术快速发展&#xff0c;以新能源为主导的新型电力系统逐步形成。高比例新能源的随机波动性导致电力系统的…

redis的数据类型的操作增删改查

redis的数据类型的操作增删改查 redis的高可用&#xff1a; 在集群当中有一个非常重要的指标&#xff0c;提供正常服务的时间的百分比&#xff08;365天&#xff09;99.9% redis的高可用的含义要更加宽泛&#xff0c;正常服务是指标之一数据容量扩展&#xff0c;数据的安全性…

Oracle 11g安装过程

文章目录 前言1.下载安装包2.安装2.1本地安装文件2.2 安装过程 3.查看是否安装成功3.1 查看oracle是否安装成功3.2 查看oracle服务 前言 本文仅用于记录亲自安装oracle的过程 1.下载安装包 官网地址&#xff1a; Oracle Database 11g Release 2 (11.2.0.1.0) 注意&#xff…

数据治理框架和成熟度模型

数据治理成熟度模型 一个企业的数据治理能力越高&#xff0c;所享受到数据治理带来的价值也会越多&#xff0c;如增加收入、减少成本、降低风险等。于是&#xff0c;很多企业想要准确地评估本公司的数据治理能力&#xff0c;可以利用数据治理成熟度模型方法&#xff0c;包括 D…

Javaweb之Vue组件库Element案例的详细解析

4.4 案例 4.4.1 案例需求 参考 资料/页面原型/tlias智能学习辅助系统/首页.html 文件&#xff0c;浏览器打开&#xff0c;点击页面中的左侧栏的员工管理&#xff0c;如下所示&#xff1a; 需求说明&#xff1a; 制作类似格式的页面 即上面是标题&#xff0c;左侧栏是导航&…

泛微OA对接金蝶云星空方案分享(对接场景解析)

分享金蝶云星空跟泛微OA系统集成对接的方案分享&#xff0c;主讲审批流程对接&#xff0c;表单对接的两类场景。分别是金蝶云星空发起申请和泛微发起流程审批&#xff0c;最终实现统一管理。 数据集成主要有以下好处&#xff1a; &#xff08;1&#xff09;数据一致性&#xf…

吉利展厅 | 透明OLED拼接2x2:科技与艺术的完美融合

产品&#xff1a;4块55寸OLED透明拼接屏 项目地点&#xff1a;南宁 项目时间&#xff1a;2023年11月 应用场景&#xff1a;吉利展厅 在2023年11月的南宁&#xff0c;吉利展厅以其独特的展示设计吸引了众多参观者的目光。其中最引人注目的亮点是展厅中央一个由四块55寸OLED透…