数据结构实验快速排序、冒泡排序算法(交换排序),使用STM32单片机测试(学计算机综合考试408悟单片机系列)

2023-05-16

快速排序和冒泡排序均属于交换排序范畴,意味着其基本操作是交换两数。

快速排序,顾名思义快速的排序,毫无遮拦得介绍了自己得特征。而冒泡排序也正如其名称,如同养鱼冒泡一样慢条斯理锝排序。(说笑了,哈哈哈)

本文所提及的算法测试时,使用随机数来进行排序算法的测试,其中随机数产生的方法请见

https://blog.csdn.net/Fairchild_1947/article/details/118757154

下面言归正传,介绍两种排序得原理和详细的测试过程。时间、空间复杂度的数量级和真实运行中消耗的时间和内存空间的对比,将于文末展示。

首先,上考点

快速排序和冒泡排序的性质
算法名称时间复杂度空间复杂度是否稳定
最好情况平均情况最坏情况
快速排序O(n)O(n²)O(n²)O(1)是        
冒泡排序O(nlog2n)O(nlog2n)O(n²)O(log2n)

快速排序在排序有序数列时,时间复杂度将为O(n²),且其一般由于使用递归的运行方式,其空间复杂度也将达到O(n)。顾在使用快速排序时,需要注意其非常不适合排序已经有一定顺序的序列。(该现象会在后面的测试部分进行实际的测试)

快速排序在理想状态下,每次选择的枢轴都是该区域正中间的数值,以此进行下去,其递归运行的过程类似于该数列的平衡二叉树,其空间复杂度的O(log2n)也是因为平衡二叉树总共有log2n层,故在递归时需要log2n层的压栈。(实际的内存资源消耗也会在其后的测试部分具体体现)

既然上文已经提到了快速排序,那就请牛逼哄哄的快速排序先上场了,快速排序将会把第一个数值选定为枢轴元素,并将比枢轴大的元素移动到枢轴后面,将比枢轴小的元素移动到枢轴前面,最终枢轴将会移动到其排序后的最终位置。此后,将以枢轴为接线划分为前后两部分,再将前后两部分重复上述操作,选定第一个元素为枢轴,并将比枢轴大的移动到枢轴后面,将比枢轴小的...... 以此一次次迭代下去,最终将会把所有的元素放在其最终的位置上。

快速排序的动态演示如下:

图中红色元素为选中元素,黄色元素为枢轴元素,绿色元素为小于枢轴的元素,紫色的元素为大于枢轴的元素,橙色元素为已经确定最终位置的元素。

 代码分为下标使用无符号数的版本和有符号数的版本,因为下边不可能为负数,但是在排序的过程中,下标变量有时会变成幅值,如(i>=0)这样的判断,若i为无符号数,其将满足条件,顾在程序运行时会引发异常。

其次代码部分与王道书本主要的不同之处有两处:

1.单独定义变量用于哨兵而不是在数组中,因为这是非常不显示的事情,因为一般情况下,需要排序的数组中数据都是从下标为0号的元素开始存放的,若要将0号元素作为哨兵则代码失去了很好的移植性。

2.形参从(数组首地址,数组长度)改为(数组首地址,开始排序的下标,结束排序的下标),这样可以满足对数组中任意部分排序,而不是死板地每次排一整个数组。

3.变量定义均在函数开头,由于C51编译器无法支持C99模式,在函数中定义变量将会导致报错,为满足移植此排序算法到8051单片机,代码编写均向下兼容C51编译器。

最后,在功能方面,该函数这对无符号32位整型数组排序,若需要为其它类型的数据排序,可直接修改数组数据类型。

下标无符号版本:

/* 计算枢轴位置并将枢轴元素移动到最终位置 */
int Partition(uint32_t array[], uint32_t start, uint32_t end)
{
	uint32_t pivot = array[start];						//获取枢轴
	while(start < end){
		while(start < end && array[end]>=pivot) end--;	//必须使用>=若仅仅使用>则会在array[start]和array[end]的数值相等时死循环
		array[start] = array[end];						//找到小于枢轴的元素,交换到枢轴左侧
		while(start < end && array[start]<=pivot) start++;//必须使用>=若仅仅使用>则会在array[start]和array[end]的数值相等时死循环
		array[end] = array[start];						//找到大于枢轴的元素,交换到枢轴右侧
	}
	array[start] = pivot;
	return start;
}
/* 快速排序算法,无符号整形,无符号下标版本 */
void QuickSort_UINT32(uint32_t array[], uint32_t start, uint32_t end)
{
	uint32_t pivotpos;			//枢轴元素位置信息变量定义
	if(start < end){
		pivotpos = Partition(array, start, end);//获取枢轴元素位置信息
		if(pivotpos > 0){
			QuickSort_UINT32(array, start, pivotpos-1);	//对枢轴左侧元素进行排序,由于下标信息使用无符号数定义,顾需要额外判断是否大于零		
		} else{
			;
		}
		QuickSort_UINT32(array, pivotpos+1, end);//对枢轴右侧元素进行排序
	}
}

有符号版本:

/* 计算枢轴位置并将枢轴元素移动到最终位置 */
int Partition_UINT32(uint32_t array[], int32_t start, int32_t end)
{
	uint32_t pivot = array[start];						//获取枢轴
	while(start < end){
		while(start < end && array[end]>=pivot) end--;	//必须使用>=若仅仅使用>则会在array[start]和array[end]的数值相等时死循环
		array[start] = array[end];						//找到小于枢轴的元素,交换到枢轴左侧
		while(start < end && array[start]<=pivot) start++;//必须使用>=若仅仅使用>则会在array[start]和array[end]的数值相等时死循环
		array[end] = array[start];						//找到大于枢轴的元素,交换到枢轴右侧
	}
	array[start] = pivot;
	return start;
}
/* 快速排序算法,无符号整形,无符号下标版本 */
void QuickSort_UINT32(uint32_t array[], int32_t start, int32_t end)
{
	uint32_t pivotpos;			//枢轴元素位置信息变量定义
	if(start < end){
		pivotpos = Partition_UINT32(array, start, end);//获取枢轴元素位置信息
		QuickSort_UINT32(array, start, pivotpos-1);	//对枢轴左侧元素进行排序,由于下标信息使用无符号数定义,顾需要额外判断是否大于零		
		QuickSort_UINT32(array, pivotpos+1, end);//对枢轴右侧元素进行排序
	}
}

有符号版本与无符号版本相比较减少了对枢轴元素位置是否为零的判断,在实际运行时略快于无符号版本。但是有符号版本千万要注意start和end必须使用大于0的数值,否则将会导致将负值作为数组下标导致访问越界。

接下来,测试该算法

测试内容分别为:对长度为32的随机数表排序并完整显示排序结果验证算法的正确性、对长度为1024的随机数表排序的时间和空间使用、对长度为1024的有序数表进行排序的时间和空间使用。

测试环境部分要点说明:

1.时间复杂度测试,毫秒级别的使用FreeRTOS嵌入式实时操作系统提供的osKernelGetTickCount()(原函数为xTaskGetTickCount())函数分别在排序算法运行前和运行后获取时间戳,相减得到时间长度。微秒级别的时间使用定时器测量原理与毫秒级别测试相似,在算法执行之前清零并打开定时器算法结束时读取定时器数值并关闭定时器。通过毫秒级和微秒级分别测量,可以避免定时器计满溢出的问题。

2.空间复杂度测试使用FreeRTOS嵌入式实时操作系统提供的内存最大水位线测试函数osThreadGetStackSpace()(原函数为uxTaskGetStackHighWaterMar())进行测试,分别在算法运行之前和运行之后测试两次,并得出内存的使用情况。

使用长度为32的随机数表验证算法的正确性:

 使用长度为1024的随机数表进行算法性能测试:

快速排序若遇到局部有序的序列,将不能很有效得划分区域得到近似一个平衡二叉树,会导致递归的深度变深,递归的次数变多导致时间和空间使用增加。

 上文提到快速排序不利于排序有序数列,接下来做个最坏的情况的测试,使用冒泡排序先将随机数表排序成有序,再使用冒泡排序进行排序,可以看到此时快速排序确实需要大量的时间和空间。

快速排序的测试完成了,接下来轮到冒泡排序了

冒泡排序顾名思义,就是做着冒泡得动作完成排序。如同在水中用吸管在水的底部吹入油滴,油滴先和水比密度,油滴小,所以上浮,到达水表面后再和空气比密度,油比空气密度大所以不再上浮。同理,将空气通过吸管吹入水底部,空气先与水比密度,空气密度小,待上浮到水表面后,再和油比密度,空气密度小,所以继续上浮到油表面。

通过这个例子可以看出,冒泡排序的特点是:逐个比较,不断上浮。

冒泡排序的动画演示如下:

图中绿色部分为逐个比较时正在比较的两个元素,橙色部分为确定最终位置的元素。

 冒泡排序在具体实现上,主要分为比较和上浮两个动作进行实现。其中比较形式确定,直接编写在算法函数内,而上浮动作是两数交换的动作,两数交换的动作具体实现分为借助第三变量和不借助第三变量两种方式。

冒泡排序算法主体代码:

void BubbleSort_UINT32(uint32_t array[], uint32_t start, uint32_t end)
{
	uint32_t i,j;
	bool flag;
	for(i=start; i<end; i++){
		flag = false;
		for(j=end; j>i; j--){
			if(array[j-1]>array[j]){
				Swap_UINT32(&array[j-1], &array[j]);    //借助第三变量交换两元素
              //Swap_UINT32_XOR(&array[j-1], &array[j]);//不借助第三变量交换两元素
				flag = true;
			}else{
				;
			}
		}
		if(flag == false){
			return;
		}
	}
}

借助第三变量交换两元素代码:

void Swap_UINT32(uint32_t *a, uint32_t *b)
{
	uint32_t temp;
	temp = *b;
	*b = *a;
	*a = temp;
}

不借助第三变量交换两元素代码:

void Swap_UINT32_XOR(uint32_t *a, uint32_t *b)
{
	*a = (*a)^(*b);
	*b = (*a)^(*b);
	*a = (*a)^(*b);
}

接下来,测试此算法。

测试内容有:分别使用两种交换两元素的方法对长度为32的随机数表排序验证算法的正确性、分别使用两种交换两元素的方法对长度为1024的随机数表进行排序并测定算法使用的时间和空间。

测试环境同快速排序。

冒泡排序正确性测试:

 使用借助第三变量交换两数的方式排序随机数表:

 不借助第三变量交换两数的方式排序随机数表:

现在交换排序的两种算法都有了结果,下面将对比起时间、空间复杂度与运行过程中真实消耗的时间和空间。

1.时间复杂度:冒泡排序O(n²),快速排序O(nlog2n),直接对复杂度做比可得,对于相同的数据表,冒泡排序与快速排序的速度比为n/log2n,本文所述测试环境中,均使用长度为1024的数组,代入计算得,冒泡排序应当比快速排序大约慢100倍。实际的情况是,冒泡排序大约在200MS左右,快速排序在4.7MS左右,虽然不是100倍,但数量级相同。

2.空间复杂度:冒泡排序O(1),快速排序O(log2n)。通过实际测试可得,冒泡排序在运行时,确实没有使用内存资源。而快速排序使用的内存量带入数据表的长度1024得,使用10个内存。在实际使用时,由于本测试平台使用的STM32单片机内部的处理器是Cortex-M3,该处理器为32位不带浮点单元的处理器,所以在递归调用时保存的断点信息、部分寄存器值和中间变量值,所以实际大约需要40字左右的空间,该大小符合数量级。

综上所述,可以明显看出,快速排序是远快于冒泡排序的,但是快速排序消耗的内存资源也是比较多的,尤其在遇到有序序列时,会吞下非常巨大的内存。这使得在设计使用快速排序算法的程序时,必须要考虑到数表的实际情况,是否有可能出现有序序列,其次也需要为快速排序算法留出比较大的内存空间。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

数据结构实验快速排序、冒泡排序算法(交换排序),使用STM32单片机测试(学计算机综合考试408悟单片机系列) 的相关文章

  • 用SST89E516RD自制51单片机仿真器

    原文网址 xff1a http www1 eccn com tech06 te074653 asp 用SST89E516RD自制51单片机仿真器 文 xff0f 吴汉清 单片机实验和开发中最重要的一个环节就是程序的调试 xff0c 在业余条
  • Latex (一) 安装和环境变量的设置

    一 安装 Tex有很多不同的版本 xff0c 很多人喜欢用ctex xff0c 但是最推荐是官方版本Texlive 搜了很多资料 xff0c 一般windows的话 xff0c 可以将Tex live 43 Tex studio作为标配 x
  • KITTI 数据集 参数—— tracking devkit中的rotation_y和alpha角

    根据devkit中的readme txt和cs overview pdf的描述以及根据通过对数据集做的小实验总结的 xff0c 如果过有错误的地方欢迎指正 61 61 61 61 61 61 61 61 61 61 61 61 61 61
  • 拯救者Y7000P 安装Ubuntu16.04问题解决

    先列一下问题 xff1a 1 wifi开不来了 xff1b 2 触摸板没法用 3 休眠后打不开 目前1 3 xff0c 解决了 xff0c 但是2依然没法解决 xff0c 不过问题不大 xff0c 大不了用鼠标 首先 xff0c 问题的原因
  • VSCode python调试库代码以及添加相关扩展支持opencv

    调试python 代码的时候可以再launch json 文件中添加 justMycode 34 false 来调试安装的包的代码 由于opencv 底层调用的C xff0c 所以如果要在代码提示中正确提示可能要安装额外插件 xff1a 比
  • vscode python包的引用一些问题

    个人使用vscode碰到的一些python包的引用问题以及尝试解决的一些办法 xff0c 可能只适用我自己的情况 项目目录大概如下 xff1a lib是根目录下的一个文件夹 xff0c 里面每个文件夹都是一个python 包 xff0c 都
  • MATLAB 矩阵的化简rref()函数

    在用MATLAB求解线性方程组的时候 xff0c 可以使用 rref 函数对矩阵进行化简 xff0c 从而很方便直观的得到原方程的解 xff0c 举一个简单的例子 xff1a 解下列线性方程组 则用MATLAB的rref函数解上述方程组的代
  • MATLAB求符号函数的函数值的方法

    在MATLAB中定义函数的方法有许多种 xff0c 比较常用的一种是定义符号变量 x 和 y 举一个简单的例子 xff1a 对函数 y 61 x 2 用上述方法的MATLAB语言如下 xff1a syms x y y 61 x 2 要想画出
  • C++寻找数组最大值和最小值

    寻找数组中的最大最小值 include lt iostream gt using namespace std include lt algorithm gt int main int n cin gt gt n int p 61 new i
  • Excel如何同时查找多个数据

    在使用多个excel表的时候 xff0c 有时需要在一个表中查找另一个表中的某些信息 xff0c 怎样能一步到位 xff0c 将所有要查找的信息一次找出来而不是一个个的Ctrl 43 F xff1f 这是前几天帮辅导员老师统计新生的数据时遇
  • python tkinter 全部组件(widget)及事件类型(event)一览

    对于一个简单的GUI程序设计来说 xff0c 我觉得无非就是三个要素 xff0c widget xff08 部件 xff09 xff0c layout xff08 布局 xff09 xff0c event xff08 事件的响应 xff09
  • DS18B20 1-WIRE ROM搜索算法详解

    转自 xff1a http blog sina com cn s blog 57ad1bd20102uxxw html 1 WIRE 搜索算法详解 xff08 1 xff09 0 前言 美信公司 xff08 http www maximin
  • 关于python tkinter 多线程依然无响应问题

    今天解决了一个GUI程序的多线程问题 因为GUI程序在执行高IO操作的时候容易出现假死和无响应的状态 xff0c 所以需要用到多线程 但我的程序开了线程之后依然是无响应状态 几次尝试 xff0c 终于找到问题所在 1 首先 xff0c 我的
  • Ubuntu内核的查看、更新、卸载、取消及启用自动更新

    1 查看当前内核版本 xff1a uname r 2 升级内核 xff1a sudo apt get update sudo apt cache search linux image 查看可用内核 在选择合适的内核后 xff0c sudo
  • 孤立森林(Isolation Forest)

    背景 现有的异常检测方法主要是通过对正常样本的描述 xff0c 给出一个正常样本在特征空间中的区域 xff0c 对于不在这个区域中的样本 xff0c 视为异常 这些方法的主要缺点是 xff0c 异常检测器只会对正常样本的描述做优化 xff0
  • FreeRTOS三种数据结构区别(StreamBuffer,MessageBuffer,Queue)

    Queue队列是最基本的数据结构 xff0c 在FreeRTOS v10 0后提供了另外两种高级数据结构为Streambuffer和MessageBuffer xff0c 称为流式缓冲区和消息缓冲区 FreeRTOS 嵌入式系统开源 Fre
  • ubuntu16安装librealsense 以及在ros上使用 [深度相机sr300]

    记录ubuntu16安装librealsense 和ros包的过程 xff0c 还有一些遇到的问题 温馨提醒 如果按照下面步骤每一步完成 xff08 都没报错 xff09 xff0c 还是不能显示图像 xff0c 换个usb3 0口试试或者
  • 原生安卓苹果APP-java抢单派单系统平台源码

    简介 xff1a java源码 派单系统平台源码完整版带项目说明 网盘下载地址 xff1a http kekewl cc 9qsCp179URb0 图片 xff1a
  • 基于Android和OpenCV的物体跟随系统设计 需要留言

    本设计为基于Android和OpenCV的物体跟随系统设计 本文对基于计算机视觉的物体跟随系统的特点和应用领域 国内外的研究现状及其发展分别做出了较详尽介绍 并且按照社会科技化进步的要求 xff0c 给出了具有参考意义的智能跟随模块系统 根
  • 【Linux C王者归来】【第十一章】【进程控制】

    1 程序可以有多个进程 xff0c 一个进程与进程id11 对应 2 PROC中的数字对应id号 xff0c getpid和getppid可以获得进程id父进程id 3 getuid geteuid 获得进程用户id和有效用户id 4 ge

随机推荐

  • DSP28335使用FIFO的串口中断总结

    一 串行通信与并行通信 DSP控制器间 xff0c DSP控制器与外部设备间交换信息 xff0c 通信 xff0c 可采取的通信方式主要两大类1 串行通信 2 并行通信 并行通信一般包括多条数据线 多条控制线和状态线 xff0c 传输速度快
  • 点阵屏上绘图——基于LCD12864 控制详解

    本文引用自 xff1a http blog csdn net s3c44b0x article details 7498706 原始地址 xff1a http www amobbs com thread 591361 1 1 html 相关
  • 使用iPad编写C++程序(转载)

    使用iPad编写C 43 43 程序 一 搭建C 43 43 环境 1在cydia内安装 deb 包 注 xff1a 在cydia 软件源 设置中改为开发者 xff0c 否则有些deb搜索不到 OpenSSH xff0c OpenSSL w
  • Python多线程学习(三、生产者与消费者)

    生产者与消费者问题是典型的同步问题 这里简单介绍两种不同的实现方法 1 xff0c 条件变量 view plaincopy to clipboardprint import threading import time class Produ
  • 在~Firmware下面用roslaunch 启动launch 报错 udp0: sendto:Invalid argument

    在 Firmware下面用roslaunch 启动launch 报错 xff0c 如下 roslaunch px4 mavros posix sitl launch 报错 ERROR 1658284290 546891096 udp0 se
  • roslaunch运行px4功能包 报错

    运行条件ubuntu 16 04 ros kinetic 隔段时间运行roslaunch 会如下错误 mavros posix sitl launch is neither a launch file in package px4 nor
  • tf2_ros::Buffer::Buffer(ros::Duration, bool)’未定义的引用

    新建一个功能包及 cpp文件后报错tf2 ros Buffer Buffer ros Duration bool 未定义的引用 opt ros kinetic include tf2 ros buffer h 51 xff1a 对 vtab
  • Android Studio 配置 JDK1.8 使用Lambda表达式

    Android Studio 配置 JDK1 8 使用Lambda表达式 JDK1 8 添加几项新特性譬如对集合的优化语法的便捷配合Lambda表达式使用可以让代码更加简便美观 xff0c 但对于一些没有接触Lambda表达式的同学们来说就
  • 深入解读四轴飞行器的硬件设计

    xfeff xfeff 转载自 xff1a http www openedv com posts list 20892 htm 传感器之一 xff1a 角速度传感器应用科里奥利力原理 xff1a 科里奥利力来自于物体运动所具有的惯性 xff
  • 【GIT】使用Vscode同步git仓库,错误和解决方法记录

    这里写目录标题 命令行操作仓库常见命令1 报错 在签出前 xff0c 请清理存储库工作树 2 报错 fatal unable to access 39 https github com 39 OpenSSL SSL read Connect
  • 基于k近邻(KNN)的手写数字识别

    作者 xff1a faaronzheng 转载请注明出处 xff01 最近再看Machine Learning in Action k近邻算法这一章节提供了不少例子 xff0c 本着Talk is cheap的原则 xff0c 我们用手写数
  • 机器学习算法地图(转自SIGAI)

    转自 xff1a http sigai cn paper 18 html 下面先看这张图 xff1a 图的左半部分列出了常用的机器学习算法与它们之间的演化关系 xff0c 分为有监督学习 xff0c 无监督学习 xff0c 强化学习 3 大
  • 一个非常适合单片机的滤波算法

    连接 xff1a http bbs 21ic com icview 170880 1 1 html 以下为原文 连接 xff1a http bbs 21ic com icview 170880 1 1 html 单片机大多资源小 xff0c
  • 6.蒙特卡洛方法(TSP)

    定义 xff1a 旅行商问题 xff0c 即TSP问题 xff08 Traveling Salesman Problem xff09 又译为旅行推销员问题 货郎担问题 xff0c 是数学领域中著名问题之一 假设有一个旅行商人要拜访n个城市
  • 云和AI时代下,需要怎样的存储?

    数字化浪潮席卷而来 xff0c 颠覆着传统的生产和生活方式 xff0c 随之产生的新经济和新应用给传统业务模式和产业结构带来前所未有的冲击 特别是云计算 人工智能 AI 和物联网 IoT 的兴起 xff0c 加快了传统产业升级改造的步伐 在
  • 人工智能6个核心技术

    机械学习 机械学习是多领域交叉的学科 xff0c 可以从学习模式和学习方法上面进行分类 xff0c 学习模式将机器学习分类为监督学习 无监督学习和强化学习等 xff0c 学习方法可以将机器学习分为传统机器学习和深度学习 机器学习按学习方式分
  • CAN总线的标准(二)

    一 OSI参考模型 CAN总线标准规定了物理层和数据链路层 xff0c 至于应用层需要用户自定义 不同的CAN标准仅物理层不同 物理层和数据链路层ISO11898 xff1b 应用层 xff1a 不同的应用领域使用不同的应用层标准 二 各层
  • 数据通信--ASCII码通信&16进制通信的区别

    16进制通信一般用于网络传输等的通信上 xff0c 传输效率高 数据量大是二进制通信 ASCII码通信一般用与串口通信等通信上 xff0c 数据量小 易于处理 便于调试 xff0c 它虽然是文本模式 xff0c 但本质仍然是二进制通信 在使
  • ubuntu下安装boost

    boost中 xff0c 用到了别的函数库 xff0c 所以为了使用boost中相应的功能 xff0c 需要先安装系统中可能缺失的库 第一步 安装依赖库 sudo apt get install mpi default dev 安装mpi库
  • 数据结构实验快速排序、冒泡排序算法(交换排序),使用STM32单片机测试(学计算机综合考试408悟单片机系列)

    快速排序和冒泡排序均属于交换排序范畴 xff0c 意味着其基本操作是交换两数 快速排序 xff0c 顾名思义快速的排序 xff0c 毫无遮拦得介绍了自己得特征 而冒泡排序也正如其名称 xff0c 如同养鱼冒泡一样慢条斯理锝排序 xff08