【常见的六大排序算法】插入排序、希尔排序、选择排序、冒泡排序、堆排序、快速排序

在这里插入图片描述

个人主页
创作不易,感谢大家的关注!

在这里插入图片描述
在这里插入图片描述

文章目录

    • 前言
  • 🎡一、插入排序
  • 🌲二、希尔排序
  • 🎉三、选择排序
  • 🎀四、冒泡排序
  • 🚘五、堆排序
  • 🛵六、快速排序
    • 1. Hoare版本
    • 2. 挖坑法
    • 3. 前后指针法
    • 4. 非递归实现

前言

在实现排序前,我们要先知道什么是排序。排序:简单的来说就是把一串数据按照一定的方式依次排序。例如:可以按照数字的大小升序或降序排列,或者按照英文字母的先后顺序排列等。使用这一系列的方式,就可以将一串无序状态下的数据排列成有序的数据。

🎡一、插入排序

  1. 基本思路

    1. 默认第一个元素为有序的,从第二个元素开始排序。
    2. 取出下一个元素tmp,往前进行比较。
    3. 如果该元素比tmp大,则将该元素往后移动一位,直到找到比tmp小于或者等于的元素。
    4. 找到比tmp小于或者等于的元素,将tmp插入到该元素的下一位。
    5. 不断重复上述步骤。
  2. 动图演示:
    在这里插入图片描述

  3. 时间复杂度: 最坏情况下为 O(N ^ 2),此时待排序的序列状况为逆序或者接近逆序。最好情况下为 O(N),此时待排序列为升序,或者说接近升序。

  4. 空间复杂度: O(1)。

代码实现:

// 插入排序
void InsertSort(int* a, int n)
{//遍历数组for (int i = 0; i < n - 1; i++){//记录有序序列最后一个元素的下标int end = i;int tmp = a[end + 1];while (end >= 0){	//比插入的数大就往后移动if (a[end] > tmp){a[end + 1] = a[end];--end;}else{//比插入的数小就退出循环break;}}//将tmp放到比插入的数小的后面a[end + 1] = tmp;}
}

🌲二、希尔排序

  1. 基本思路

    希尔排序是插入排序的一个优化方案,其本质上还是插入排序。但它们的时间复杂度却并不相同。

    1. 先将待排序列进行预排序,使得待排序列接近有序,然后再进行插入排序。
    2. 将待排序的数据分为多个组,使每组间隔gap为2或3…。
    3. 如果第一个元素大于最后一个元素,则将它们两个元素进行交换。
    4. 不断重复上述操作,直到每组间隔只有1时,说明所有的数据已经完成排序。
  2. 动图演示:在这里插入图片描述

  3. 时间复杂度: O(N ^ 1.3)。

  4. 空间复杂度: O(1)。

代码实现:

// 希尔排序
void ShellSort(int* a, int n)
{int gap = n;while (gap > 1){// +1保证当n的值小于3时,gap最低为1,依次进行插入排序gap = gap / 3 + 1;for (size_t i = 0; i < n - gap; i += gap){int end = i;int tmp = a[end + gap];while (end >= 0){if (a[end] > tmp){a[end + gap] = a[end];end = end - gap;}else{break;}}a[end + gap] = tmp;}}
}

🎉三、选择排序

  1. 基本思路
    1. 从待排序序列中选出最小值mini,放在序列的起始位置。
    2. 每次遇到比mini小的值就更新mini,直到遍历完整个序列。
    3. 然后将该值放到序列的排列好的有序序列的下一个位置。
    4. 重复上述步骤,直到待排序只剩下一个元素,说明排序完成。
  2. 动图演示:
    在这里插入图片描述
  3. 时间复杂度: O(N^2)。有n个数,每趟选一个最大或最小值,需要选n趟。
  4. 空间复杂度: O(1)。

代码实现

// 选择排序
void SelectSort(int* a, int n)
{//定义一个begin和end分别指向数组的头下标和尾下标int begin = 0;int end = n - 1;while (begin < end){//定义mini和begin分别表示最小值和最大值的下标int mini = begin, maxi = begin;for (int i = begin + 1; i <= end; i++){if (a[i] > a[maxi])maxi = i;if (a[i] < a[mini])mini = i;}Swap(&a[begin], &a[mini]);//需要注意的是,当最大值在最小值位置的时候,a[maxi]的值会与a[mini]的值进行交换,因此需要提前将maxi置为miniif (begin == maxi){maxi = mini;}Swap(&a[end], &a[maxi]);begin++;end--;}
}

总结: 效率相对较低,实际中很少使用。

🎀四、冒泡排序

  1. 基本思路
    根据序列中两个记录键值的比较结果来交换这两个记录在序列中的位置,一前一后两个下标的值进行比较,如果前一个值比后一个值大就进行交换。
  2. 动图演示:在这里插入图片描述
  3. 时间复杂度: O(N ^2)。该排序每次都要和相邻的两个数进行比较,最坏情况下比较n次,跑n躺。
  4. 空间复杂度: O(1)。

代码实现:

// 冒泡排序
void BubbleSort(int arr[], int n)
{for (int i = 0; i < n - 1; i++){//假设没有发生判断,如果发生判断,就将flag置为,否则就退出int flag = 0;for (int j = 0; j < n - 1 - i; j++){int tmp = 0;tmp = arr[j];arr[j] = arr[j + 1];arr[j + 1] = tmp;flag = 1;}if (flag == 0){break;}}
}

结论: 该排序效率低下,实际中几乎不使用。

🚘五、堆排序

  1. 基本思路
    将一组无序的数据,按照升序或降序的方式对其进行排序。
    注意: 在堆排序中,升序建大堆,降序建小堆。 因为堆排序的逻辑是将根节点与最后一个节点依次发生交换。不断交换,大堆排序完,堆尾的数据就是最大的值,并且不断向堆顶的数据依次缩小;相反,小堆排序完后,堆尾的数据就是最小的值,并不断向堆顶的数据依次增大。
  2. 图片演示:**在这里插入图片描述
  3. 时间复杂度: O(N * log(N))堆排序建堆的时间复杂度为O(N),但是选数时都需要将堆顶和堆尾的数据进行交换,而堆的高度是log(N),而建堆的时间复杂的可以忽略不计,因此为O(N * log(N))。
  4. 空间复杂度: O(1)。

代码实现:

void Swap(int* p1, int* p2)
{int tmp = *p1;*p1 = *p2;*p2 = tmp;
}// 向下调整排序
void AdjustDown(int* a, int n, int parent)
{//假设左孩子大int child = parent * 2 + 1;while (child < n){if (child + 1 < n && a[child + 1] < a[child]){child++;}if (a[child] < a[parent]){Swap(&a[child], &a[parent]);parent = child;child = parent * 2 + 1;}else{break;}}
}//堆排序
//假设使用建小堆的方法
void HeapSort(int* a, int n)
{int end = n - 1;while (end > 0){Swap(&a[0], &a[end]);AdjustDown(a, end, 0);--end;}
}

总结: 排序效率高,实际中也可以使用。

🛵六、快速排序

一般我们使用快速排序方法的顺序:挖坑法 -> Hoare -> 前后指针法。

1. Hoare版本

  1. 基本思路
    1. 选出一个key,一般在最左边或在最右边。
    2. 定义一个begin为序列的头和end为序列的尾,begin从左向右开始走,end从右向左进行走。(注意: 如果选择最左边的数据为key,则需让end先走;若选择右边的数据为key,则让begin先走)
    3. 假设最左边为key,end开始先走。在走的过程中,如果end遇到比key小的数据,就停下,然后让begin开始走,直到begin遇到比key大的数据就停下,然后将begin里的值和end里的值进行交换,然后又继续从end开始先走,不断循环往复,直到begin和end相遇,则将相遇点的内容和key进行交换即可。
    4. 此时,key左边的数据都是小于key的,key右边的数据都是大于key的,说明key已在序列中排好序了。
    5. 因此,序列中划分出了三个区间,分别为 [left, key - 1],key,[key + 1, right] 这三个区间,然后不断对[left, key - 1]和[key + 1, right]这两个区间重复上述步骤,直到所有的key都排好序。
  2. 单趟的动图演示如下:
    在这里插入图片描述
    代码实现:
// 快速排序
void QuickSort(int* a, int left,int right)
{//只有一个数或区间不存在if (left >= right){return;}int begin = left, end = right;while (begin < end){//右边找小,begin < end是为了防止越界while (begin < end && a[end] >= a[keyi]){end--;}//左边找大while (begin < end && a[begin] <= a[keyi]){++begin;}//小的换到右边,大的换到左边Swap(&a[begin], &a[end]);}Swap(&a[keyi], &a[begin]);// [left,keyi-1] keyi [keyi+1,right],使用递归的方法QuickSort(a, left, keyi - 1);QuickSort(a, keyi + 1, right);
}

代码优化:
在实际中,我们发现:快速排序在有序或接近有序数组中完成排序的效率低下。因此我们可以采用每次都取大小中等的值来作为数组中最左边的key,这样可以有效提升代码的运行效率,减少循环的次数。

// 三数取中
int GetMidi(int* a, int left, int right)
{int midi = (left + right) / 2;if (a[left] < a[midi]){if (a[midi] < a[right])return midi;else if (a[left] < a[right])return right;else{return left;}}// a[left] > a[midi]else{if (a[midi] > a[right])return midi;// a[midi] < a[right]else if (a[left] < a[right])return left;elsereturn right;}
}

2. 挖坑法

  1. 基本思路
    1. 选出最左边的元素存入key中,在此位置中形成一个坑位。
    2. 定义left从左到右开始遍历,定义right从右向左开始遍历。
    3. 当right遍历找到比key小的数据,就放入left当中的位置。
    4. 当left遍历找到比key大的数据,就放入right当中的位置
    5. 如果left和right下标相遇,则把key中的数据放入相交处。
  2. 单趟动图演示:
    在这里插入图片描述
    代码实现:
//挖坑法
int QuickSort1(int* a, int left, int right)
{int midi = GetMidi(a, left, right);Swap(&a[left], &a[midi]);midi = left;int key = a[midi];while (left < right){// 左右开始遍历while (left < right && a[right] >= key){--right;}a[midi] = a[right];midi = right;while (left < right && a[left] <= key){++left;}a[midi] = a[left];midi = left;}a[midi] = key;return midi;
}

3. 前后指针法

  1. 基本思路
    1. 定义key来存储数组中最左边的数据,perv指向数组开始的位置,cur指向prev的下一个位置。
    2. 当cur下标中的元素小于key时,则让prev开始往后走。如果此时prev下标中的元素不等于cur下标中的元素,则两者进行交换。
    3. 否则cur一直往下走,当cur走完时,将prev下表中的元素和key中的数据进行交换
    4. 不断重复上述操作。
  2. 单趟动图演示:
    在这里插入图片描述
    代码实现:
//前后指针法
int QuickSort2(int* a, int left, int right)
{assert(a);int keyi = GetMidi(a, left, right);Swap(a[keyi], a[left]);keyi = left;int prev = left;int cur = prev + 1;while (cur <= right && a[cur] < a[keyi]){++prev;++cur;}while (cur <= right){while (cur <= right && a[cur] >= a[keyi]){++cur;}if (cur <= right){++prev;Swap(a[prev], a[cur]);}++cur;}Swap(&a[prev], &a[keyi]);return prev;
}

4. 非递归实现

基本思路
1. 使用栈的形式,模拟递归实现
2. 因为每次都要对序列的左右边界进行传入,根据栈的性质:先进后出,后进先出。因此,我们要先传入左边界,再传入右边界,获取数据时要先取右边界,在获取左边界。
3. 注意:获取数据后要及时删除,避免影响栈头结点的数据。

代码实现:

// 非递归方法
void QuickSort3(int* a, int begin, int end)
{assert(a);Stack ST;StackInit(&ST);StackPush(&ST, begin);StackPush(&ST, end);while (!StackEmpty(&ST)){int right = StackTop(&ST);StackPop(&ST);int left = StackTop(&ST);StackPop(&ST);if (left >= right){continue;}int keyi = QuickSort2(a, left, right);StackPush(&ST, keyi + 1);StackPush(&ST, right);StackPush(&ST, left);StackPush(&ST, keyi - 1);}
}

今天的分享就到这里啦,后续我还会新增一个排序算法-------归并排序,希望这篇文章可以帮助大家解决排序算法中的问题。我们下次再见,拜拜啦!在这里插入图片描述

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

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

相关文章

大佬推荐的好用网盘工具

转载说明&#xff1a;如果您喜欢这篇文章并打算转载它&#xff0c;请私信作者取得授权。感谢您喜爱本文&#xff0c;请文明转载&#xff0c;谢谢。 前段时间有大佬推荐了一款网盘工具seafile&#xff0c;自己搭建起来试用了一下&#xff0c;发现还挺好用的&#xff0c;这款工具…

【C++】C++11新特性:新的类功能、可变参数模板、STL容器中的empalce相关接口函数、lambda表达式、包装器(function、bind)

目录 一、新的类功能 1.1 移动构造函数和移动赋值运算符重载 1.2 强制生成默认函数的关键字default 1.3 禁止生成默认函数的关键字delete 1.4 其它的类功能 二、可变参数模板 三、STL容器中的empalce相关接口函数 四、lambda表达式 4.1 lambda的引入 4.2 lambda表达式…

openVPN+SmartDNS=openDNS or smartVPN?

正文共&#xff1a;777 字 11 图&#xff0c;预估阅读时间&#xff1a;1 分钟 我们现在已经熟练的掌握了openVPN的部署方式和配置方法&#xff08;带认证的openVPN连接/断开操作指南&#xff09;&#xff0c;还掌握了在CentOS系统部署SmartDNS的方法&#xff08;基于CentOS部署…

【网络安全】Web安全基础 - 第二节:前置基础知识- HTTP协议,握手协议,Cookie及Session

本章节主要介绍一些基础知识 d(^_^o) HTTP协议 什么是HTTP 超文本传输协议&#xff08;HyperText Transfer Protocol&#xff09;是一种用于分布式、协作式和超媒体信息系统的应用层协议。 HTTP是一个基于请求与响应&#xff0c;无状态的&#xff0c;应用层协议&#xff0c;…

100.网络游戏逆向分析与漏洞攻防-ui界面的设计-聊天功能的界面与设计

免责声明&#xff1a;内容仅供学习参考&#xff0c;请合法利用知识&#xff0c;禁止进行违法犯罪活动&#xff01; 如果看不懂、不知道现在做的什么&#xff0c;那就跟着做完看效果 现在的代码都是依据数据包来写的&#xff0c;如果看不懂代码&#xff0c;就说明没看懂数据包…

Git常用命令1

1、设置用户签名 ①基本语法&#xff1a; git config --global user.name 用户名 git config --global user.email 邮箱 ②实际操作 ③查询是否设置成功 cat ~/.gitconfig 注&#xff1a;签名的作用是区分不同操作者身份。用户的签名信息在每一个版本的提交…

MulterError: Field name missing 报错解决

Request POST /FileUpload/chunkApi/upload/mProjectNews/Images failed with status code 500. MulterError: Field name missing. 原因&#xff1a;Multer是基于Busboy解析的表单参数信息&#xff0c;经定位发现是解析表单中文本参数时出现了null&#xff0c;故收到MISSION_…

【算法】模拟算法——数青蛙(medium)

题解&#xff1a;模拟算法——数青蛙(medium) 目录 1.题目2.题解3.参考代码4.总结 1.题目 题目链接&#xff1a;LINK 2.题解 用循环进行遍历&#xff0c; 如果该字符为o\o\a\k 找一下前驱字符是否存在 如果存在&#xff0c;前驱字符–&#xff0c;该字符如果不存在&#x…

CATIA二次开发VBA入门(4)——进程外开发环境搭建,vb.net在Visual Studio中开发,创建圆柱曲面的宏录制到二次开发案例

目录 引出vb.net和vb6.0 进程外开发环境搭建vb.net开发环境搭建《CATIA二次开发技术基础》模板 添加宏库引用 vs开发环境初步vs中的立即窗口对象浏览器 建立模板案例&#xff1a;创建一堆圆柱曲面第一步&#xff1a;录制宏第二步&#xff1a;代码精简第三步&#xff1a;for循环…

⌈ 传知代码 ⌋ 命名实体识别

&#x1f49b;前情提要&#x1f49b; 本文是传知代码平台中的相关前沿知识与技术的分享~ 接下来我们即将进入一个全新的空间&#xff0c;对技术有一个全新的视角~ 本文所涉及所有资源均在传知代码平台可获取 以下的内容一定会让你对AI 赋能时代有一个颠覆性的认识哦&#x…

提升B端图表设计技能:教程分享

图表是数据可视化的常用表现形式&#xff0c;是对数据的二次加工&#xff0c;可以帮助我们理解数据、洞悉数据背后的真相&#xff0c;让我们更好地适应这个数据驱动的世界。本期就来带大家学习图表的设计及构成&#xff0c;帮助大家更好的理解图表设计。 设计教程源文件http:/…

OpenAI已全面开放自定义GPT以及文件上传等功能

今天&#xff0c;OpenAI兑现了前段时间做出的承诺&#xff1a;免费向所有用户开放GPT-4o。这意味着所有的免费用户都能使用自定义GPT模型、分析图表等其他GPT-4o新功能了。现在ChatGPT界面长这样&#xff1a; 可以看出&#xff0c;免费用户也能使用GPT store中定义好的模型&…

Python开发与应用实验1 | 开发环境安装配置

*本文来自博主对专业课 Python开发与应用 实验部分的整理与解析。 *一些题目可能会增加了拓展部分&#xff08;⭐&#xff09;。拓展部分不是实验报告中原有的内容&#xff0c;而是博主本人的补充&#xff0c;以便各位学习参考。 *实验环境为&#xff1a;Python 3.10 &#xf…

java实现地形dem产汇流流场数据提取解析

一、基础概念 在GIS和气象学、海洋学、大气科学、水文学等领域&#xff0c;"提取流场"通常指的是从数据集中识别和分析流体&#xff08;如水流、风场、洋流、大气流&#xff09;的运动模式和流向的过程。这个过程涉及数据处理、可视化和分析技术&#xff0c;下面是提…

Wpf 使用 Prism 实战开发Day31

登录数据绑定 1.首先在LoginViewModel 登录逻辑处理类中&#xff0c;创建登录要绑定属性和命令 public class LoginViewModel : BindableBase, IDialogAware {public LoginViewModel(){ExecuteCommand new DelegateCommand<string>(Execure);}public string Title { ge…

vue-标签选择

效果 选中后 代码 <span :class"[item.bealtrue?p_yx_span span_active :span p_yx]" click"onTagSelect(index)" v-for"(item,index) in tagList" :key"index" >{{item.name}} </span> // 列表值 tagList:[ {id: 1, na…

R语言ggplot2包绘制世界地图

数据和代码获取&#xff1a;请查看主页个人信息&#xff01;&#xff01;&#xff01; 1. 数据读取与处理 首先&#xff0c;从CSV文件中读取数据&#xff0c;并计算各国每日收入的平均签证成本。 library(tidyverse) ​ df <- read_csv("df.csv") %>% group_…

PostgreSQL基础(六):PostgreSQL基本操作(二)

文章目录 PostgreSQL基本操作(二) 一、字符串类型 二、日期类型 三、

基于分步傅立叶数值算法的一维非线性薛定谔方程求解matlab仿真

目录 1.程序功能描述 2.测试软件版本以及运行结果展示 3.核心程序 4.本算法原理 5.完整程序 1.程序功能描述 基于分步傅立叶数值算法的一维非线性薛定谔方程求解matlab仿真. 2.测试软件版本以及运行结果展示 MATLAB2022A版本运行 3.核心程序 ........................…

今天,组长和研发总监吵起来了 ...

插&#xff1a; AI时代&#xff0c;程序员或多或少要了解些人工智能&#xff0c;前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家(前言 – 人工智能教程 ) 坚持不懈&#xff0c;越努力越幸运&#xff0c;大家…