八大排序算法(含时间复杂度、空间复杂度、算法稳定性)

文章目录

  • 八大排序算法(含时间复杂度、空间复杂度、算法稳定性)
    • 1、(直接)插入排序
      • 1.1、算法思想
      • 1.2、排序过程图解
      • 1.3、排序代码
    • 2、希尔排序
    • 3、冒泡排序
      • 3.1、算法思想
      • 3.2、排序过程图解
      • 3.3、排序代码
    • 4、(简单)选择排序
      • 4.1、算法思想
      • 4.2、排序过程图解
      • 4.3、排序代码
    • 5、堆排序
    • 6、快速排序
    • 7、归并排序
    • 8、计数排序
      • 8.1、算法思想
      • 8.2、排序过程图解
      • 8.3、排序代码

img

八大排序算法(含时间复杂度、空间复杂度、算法稳定性)

下列算法默认都是对数组进行升序

1、(直接)插入排序

1.1、算法思想

  • 插入排序是一种简单直观的排序算法,它的工作原理是通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入。

    插入排序的具体步骤如下:

    1. 从第一个元素开始,该元素可以认为已经被排序;
    2. 取出下一个元素,在已经排序的元素序列中从后向前扫描;
    3. 如果该元素(已排序)大于新元素,将该元素移到下一位置;
    4. 重复步骤3,直到找到已排序的元素小于或者等于新元素的位置;
    5. 将新元素插入到该位置后;
    6. 重复步骤2~5。

img


1.2、排序过程图解

  • 从第一个元素开始,该元素可以认为已经被排序,取出下一个元素并记录到临时变量tmp中,在已经排序的元素序列中从后向前扫描(end--),如果该元素(已排序)大于新元素,将该元素移到下一位置,如果该元素小于等于新元素,则直接在这个元素的后面把新元素放进来。

    • 这里仅演示部分过程,其他过程自行考虑(和上述过程类似)。

1.3、排序代码

  • end指向当前要插入元素的前一个位置end+1指向当前要插入元素的位置),tmp保存当前要插入的元素,在已经排序的元素序列中从后向前扫描,找到比新元素小的元素的时候(因为有序,这个位置前面的元素比这个元素更小),直接把新元素插入到这个位置的后面。

    //插入排序
    void InsertSort(int *arr, int n) {for (int i = 0; i < n - 1; ++i) {//一趟int end = i;int tmp = arr[end + 1];while (end >= 0) {if (tmp < arr[end]) {arr[end + 1] = arr[end];} else {break;}--end;}arr[end + 1] = tmp;}
    }
    
  • 时间复杂度计算:

    • 最坏时间复杂度:数组元素原本是降序,现要求使其升序。那么每个元素需要移动或者比较的次数为:

      • 第一个元素:0
      • 第二个元素:1
      • 第三个元素:2
      • 第n个元素:n-1

      总次数:0+1+2+3+...+n-1 = n*(n-1)/2

      所以最坏时间复杂度为:O(n^2)

    • 最好时间复杂度:考虑数组原本是升序,那么所有元素需要移动或者比较的总次数为:0+1+1+...+1 = n-1。所以最好时间复杂度为O(n)

    • 平均时间复杂度:O(n^2) ----> 算法不太行

  • 空间复杂度计算:由于没有开辟额外空间来辅助数组排序,故空间复杂度为O(1)

  • 算法稳定性稳定,因为对于值相同的元素,后插入的时候不会插到相同元素的前面(tmp >= arr[end]break,即不插入)。


2、希尔排序

希尔排序详解


3、冒泡排序

3.1、算法思想

  • 冒泡排序是通过对相邻元素的比较和位置交换,使得每次遍历都可以得到剩余元素中的最大值,将其放入有序序列中最终的位置,然后下一趟排序的时候就不用去比较这个已经确定了的元素。在冒泡排序中,会依次比较相邻元素的值,若发现逆序则交换,使值较大的元素逐渐从前移向后部,就如同水底下的气泡一样逐渐向上冒。

3.2、排序过程图解

  • 每趟排序可以把一个元素”冒“到最终位置上,下一趟排序就可以少排序一个元素。

    • 这里仅演示部分过程,其他过程自行考虑(和上述过程类似)。

3.3、排序代码

  • 指针i控制每趟需要少排序的元素个数(即已经有i个元素已经在最终位置上),指针j用来比较相邻元素的大小,若相邻元素是降序,则交换这两个元素。

  • 这里定义了一个flag,用来标记每趟排序是否有交换,如果有交换,就需要继续下一趟排序,没有交换则说明数组已经有序,那么就不用继续下一趟排序!

    void Swap(int *a, int *b) {int tmp = *a;*a = *b;*b = tmp;
    }//冒泡排序
    void BubbleSort(int *arr, int n) {for (int i = 0; i < n; ++i) {int flag = 0;for (int j = 0; j < n - 1 - i; ++j) {if (arr[j + 1] < arr[j]) {flag = 1;Swap(&arr[j], &arr[j + 1]);}}if (flag == 0) {break;}}
    }
    
  • 时间复杂度计算:

    • 最坏时间复杂度:考虑数组原本是降序,现在要求其升序。那么每个元素需要移动或者比较的次数为:

      • 第一趟排序:n-1
      • 第二趟排序:n-2
      • 第三趟排序:n-3
      • 第n趟排序:1

      总次数:n-1+n-2+n-3+...+1 = n*(n-1)/2

      所以最坏时间复杂度为:O(n^2)

    • 最好时间复杂度:考虑数组原本是升序。那么所有元素需要移动或者比较的次数为:

      若不使用flag:比较次数为n-1+n-2+n-3+...+1 = n*(n-1)/2次。

      使用flag:比较次数为n-1次。

    • 平均时间复杂度:O(n^2) —> 算法不太行

  • 空间复杂度计算:由于没有开辟额外空间来辅助数组排序,故空间复杂度为O(1)

  • 算法稳定性稳定,因为对于值相同的元素,每一趟排序的时候不会交换(arr[j + 1] < arr[j]才交换)。


4、(简单)选择排序

4.1、算法思想

  • 选择排序是一种简单直观的排序算法。它的工作原理如下:(优化后的选择排序–>每次都能确定当前未排序序列的最小元素和最大元素的最终位置)
    1. 在未排序序列中找到最小元素和最大元素,最小元素存放到排序序列的起始位置,最大元素存放到排序序列的末尾位置。
    2. 再从剩余未排序元素中继续寻找最小元素和最大元素,然后最小元素放到前面已排序序列的末尾最大元素放到后面已排序序列的前面
    3. 以此类推,直到所有元素均排序完毕。
  • 这里动画排序是每次选出一个最小值。(我们讲的算法更优哈哈)

4.2、排序过程图解

  • 在未排序序列中找到最小元素和最大元素,最小元素存放到排序序列的起始位置,最大元素存放到排序序列的末尾位置。

  • 再从剩余未排序元素中继续寻找最小元素和最大元素,然后最小元素放到前面已排序序列的末尾最大元素放到后面已排序序列的前面

    • 这里仅演示部分过程,其他过程自行考虑(和上述过程类似)。
  • 以此类推,直到所有元素均排序完毕。


4.3、排序代码

  • 使用minimaxi分别记录当前未排序的最小值下标和最大值下标在未排序的序列中找出最小值和最大值,然后分别交换到当前未排序的起始位置和末尾位置。需要注意的是如果当前未排序的序列中,最大值刚好在未排序序列的起始位置,那么就需要记录好这个最大值与当前未排序的序列中的最小值交换后的位置,不记录的话,那么当前maxi指向的值不一定是最大值!

    //选择排序
    void SelectSort(int *arr, int n) {int mini = 0;int maxi = 0;int start = 0;int end = n - 1;while (start < end) {for (int i = start + 1; i <= end; ++i) {if (arr[i] > arr[maxi]) {maxi = i;}if (arr[i] < arr[mini]) {mini = i;}}Swap(&arr[mini], &arr[start]);//注意此时如果start刚好是最大值的话,就会把最大值换走了,也就是本来最大值在 0 位置,交换后换到其他位置了,所以判断一下if (start == maxi) {maxi = mini;//找到最大值的下标}Swap(&arr[maxi], &arr[end]);//向中间靠拢++start;--end;}
    }
    
  • 时间复杂度计算:对于选择排序排序来说,没有什么最坏时间复杂度和最好时间复杂度,因为不管原数组起始是升序还是降序,元素之间的比较次数都是一样的:

    • 确定了2个元素的最终位置:n-1
    • 确定了4个元素的最终位置:n-1+n-3
    • 确定了6个元素的最终位置:n-1+n-3+n-5
    • 确定了n个元素的最终位置:n+n-3+n-5+...+1 = n*(n+1)/4 <---大约,所以时间复杂度为O(n^2)
  • 空间复杂度计算:由于没有开辟额外空间来辅助数组排序,故空间复杂度为O(1)

  • 算法稳定性不稳定,考虑序列(1,2,2),排序后序列为(1,2,2),我们发现2的相对位置发生了变化,所以是不稳定的排序算法。


5、堆排序

堆排序详解


6、快速排序

快速排序递归方法和非递归方法详解


7、归并排序

快速排序递归方法和非递归方法详解


8、计数排序

8.1、算法思想

  • 计数排序就是使用一个临时数组来记录这个原数组的元素对应这个临时数组下标出现的次数,然后再对这个临时数组0开始往后按下标出现的次数遍历。

  • 优化:对于原数组最小值较大的情况,我们可以使用对这个临时数组进行==重定位==。

    • 重定位:相当于计算机组成原理里面的将逻辑地址转化为物理地址的过程,比如序列110,110,111,120,125,122,115,118,112,118,其实它的范围就是在110~125,区间长度为16,如果我们按照这个序列的最大值来建立数组,那么需要长度为126的数组,但是这个数组的前110个空间都是0,也就是并没有用上,浪费了。但是如果创建一个长度为16的数组,下标为0~15(原数组每个元素减110,这个110是这个原数组的最小值),是不是就可以匹配这个序列的范围了呢?

      那么问题是之后遍历这个临时数组,只能得到0~15的下标,并不是我们要的110~125!其实,在遍历这个临时数组的时候,可以继续使用重定位,把这个0~15的下标重定位到110~125(每个下标都加110,这个110是这个原数组的最小值)!

8.2、排序过程图解

  • 先找到原数组的最大值和最小值,然后就可以确定临时数组的长度,然后初始化这个临时数组(全0)。

  • 然后依次遍历原数组,根据重定位,将原数组的元素减去最小值去对应临时数组的下标,并对这个下标里的元素+1

  • 遍历这个临时数组,对每个下标进行遍历,按下标对应的元素值看需要对此下标遍历几次(需要重定位回去—加上原数组的最小值)。


8.3、排序代码

  • minmax记录原数组的最小值和最大值,确定临时数组的长度(max-min+1),然后对临时数组count进行初始化,接下来就是把原数组里的元素重定位为临时数组的下标(元素值减原数组的最小值),并对此下标对应的元素+1,一直到遍历完原数组。

  • 遍历这个临时数组count,对每个下标进行遍历,按下标对应的元素值看需要对此下标遍历几次(需要重定位回去—加上原数组的最小值)。

  • 注意:这里不能找最大最小值的下标,因为在重定位回去的时候arr[j++]在变,也就是最小值下标不一定对应到最小值了!

    //计数排序
    void CountSort(int *arr, int n) {//先找出数组的最大最小值int max = arr[0];int min = arr[0];for (int i = 1; i < n; ++i) {if (arr[i] > max) {max = arr[i];}if (arr[i] < min) {min = arr[i];}}//节省空间,需要对元素重定位int capacity = max - min + 1;//元素大小区间//记录每个元素的出现次数int *count = (int *) malloc(sizeof(int) * capacity);if (count == NULL) {perror("malloc error");exit(-1);}memset(count, 0, sizeof(int) * capacity);for (int i = 0; i < n; ++i) {count[arr[i] - min]++;}int j = 0;for (int i = 0; i < capacity; ++i) {while (count[i]--) {arr[j++] = i + min;}}free(count);
    }
    
  • 时间复杂度计算:这里找最大值最小值花费时间n,遍历临时数组花费时间k(临时数组长度),所以时间复杂度为O(n+k)

  • 空间复杂度计算:使用了临时数组(临时数组长度为k),所以空间复杂度为O(k)

  • 算法稳定性稳定,因为它是利用一个数据的索引来记录元素出现的次数,而这个数组的索引就是元素的数值。当计数排序完成后,具有相同数值的元素在数组中的位置也相同,因此它们的顺序保持不变。


八大排序算法整体的时间复杂度、空间复杂度、算法稳定性等看如下表格:

排序算法平均时间复杂度最好情况最坏情况空间复杂度排序方式稳定性
(直接)插入排序O(n^2)O(n)O(n^2)O(1)内部排序稳定
希尔排序O(n^1.3)O(n^1.3)O(n^1.3)O(1)内部排序不稳定
冒泡排序O(n^2)O(n)O(n^2)O(1)内部排序稳定
(简单)选择排序O(n^2)O(n^2)O(n^2)O(1)内部排序不稳定
堆排序O(nlogn)O(nlogn)O(nlogn)O(1)内部排序不稳定
快速排序O(nlogn)O(nlogn)O(n^2)O(logn)内部排序不稳定
归并排序O(nlogn)O(nlogn)O(nlogn)O(n)外部排序稳定
计数排序O(n+k)O(n+k)O(n+k)O(k)外部排序稳定

OKOK,八大排序算法就到这里。如果你对Linux和C++也感兴趣的话,可以看看我的主页哦。下面是我的github主页,里面记录了我的学习代码和leetcode的一些题的题解,有兴趣的可以看看。

Xpccccc的github主页

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

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

相关文章

小谈设计模式(27)—享元模式

小谈设计模式&#xff08;27&#xff09;—享元模式 专栏介绍专栏地址专栏介绍 享元模式模式结构分析享元工厂&#xff08;FlyweightFactory&#xff09;享元接口&#xff08;Flyweight&#xff09;具体享元&#xff08;ConcreteFlyweight&#xff09;非共享具体享元&#xff0…

竞赛选题 深度学习LSTM新冠数据预测

文章目录 0 前言1 课题简介2 预测算法2.1 Logistic回归模型2.2 基于动力学SEIR模型改进的SEITR模型2.3 LSTM神经网络模型 3 预测效果3.1 Logistic回归模型3.2 SEITR模型3.3 LSTM神经网络模型 4 结论5 最后 0 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 …

使用 GitHub Action 自动更新 Sealos 集群的应用镜像

在 IT 领域&#xff0c;自动化无疑已成为提高工作效率和减少人为错误的关键。Sealos 作为一个强大的云操作系统&#xff0c;已经为许多企业和开发者提供了稳定可靠的服务。与此同时&#xff0c;随着技术不断发展&#xff0c;集成更多的功能和服务变得尤为重要。考虑到这一点&am…

BAT023:将当前目录同名文件(不包括扩展名)整理到以其命名的文件夹内

引言&#xff1a;编写批处理程序&#xff0c;实现将当前目录同名文件&#xff08;不包括扩展名&#xff09;整理到以其命名的文件夹内。 一、新建Windows批处理文件 参考博客&#xff1a; CSDNhttps://mp.csdn.net/mp_blog/creation/editor/132137544 二、写入批处理代码 1.…

机器人命令表设计

演算命令 CLEAR 将数据 1 上被指定的编号以后的变数的内容&#xff0c;以及数据 2 上仅被指定的个数都清除至 0。 INC 在被指定的变数内容上加上 1。 DEC 在被指定的变数内容上减掉 1。 SET 在数据 1 上设定数据 2。 ADD 将数据 1 和数据 2 相加&#xff0c;得出的结果保存在数…

IDEA—java: 常量字符串过长问题解决

问题描述&#xff1a; Error: java: 常量字符串过长 问题分析&#xff1a; 字符串长度过长&#xff0c;导致 idea 默认使用的 javac 编译器编译不了。 解决办法&#xff1a; Javac 编译器改为 Eclipse 编译器。 File -> Settings -> Build,Execution,Deployment -&…

buuctf week2-web-Upload again!

尝试传修改后缀的普通一句话木马&#xff0c;被检测 尝试传配置文件 .htaccess 和 .user.ini 两个都传成功了 接下来继续传入经过修改的木马 GIF89a <script language"php"> eval($_POST[cmd]); </script> 没有被检测&#xff0c;成功绕过 直接上蚁剑…

免费chatGPT工具

发现很多人还是找不到好用的chatGPT工具&#xff0c;这里分享一个邮箱注册即可免费试用。 PromptsZone - 一体化人工智能平台使用 PromptsZone 与 ChatGPT、Claude、AI21 Labs、Google Bard 聊天&#xff0c;并使用 DALL-E、Stable Diffusion 和 Google Imagegen 创建图像&…

【环境】Ubuntu20.04 安装 Anaconda 顺顺利利

ubuntu里面安装的Anaconda也是顺顺利利 别忘了source source一下 参考链接 中间遇到了一个问题&#xff0c;用下面的链接轻松解决了 关于修改anaconda安装路径的问题

商贸进销存软件怎么选?

本文将为大家讲解&#xff1a;1、商贸企业进销存流程上的难点&#xff1b;2、商贸企业如何选择进销存系统&#xff1f; 在当今快速发展的商业环境中&#xff0c;有效的进销存&#xff08;Inventory Management&#xff09;管理是商贸企业成功的关键因素之一。然而&#xff0c;…

FPGA coaxpress 2.0 ip

CoaXPress是一种高速串行接口技术&#xff0c;可以实现高带宽、长距离传输和多通道传输。CoaXPress图像采集卡通常具有多个CoaXPress接口&#xff0c;可以同时连接多个相机&#xff0c;实现多通道的图像采集。它们通常具有高速数据传输能力&#xff0c;可以实时采集和传输高分辨…

什么是API网关?——驱动数字化转型的“隐形冠军”

什么是API网关 API网关&#xff08;API Gateway&#xff09;是一个服务器&#xff0c;位于应用程序和后端服务之间&#xff0c;提供了一种集中式的方式来管理API的访问。它是系统的入口点&#xff0c;负责接收并处理来自客户端的请求&#xff0c;然后将请求路由到相应的后端服…

【LeetCode高频SQL50题-基础版】打卡第7天:第36~40题

文章目录 【LeetCode高频SQL50题-基础版】打卡第7天&#xff1a;第36~40题⛅前言按分类统计薪水&#x1f512;题目&#x1f511;题解 上级经理已离职的公司员工&#x1f512;题目&#x1f511;题解 换座位&#x1f512;题目&#x1f511;题解 电影评分&#x1f512;题目&#x…

【Page-level Heap Fengshui -- Cross-Cache Overflow】corCTF2022-cache-of-castaways

前言 什么叫 Cross Cache 呢&#xff1f;其实就是字面意思&#xff0c;我们知道内核中的大部分结构体都有自己的专属 slab 内存池。那现在我们可以想象一下这个场景&#xff0c;我们拥有一个特定 kmem-cache 的溢出漏洞&#xff0c;那么我们该如何利用呢&#xff1f; 程序分析…

uni-app开发微信小程序的报错[渲染层错误]排查及解决

一、报错信息 [渲染层错误] Framework nner error (expect FLOW INITIALCREATION end but get FLOW CREATE-NODE) 二、原因分析及解决方案 第一种 原因&#xff1a;基础库版本的原因导致的。 解决&#xff1a; 1.修改调试基础库版本 2.详情—>本地设置—>调试基础库…

volatile为什么无法保证原子性

假设定义 volatile int i 0; 现在2个线程同时 i&#xff0c;为什么数据还可能会出错&#xff1f;一起来看下图&#xff0c;虽然volatile的机制是&#xff1a;如果volatile修饰的变量有修改&#xff0c;那么会将变更内容写回主内存&#xff0c;同时让其他线程工作内存的该变量缓…

Leetcode226.翻转二叉树

本专栏内容为&#xff1a;leetcode刷题专栏&#xff0c;记录了leetcode热门题目以及重难点题目的详细记录 &#x1f493;博主csdn个人主页&#xff1a;小小unicorn ⏩专栏分类&#xff1a;Leetcode &#x1f69a;代码仓库&#xff1a;小小unicorn的代码仓库&#x1f69a; &…

react antd table表格点击一行选中数据的方法

一、前言 antd的table&#xff0c;默认是点击左边的单选/复选按钮&#xff0c;才能选中一行数据&#xff1b; 现在想实现点击右边的部分&#xff0c;也可以触发操作选中这行数据。 可以使用onRow实现&#xff0c;样例如下。 二、代码 1.表格样式部分 //表格table样式部分{…

【基础篇】四、本地部署Flink

文章目录 1、本地独立部署会话模式的Flink2、本地独立部署会话模式的Flink集群3、向Flink集群提交作业4、Standalone方式部署单作业模式5、Standalone方式部署应用模式的Flink Flink的常见三种部署方式&#xff1a; 独立部署&#xff08;Standalone部署&#xff09;基于K8S部署…

深入解析Spring Cloud Gateway的GlobalFilter

文章目录 摘要引言GlobalFilter的作用使用GlobalFilter默认的GlobalFilter自定义GlobalFilter 示例代码配置GlobalFilter配置文件方式代码方式 高级用法&#xff1a;重写GlobalFilter思路代码实现 结论参考文献 摘要 本文将详细介绍Spring Cloud Gateway中的GlobalFilter&…