c语言数据结构(10)——冒泡排序、快速排序

欢迎来到博主的专栏——C语言数据结构
博主ID:代码小豪

文章目录

    • 冒泡排序
    • 冒泡排序的代码及原理
    • 快速排序
    • 快速排序的代码和原理
    • 快速排序的其他排序方法
    • 非递归的快速排序

冒泡排序

相信冒泡排序是绝大多数计科学子接触的第一个排序算法。作为最简单、最容易理解的排序算法,冒泡排序虽然效率不高,但是冒泡排序的思路给了其他排序算法一些启发。

冒泡排序的思路如下:
(1)从第一个数据开始,向后继元素比较大小、若满足条件(大于或小于),则交换数据的位置,然后从后一个位置开始继续比较。
(2)当N-1与N进行数据比较后、完成一趟冒牌排序,然后从头开始继续进行冒牌排序
(3)完成N-1趟排序后、冒牌排序结束。

在这里插入图片描述
在这里插入图片描述
可以发现、在第一趟冒泡排序结束后,最大值7排到了最后一位,而升序数组中的7也是排到最后一位。这一趟冒泡排序就确定了升序数组中的一位。

当第二趟冒泡排序开始时,由于最后一位的数据已经确定了,因此不再需要参与排序,将end向前移动一位,以此类推。
在这里插入图片描述

冒泡排序的代码及原理

void Swap(int* e1, int* e2)//交换函数
{int tmp = *e1;*e1 = *e2;*e2 = tmp;
}
void BubbleSort(int* a, int n)
{for (int i = 0; i < n-1; i++)//共计排n-1趟{int end = n - i - 1;for (int begin = 0;//每趟冒泡排序从0开始begin < end;//当begin=end时结束begin++){if (a[begin] > a[begin + 1])//满足条件交换数据{Swap(&a[begin], &a[begin + 1]);}}}
}

冒牌排序的原理如下:
每一趟冒泡排序都能使至少一个数据处于正确的升降序的位置。即每趟排序都能让当前范围的最大值,位于最后一个位置,好比水中的鱼吐出的泡泡,从水底到水面渐渐变大。
在这里插入图片描述

快速排序

快速排序和冒泡排序都有一个相似之处,或者是一种排序的思路,即一趟排序完成单个或多个数据的定位(将数据排在最终确定的位置上),多次排序后将所有数据都完成定位,最终完成排序操作。

冒泡排序的思路是每次确定最后一位,因此每趟排序都需要将整个数组遍历一遍,导致时间复杂度非常高。快速排序作为最著名昭著的排序算法,以高效率深受程序员喜爱,这里讲讲快排的优化之处。

先来了解一下快速排序的步骤:
(1)选取数组中的任意一个数据作为关键数据(key)
(2)让key左边的数据小于(大于)key,右边的数据大于(小于)key
(3)将整体分割为两部分,一部分是key左边,另一部分是key右边,以这些部分作为一个整体,重复(1)(2)(3)操作,直到所以数据不可分割为止。

这个思路单凭讲述时很难理解其中奥义的。我们先拆分步骤逐渐讲解。
(1)选取key可以选择区间中的任意数据,选取方法通常有三种:取队首、取队尾、随机值取中间数,通常来说结合三数取中(即在队首、队尾、随机之间选择中间数作为key)是最合理的。但这里为了方便讲解,选择取队首作为key

(2)中让key左边的数据小于key,右边的数据大于key有多种方法,总体而言对效率影响不大,我们先来了解快排发明者霍尔的方法。

首先将待排序的区间选出,队首第一个值为key值。区间最左边设置一个left,最右边设置一个right。如图:
在这里插入图片描述

让right往左边遍历,若遍历的数据小于key,则right停留在该数据处。
在这里插入图片描述

当right找到比key小的数后,让left向右遍历找到比key大的数。找到比key大的数后,停留在该数据处。
在这里插入图片描述
当left和right都停留时,交换left和right
在这里插入图片描述
交换结束后,重复上述步骤,让right向左遍历。
在这里插入图片描述
此时left继续向右遍历,left与right重合,此时将key与重合位置的数据交换。
在这里插入图片描述
以key为分割线,将这段区间分为两个区间。
在这里插入图片描述
再对这两个区间进行快排。以此类推。
在这里插入图片描述
当某个区间的left和right重合,数据不可再分割,不再需要排序。
在这里插入图片描述

快速排序的代码和原理

快速排序的原理如下:
和冒泡排序有个类似的点,每一趟冒泡排序都有一个数据被定位,快速排序也是如此,key位置的左边小于key,右边大于key,那么key这个位置就是符合排序要求的位置的。那么快速排序比冒泡排序有效率的点在哪呢?

冒泡排序每趟排序都需要将整个数组遍历一遍。快速排序使用了一种分治的方法,将区间分割成多个小区间进行排序,减少了需要遍历的元素个数。
在这里插入图片描述
此时快速排序就不再需要遍历整个数组了,而是遍历小区间,遍历区间1只用遍历4个数据,定位一个,区间2只需遍历2个数据,定位1,当数据数量变得很多时,快速排序的优势更加明显,效率更快。

代码如下:

void QuickSort(int* a, int begin, int end)
{int left = begin;int right = end-1;if (left >= right)//区间不可再分割,结束递归{return;}int key = left;//队首取keywhile (left < right)//遍历条件{while (left < right && a[key] < a[right])//right向左寻找比key小的值{right--;}while (left < right&&a[key]>a[left])//left向右寻找比key大的值{left++;}Swap(&a[left], &a[right]);//找到之后交换}Swap(&a[key], &a[left]);//left与right相等,与key交换。key = left;QuickSort(a, begin, key);//分割QuickSort(a, key + 1, end);//分割
}

快速排序的其他排序方法

这里介绍另外一种排序方式——前后指针法。霍尔使用left和right的找寻方法非常巧妙,但是代码较复杂。这里讲一种较为简洁的找寻方法(对效率影响不大)。

定义一个prev和cur指向队首与队首的后一个元素,key值仍取队首。

步骤如下:
(1)判断cur的值是否大于prev
若大于:cur往前移动一位,prev不变。
若小于:prev往前移动一位,与cur交换数据,cur再往前移动一位。(若prev指针与cur相遇,不交换)。
(2)当cur超出区间后,让key与prev交换数据。
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
剩下也是将key分割两个区间继续分治,只是找寻方法改变了。

为什么这种方法能完成key的左边比key小,右边比key大呢?
首先,我们可以发现,cur与prev之间的元素都是比key大的。我们重新回顾一下步骤。

(1)最初开始时,prev与cur之间不存在任何数据(因为他们相邻)。
(2)cur与prev最初是挨着的,当cur遍历到比key大的树据才会有所距离(此时距离之间的数据都是比key大到数)。
(3)为了保持cur与key之间的数都是大于key值,需要将prev后一位的数据(一定大于key),与小于key值的cur进行交换
(4)最后prev的值一定是小于key的(因为此时prev的值是从小于key的cur值里交换而来的)。所以prev一定小于key。

如果prev和cur中间的数据保持比key大,那么当cur超出范围时,key左边一定小于key,右边一定大于key

代码如下:

void QuickSort(int* a, int begin, int end)
{if (begin >= end)//区间不可分割时,停止递归{return;}int prev = begin;int cur = begin + 1;int key = begin;while (cur < end)//cur没超出区间{if (a[cur] <= a[key])//当cur小于key的值时{prev++;if (prev != cur){Swap(&a[prev], &a[cur]);}}cur++;//不管是大于key还是小于key,cur最后都要++}Swap(&a[prev], &a[key]);key = prev;QuickSort(a, begin, key);QuickSort(a, key+1, end);
}

非递归的快速排序

前面讲了快排的递归形式,但是递归就说明需要大量的调用堆栈,一旦递归深度过高,就会导致栈溢出,因此有人想出了快速排序的非递归方法。

想要替代递归的作用,就得先找到为什么使用递归以及递归是为了实现什么。

快速排序中的递归起的作用是为了记录分割的区间的起始点与结束点

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

如果我们有办法将每趟快速排序的分割区间记录下来。就能取代递归的作用。

我们可以创建一个栈,用来记录快速排序的区间。这样子就不再需要递归了。

记录的步骤如下:
(1)将左区间和右区间压入栈中
(2)将左区间和右区间弹出。将弹出的数据作为一个区间进行一趟快速排序
(3)完成快速排序后,将新分割好的节点压入栈中
(4)若取出的左右区间不构成新区间,不执行这个操作。
循环往复,直到栈变成空栈。

代码如下:

void QuickSort(int* a, int begin, int end)
{stack s1;StackInit(&s1);//初始化栈StackPush(&s1,begin);//压入左区间StackPush(&s1,end);//压入右区间while (!StackEmpty(&s1)){int right = StackTopData(&s1);//出栈StackPop(&s1);int left = StackTopData(&s1);//出栈StackPop(&s1);int cur = left+1;int prev = left;int key = left;while (cur < right){if (a[cur] < a[key]){prev++;if (prev != cur){Swap(&a[cur], &a[prev]);}}cur++;}Swap(&a[prev], &a[key]);key = prev;//[left,key],[key+1,right]if (left < key){StackPush(&s1, left);//压入分割后的左区间StackPush(&s1, key);//压入分割后的右区间}if (key + 1 < right){StackPush(&s1, key + 1);//压入分割后的左区间StackPush(&s1, right);//压入分割后的右区间}}StackDestory(&s1);
}

栈的实现函数

void StackInit(stack* ps)//栈的初始化
{ps->data = NULL;ps->capacity = 0;ps->top = 0;
}void StackPush(stack* ps, datatype n)//压栈
{assert(ps);if (ps->top == ps->capacity){int newcapacity = ps->capacity == 0 ? 4 : 2 * ps->capacity;datatype* tmp = (datatype*)realloc(ps->data, sizeof(stack) * newcapacity);assert(tmp);ps->data = tmp;ps->capacity = newcapacity;}ps->data[ps->top] = n;ps->top++;
}void StackPop(stack* ps)//出栈
{if (StackEmpty(ps)){perror("Stack is empty\n");return;}ps->top--;
}bool StackEmpty(stack* ps)//判断是否空栈
{return ps->top == 0;
}datatype StackTopData(stack* ps)//取栈顶
{if (StackEmpty(ps)){perror("Stack is empty\n");exit(1);}return ps->data[ps->top - 1];
}void StackDestory(stack* ps)//销毁栈
{assert(ps);free(ps->data);ps->data = NULL;ps->top = 0;ps->capacity = 0;
}

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

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

相关文章

MySQL编程实战LeetCode经典考题

文章简介 本文主要收集了LeetCode上关于MySQL的一些经典考题。 后续也会陆续把所有经典考题补充完整。 175.组合两个表 175.组合两个表 解答&#xff1a; select p.FirstName as firstName, p.LastName as lastName,a.City as city, a.State as state from Person p l…

使用vite创建一个react18项目

一、vite是什么&#xff1f; vite 是一种新型前端构建工具&#xff0c;能够显著提升前端开发体验。它主要由两部分组成&#xff1a; 一个开发服务器&#xff0c;它基于原生 ES 模块提供了丰富的内建功能&#xff0c;如速度快到惊人的模块热更新&#xff08;HMR&#xff09;。 …

Lafida多目数据集实测

Lafida 数据集 paper&#xff1a;J. Imaging | Free Full-Text | LaFiDa—A Laserscanner Multi-Fisheye Camera Dataset 官网数据&#xff1a;https://www.ipf.kit.edu/english/projekt_cv_szenen.php 官网&#xff1a;KIT-IPF-Software and Datasets - LaFiDa 标定数据下载&…

STM32_串口重定向

这里写目录标题 前言1、需要勾选Use Micro LIB2、不需要勾选Use Micro LIB 前言 最近在学习LVGL时遇到了一个坑&#xff0c;我原来使用的重定向方法必须要勾选Use Micro LIB&#xff0c;否则程序会卡死&#xff0c;但是在移植LVGL时又发现不能勾选Use Micro LIB&#xff0c;否则…

第一届长城杯初赛部分wp(个人解题思路)

目录 Black web babyrsa2 APISIX-FLOW cloacked 本人不是很擅长ctf&#xff0c;这只是我自己做出的西部赛区部分题的思路&#xff0c;仅供参考 Black web 访问http://192.168.16.45:8000/uploads/1711779736.php 蚁剑连接 访问/var/www/html/u_c4nt_f1nd_flag.php babyr…

springboot项目引入swagger

1.引入依赖 创建项目后&#xff0c;在 pom.xml 文件中引入 Swagger3 的相关依赖。回忆一下&#xff0c;我们集成 Swagger2 时&#xff0c;引入的依赖如下&#xff1a; <dependency><groupId>io.springfox</groupId><artifactId>springfox-swagger2&…

Minikube本地搭建单节点Kubernetes集群

1、什么是 Minikube Minikube 是一个开源工具&#xff0c;旨在为开发者提供一种便捷的方式在本地环境中搭建单节点的 Kubernetes 集群。它主要用于开发、测试和学习 Kubernetes 应用程序&#xff0c;无需依赖大型的硬件资源或复杂的多节点集群配置。minikube 使用轻量级虚拟化技…

Mac系统Unity团结引擎打包OpenHomeny项目配置

1、团结引擎下载&#xff1a;直接百度下载即可 2、mac版本的DevEco4.0编辑器下载&#xff1a; widthdevice-width,initial-scale1.0https://docs.openharmony.cn/pages/v4.0/zh-cn/release-notes/OpenHarmony-v4.0-release.md/#%E9%85%8D%E5%A5%97%E5%85%B3%E7%B3%BB3、打开D…

LINUX笔记温习

目录 DAY1 DAY2 day3&#xff1a; day4 day5 day6 day7 day8 day9 day10 day11 day12 day13 day14 day15 20day DAY1 1、多层级文件夹创建要带-p&#xff1b; 2、创建多文件&#xff0c;要先到该目录下才能创建(第一个目录必须存在才能有效建立)&#xff1b; D…

C++ 简单模拟实现 STL 中的 set、map 与 multiset、multimap

目录 一&#xff0c;RB_tree 的实现 1&#xff0c;RB_tree 的节点与数据结构 2&#xff0c;RB_tree 的迭代器 3&#xff0c;RB_tree 的构造 4&#xff0c;RB_tree 的元素操作 5&#xff0c;完整代码 二&#xff0c;set 与 multiset 的实现 1&#xff0c;set 2&#x…

Qtxlsx第三方库的安装和使用

本文仅作为一个记录&#xff0c;安装QtXlsx方便操作excel&#xff0c;主要参考了这篇博文&#xff1a;https://blog.csdn.net/u014779536/article/details/111769792 1&#xff0c;下载安装Perl脚本Strawberry Perl for Windows&#xff0c;默认安装strawberry-perl-5.30.0.1-…

Flask-RESTful 分析

Flask-RESTful 是一个 Flask 扩展&#xff0c;它为构建 RESTful API 提供了方便的工具和资源。它简化了创建 RESTful 服务的过程&#xff0c;允许开发者专注于业务逻辑而不是 HTTP 协议的细节。 资源&#xff08;Resources&#xff09;&#xff1a; Resource 类&#xff1a;是…

[WinForm开源]原神混池模拟器-蒙德篇:软件的基本介绍、使用方法、常见问题解决与代码开源

首先先和各位旅行者道个歉&#xff0c;混池都过去这么久了才把软件开发好并发布出来 >_< 创作目的&#xff1a;为给各位旅行者&#xff08;当然包括我自己&#xff09;估测混池抽取的出货率以及让各位旅行者可以过手瘾&#xff0c;故开发了此项目作为参考。 创作说明&am…

java 常见API(Objects)

定义 API就是别人定义好的工具类和工具包目的&#xff1a;避免重复造轮子&#xff0c;提升开发效率&#xff0c;更加专注于实现业务逻辑 Object 类 object类是所有类的祖宗类&#xff0c;所有的类型都是可以使用Object的方法的 最常见的三个方法&#xff1a; toString:print就会…

【项目实战】——商品管理的制作完整代码

&#x1f468;‍&#x1f4bb;个人主页&#xff1a;开发者-曼亿点 &#x1f468;‍&#x1f4bb; hallo 欢迎 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! &#x1f468;‍&#x1f4bb; 本文由 曼亿点 原创 &#x1f468;‍&#x1f4bb; 收录于专栏&#xff1a…

3. python练习题3-自由落体

3. python练习题3-自由落体 【目录】 文章目录 3. python练习题3-自由落体1. 目标任务2. 解题思路3. 知识回顾-%占位符格式化处理3.1 概述3.2 占位符的多种用法3.3 格式化操作符辅助指令3.4 将整数和浮点数格式化为字符串 4. 解题思路4.1 球第1次下落4.2 球第2次下落 5. 最终代…

C++(语法以及易错点2)

1.内联函数 1.1 概念 以inline修饰的函数叫做内联函数&#xff0c;编译时C编译器会在调用内联函数的地方展开&#xff0c;没有函数调 用建立栈帧的开销&#xff0c;内联函数提升程序运行的效率。 ​int ADD(int a,int b) {return ab; }​ 1.2 特性 1. inline是一种以空间换时间…

【Python基础教程】5. 数

&#x1f388;个人主页&#xff1a;豌豆射手^ &#x1f389;欢迎 &#x1f44d;点赞✍评论⭐收藏 &#x1f917;收录专栏&#xff1a;python基础教程 &#x1f91d;希望本文对您有所裨益&#xff0c;如有不足之处&#xff0c;欢迎在评论区提出指正&#xff0c;让我们共同学习、…

Taro + vue3 小程序封装标题组件

分为没有跳转页面的title组件和 有跳转页面的title组件 我们可以把这个封装成一个组件 直接上代码 <template><div class"fixed-title-container"><div class"box"><div class"icon" v-if"isShow" click"…

mysql故障排查

MySQL是目前企业最常见的数据库之一日常维护管理的过程中&#xff0c;会遇到很多故障汇总了常见的故障&#xff0c;MySQL默认配置无法满足高性能要求 一 MySQL逻辑架构图 客户端和连接服务核心服务功能存储擎层数据存储层 二 MySQL单实例常见故障 故障1 ERROR 2002 (HY000)…