【初阶数据结构】星河中的光影 “排” 象:排序(上)

【初阶数据结构】星河中的光影 “排” 象:排序(上)

文章目录

本章节是初阶数据结构最后一个部分,排序在数据中的应用是特备常见的,一般在企业中的排序数据量成万上亿,所以选取一种合适且效率高的排序尤为重要

1.排序的概念及分类

排序,就是使一串记录,按照其中的某个或某些关键字的大小,递增或递减的排列起来的操作

常见的排序算法的可以按如图所示分类:

在这里插入图片描述

以下所有的排序算法均以升序的方式实现

2.插入排序

2.1 直接插入排序(InsertSort)

直接插入排序是一种简单的插入排序法,其基本思想是: 把待排序的记录按其关键码值的大小逐个插入到一个已经排好序的有序序列中,直到所有的记录插入完为止,得到一个新的有序序列

简单来说我们平常玩的扑克牌游戏就是一种插入排序

在这里插入图片描述

排序原理:

当插入第 i ( i >= 1 ) 个元素时,前面的 array[0]array[1]array[i-1] 已经排好序,此时用 array[i] 的排序码与 array[i - 1]array[i - 2] 的排序码顺序进行比较,找到插入位置即将 array[i] 插入,原来位置上的元素顺序后移

动图理解:

请添加图片描述

💻排序实现:

voidInsertSort(int* a,int n){for(int i =1; i < n;++i){int end = i -1;int tmp = a[i];while(end >=0){if(tmp < a[end]){ a[end +1]= a[end];--end;}else{break;}} a[end +1]= tmp;}}

⌨️代码解读:

  1. 第一个 for 循环指的是从第一个数开始依次往后遍历,end 指向已排序部分的最后一个元素(即开始的 a[0] ),tmp = a[i] 提前保存该位置的数据,避免移动时的覆盖消除该数据。
  2. 从后往前遍历已排序部分,找到合适的插入位置,如果当前要插入的元素小于已排序部分的元素,将已排序部分的元素后移一位,--end 继续向前比较;如果找到合适的插入位置break 退出循环
  3. 找到合适位置后,a[end + 1] = tmp 将当前要插入的元素插入到合适的位置

🖱️复杂度分析:

时间复杂度:

最好情况:数组已经是有序的,此时内层的 while 循环每次只需要比较一次就会退出,时间复杂度为 O(n)

最坏情况:数组是逆序的,内层的 while 循环每次都需要比较到已排序部分的第一个元素,时间复杂度为 O(n²)

平均情况: 时间复杂度为 O(n²)

空间复杂度:O(1)

总结:元素集合越接近有序,直接插入排序算法的时间效率越高

2.2 希尔排序(ShellSort)

希尔排序又称缩小增量法其基本思想是: 先选定一个整数 gap ,把待排序文件中所有记录分成多个组,所有距离为 gap 的记录分在同一组内,并对每一组内的记录进行排序。然后,取新的 gap ,重复上述分组和排序的工作。当到达 gap = 1 时,所有记录在统一组内排好序

分组如图所示:

在这里插入图片描述

相同颜色的数字为同一组

💻排序实现:

版本1: 按组分配排序

voidShellSort(int* a,int n){int gap = n;for(int j =0; j < gap; j++){ gap /=2;for(int i = j; i < n - gap; i += gap){int end = i;int tmp = a[i + gap];while(end >=0){if(tmp < a[end]){ a[end + gap]= a[end]; end -= gap;}else{break;} a[end + gap]= tmp;}}}}

版本2: 依次排序

voidShellSort(int* a,int n){int gap = n;while(gap >1){ gap /=2;//gap = n / 3 + 1;for(int i =0; i < n - gap; i++){int end = i;int tmp = a[i + gap];while(end >=0){if(tmp < a[end]){ a[end + gap]= a[end]; end -= gap;}else{break;} a[end + gap]= tmp;}}}}

⌨️代码解读:

希尔排序其实和插入排序是十分相似的,希尔排序是对直接插入排序的优化,只不过是每间隔 gap 个数进行排序,插入排序间隔是 1 ,通过多次 gap 的改变,多次的分组排序最终能趋向于有序

🔥值得注意的是:

  1. for 循环中 i < n - gap 是因为排完序后剩下的数根据 gap 的大小,剩下的数已经自动有序了
  2. gap /= 2gap = n / 3 + 1 都是可以的,并没有严格的限制
  3. tmp 一直在 end 的下一个位置,插入的位置一直在 end 的下一个位置
  4. gap > 1 时都是预排序,目的是让数组更接近于有序。当 gap == 1 时,数组已经接近有序的了,这样就会很快
  5. 希尔排序的时间复杂度不好计算,因为 gap 的取值方法很多,导致很难去计算,因此在好些书中给出的希尔排序的时间复杂度都不固定

《数据结构(C语言版)》— 严蔚敏:

在这里插入图片描述

🖱️复杂度分析:

时间复杂度:

最好情况:数组已经是有序的,时间复杂度为 O(n)

最坏情况:数组是逆序的,时间复杂度为 O(n²)

平均情况: 时间复杂度大约为 O( n 1.3 n^{1.3} n1.3)

空间复杂度:O(1)

总结:希尔排序是不稳定的

3.选择排序

3.1 直接选择排序(SelectSort)

直接选择排序可以说是最简单的一种排序了,其基本思想为: 每一次从待排序的数据元素中选出最小(或最大)的一个元素,存放在序列的起始位置,直到全部待排序的数据元素排完

排序原理:

  1. 在元素集合 array[i]array[n-1] 中选择关键码最大(小)的数据元素
  2. 若它不是这组元素中的最后一个(第一个)元素,则将它与这组元素中的最后一个(第一个)元素交换
  3. 在剩余的 array[i]array[n-2]array[i+1]array[n-1])集合中,重复上述步骤,直到集合剩余 1 个元素

动图理解:

请添加图片描述

💻排序实现:

voidSwap(int* p1,int* p2){int tmp =*p1;*p1 =*p2;*p2 = tmp;}voidSelectSort(int* a,int n){int left =0, right = n -1;while(left < right){int mini = left, maxi = right;for(int i = left; i <= right;++i){if(a[i]< a[mini]){ mini = i;}if(a[i]> a[maxi]){ maxi = i;}}Swap(&a[left],&a[mini]);//如果left和maxi重叠,交换后修正一下if(left == maxi){ maxi = mini;}Swap(&a[right],&a[maxi]);++left;--right;}}

⌨️代码解读:

  1. mini 初始化为 leftmaxi 初始化为 right,用于记录当前未排序部分的最小值和最大值的索引
  2. 通过 for 循环从 leftright 遍历数组,比较每个元素与 a[mini]a[maxi] 的大小,如果发现更小的元素则更新 mini,如果发现更大的元素则更新 maxi
  3. 调用 Swap 函数将最小值 a[mini] 交换到左边界 a[left] 的位置,最大值 a[maxi] 交换到右边界 a[right] 的位置
  4. ++left--right 分别将左边界右移和右边界左移,缩小未排序部分的范围

🔥值得注意的是: 如果 left 恰好等于 maxi,说明在交换最小值时,最大值的位置已经被交换到了 mini 处,所以需要将 maxi 更新为 mini

🖱️复杂度分析:

时间复杂度:

最好情况: 即使数组已经有序,每一轮仍然需要遍历未排序部分来确定最小值和最大值的位置,时间复杂度为 O(n)

最坏情况: 当数组是逆序排列时,同样需要进行完整的遍历和交换操作,时间复杂度为 O(n²)

平均情况: 时间复杂度为 O(n²)

空间复杂度:O(1)

总结:直接选择排序思考非常好理解,但是效率不是很好,实际中很少使用

3.2 堆排序(HeapSort)

堆排序是指利用堆积树(堆)这种数据结构所设计的一种排序算法,它是选择排序的一种。它是通过堆来进行选择数据。需要注意的是排升序要建大堆排降序建小堆

堆的专题章节已经有详细的分析了,这里就不过多讲解

传送门:【初阶数据结构】森林里的树影 “堆” 光:堆

总结:堆排序使用堆来选数,效率就高了很多


希望读者们多多三连支持

小编会继续更新

你们的鼓励就是我前进的动力!

请添加图片描述

Read more

【2025 年最新版】Java JDK 安装与环境配置教程(附图文超详细,Windows+macOS 通用)

【2025 年最新版】Java JDK 安装与环境配置教程(附图文超详细,Windows+macOS 通用)

Java 作为后端开发的核心语言,JDK(Java Development Kit)是开发和运行 Java 程序的基础环境。2025 年最新推荐安装JDK 21—— 这是 Java SE 平台的长期支持(LTS)版本,可免费用于生产环境及重新分发,直到 2026 年 9 月仍能享受免费更新服务,后续更新将按 Oracle OTN 许可证管理。本文将针对 Windows(10/11)和 macOS(Intel/M 芯片)两大主流系统,提供从官方下载、分步安装到环境变量配置的完整教程,附带验证步骤和常见问题排查,零基础也能轻松上手! 一、JDK 21 核心优势(为什么选它?) 1. 长期支持更稳定:作为

By Ne0inhk
Java 大视界 -- Java 大数据在智能家居环境监测与智能调节中的应用拓展(423)

Java 大视界 -- Java 大数据在智能家居环境监测与智能调节中的应用拓展(423)

Java 大视界 -- Java 大数据在智能家居环境监测与智能调节中的应用拓展(423) * 引言: * 快速上手指南:3 步跑通智能家居 Demo(新手友好) * Step 1:环境准备(必装软件清单) * Step 2:代码运行(按顺序执行) * Step 3:效果验证(用 Postman 模拟数据) * 正文: * 一、智能家居环境监测与调节的核心痛点 * 1.1 设备数据的 “异构化” 困境 * 1.1.1 多源数据的 “协议壁垒” * 1.1.2 数据规模的 “爆发式增长” * 1.2 实时调节的 “滞后性” 痛点 * 1.

By Ne0inhk
计算机毕业设计springboot中小型制造型企业erp管理系统 基于Spring Boot的中小型制造企业资源计划系统设计与实现 基于Java的中小型生产企业运营管控平台开发

计算机毕业设计springboot中小型制造型企业erp管理系统 基于Spring Boot的中小型制造企业资源计划系统设计与实现 基于Java的中小型生产企业运营管控平台开发

计算机毕业设计springboot中小型制造型企业erp管理系统0t90v9 (配套有源码 程序 mysql数据库 论文) 本套源码可以在文本联xi,先看具体系统功能演示视频领取,可分享源码参考。 本系统采用Spring Boot作为核心开发框架,结合MySQL数据库与Tomcat服务器,构建B/S架构的Web应用平台。系统严格遵循MVC设计模式,将业务逻辑、数据访问与视图展示层解耦,确保代码的高内聚低耦合与良好的可扩展性。前端界面设计注重用户体验,采用简洁直观的交互风格,降低员工学习成本。系统支持Windows操作系统下的稳定运行,兼容主流浏览器访问,满足企业多场景、跨终端的办公需求。 系统核心功能涵盖以下模块: 员工管理模块:实现员工基础信息的录入、查询、修改与删除,包含员工工号、姓名、性别、年龄、联系方式、所属部门、职务等核心字段,支持员工账号的注册与登录验证。 人事档案模块:记录员工详细人事信息,包括入职日期、个人照片、职务履历、个人档案材料等,支持档案的电子化管理与快速检索。 部门信息管理模块:维护企业组织架构,支持部门名称的增删改查,构建清晰的部门层级关系。 员

By Ne0inhk
飞算JavaAI全链路实战:智能构建高可用电商系统核心架构

飞算JavaAI全链路实战:智能构建高可用电商系统核心架构

飞算JavaAI全链路实战:智能构建高可用电商系统核心架构 前言:AI编程新时代的电商系统开发范式变革 最近学习人工智能时遇到一个好用的网站给大家分享一下 人工智能学习 在当今数字经济时代,电商系统作为企业数字化转型的核心载体,其复杂度和技术要求与日俱增。一个完整的电商系统不仅需要处理商品、订单、用户等基础业务,还要应对高并发、分布式事务、数据一致性等复杂技术挑战。传统开发模式下,从需求分析到系统上线往往需要耗费大量人力和时间成本。 本次我通过飞算JavaAI平台,深入探索"电商系统核心功能模块"这一实战赛道,全面体验了从需求分析到代码生成的全链路开发过程。本文将完整呈现如何借助AI辅助开发工具,高效构建一个包含用户管理、商品系统、订单流程、支付集成等核心模块的电商平台,严格遵循"需求分析-开发实录-优化调试-成果总结"的四大核心框架,为开发者提供一份AI辅助全栈开发的完整实践指南。 一、需求分析与规划:构建电商系统的业务架构蓝图 在启动飞算JavaAI之前,需要进行全面的业务需求梳理和系统架构设计,这是确保AI生成代码符合预期的基础。 1.(理解需求)系统核心模块与

By Ne0inhk