数据结构初阶排序全解
目录
1>>排序前言
2>>插入排序
2.1>>直接插入排序
2.2>>希尔排序
3>>选择排序
3.1>>直接选择排序
3.2>>堆排序
4>>交换排序
4.1冒泡排序
4.2快速排序
5>>归并排序
6>>测试
test.c
sort.h
sort.c
7>>总结
1>>排序前言
排序顾名思义,就是将一组乱序的数,按从大到小或者从小到大进行排列的操作。
常见的排序如下
可以看到排序也是有分类的,既然有分类,那么这边给出宝子们一个思考,在最后测试的时候给出答案,思考1:排序方法这么多,哪个最好呢?最差的有什么用呢?
那么废话不多说,今天的重点内容就是理解并写出一个个排序算法,现在直接上高速,带着宝子们攻破一个个排序算法!
这边给出各个排序算法的声明,会在对应内容进行解释,宝子们先有个印象即可~~
2>>插入排序
2.1>>直接插入排序
直插排序类似生活中的扑克牌,每拿到一张新牌,就要插入到手中的有序扑克牌中。那靠这个思路就可以写出步骤:
1.设置新插入的牌tmp
2.比较之前的每一个牌,找到比tmp小的,将整体往后移动腾出空间
3.插入到这张牌后面
代码如下:

设置end为每次手中牌的末尾,tmp为新插入的牌,循环end>=0进入循环,如果比较发现手中牌比插入牌大,那么将手中牌往后移动一位,若小,跳出循环,将这张牌后一张牌,也就是空出来的位置赋值tmp,也就是新插入的牌。
2.2>>希尔排序
希尔排序相当于直接插入排序的最优化法,什么意思呢?就是通过预排序,将整个数组提升到接近有序的状态,最后再用直插排序进行排序,这样会快很多,那怎么实现呢?就是通过分组,将每隔n/3+1个数两两分为一组,先实现一次预备排序,这里直接来看代码进行解释


刚开始令gap等于n,只要gap大于1那么就进入循环,每次gap等于gap/3+1,每次都进行预排序,当gap为1时就是直接插入排序啦,代码也只需要更改直接插入排序里面为1的值即可。
3>>选择排序
3.1>>直接选择排序
思路:
1.定义begin和end代表开始与结束,每次循环begin++,end--直到begin>=end
2.找出数组内最大值和最小值
3.将最大值和end交换,最小值与begin交换
代码如下:

代码就是定义begin为0,end为末尾值,然后循环遍历找最大值下标和最小值下标,在出循环的时候进行交换即可。
注意:当begin等于max的时候,此时交换相当于原地不动,因此需要让max等于min让它实现原地交换一次,在跟end互换。如 9 3 5 1begin为9,max也为9,end为1,min也为1,实现上面交换,1 3 5 9,max又跟end交换,9 3 5 1,所以需要避免这种情况。
3.2>>堆排序
1.要实现堆排序,首先得要是一个堆,那么第一个循环就要把堆建立,双亲结点向下调整思想,从最后一个结点的双亲结点开始(最后一个结点是n-1)它的双亲结点是-1然后除2,依次向下调整,这样就是一个堆。
2.将这个棵树的每一个子树都想象成一个堆,然后:
3.从最后一个结点开始,到0为止,每次交换它的最后一个结点和第0个结点,然后向下调整,这样就能够从大/从小排序

4>>交换排序
4.1冒泡排序
需要十个数就用数组存储,那么两两相邻的相比不就是数组两个相邻元素的比较吗?所以使用j和j+1进行比较,这里要注意j要<9,因为这样操作的就是0-8下标的值,j+1就不会越界,并且每次循环都将一个最大的放到最右边了,所以j<9-i,每次循环少循环一次。
到这里,基本框架已经完成,我们只需再完善一下:如果我们输入的十个数是9 0 1 2 3 4 5 6 7 8,那么会发现,即使我们第一次循环就已经排序完成,但是循环还是进行了,那么此时我们就可以加入一个布尔值变量,在循环开始定义为1,如果对数值进行两两替换,那么布尔值就为0,若出来为1就跳出循环即可,这种操作广泛用于各种循环和一些满足条件跳出的代码,可以充分减少运行次数。

4.2快速排序
快速排序讲一种最经典的排序找基准值法——hoare法


如何实现快速排序呢?就要通过递归的思想实现,每次找到一个基准值,使得左边的数都小于它,右边的数都大于它。
那么hoare方法就是设定基准值为0下标值,定义left和right,从右边往左找比基准值小的,从左往右找比基准值小的,然后进行交换,最后当left>right时,交换基准值和right,返回right就是基准值啦!
5>>归并排序
这个比较复杂,但是也还好,容我细说

以上素材来源网络,如有侵权告知删除。
借用一下,通过这张图片来理解,所谓归并,就是一个数组微分积分的过程,将一个无序数组,不断的拆解,拆成单个单个有序数组,实现两个有序数组的排序,这就是归并。
那么代码怎么实现呢?
首先,这是一个递归的过程,每次都进行二分,分成左右序列,
0-3为新无序数列,4-7为新无序数列,分到它为有序为止,也就是
单个的时候,本身就是一个有序的
比较10,6,谁小谁先放,然后依次放入新数组,此时需要一个新数组暂时存排序后的值,然后赋值给原数组。来看代码吧
void _MergeSort(int* arr,int left, int right,int* tmp) {if (left >= right)return; int mid = left + (right - left) / 2;//分成左右序列_MergeSort(arr, left, mid, tmp);_MergeSort(arr, mid+1, right, tmp);//定义两个假有序数组int begin1 = left;int end1 = mid;int begin2 = mid + 1;int end2 = right;int index = begin1;//index表示第二个暂存数组的下标值while (begin1 <= end1 && begin2 <= end2) {if (arr[begin1] > arr[begin2]) {tmp[index++] = arr[begin2++];}else{tmp[index++] = arr[begin1++];}}while (begin1 <= end1) {tmp[index++] = arr[begin1++];}while (begin2 <= end2) {tmp[index++] = arr[begin2++];}//最后把暂存数组值放到原数组for (int i = left; i <= right; i++) {arr[i] = tmp[i];}
}
void MergeSort(int* arr, int n) {int* tmp = (int*)malloc(sizeof(int) * n);_MergeSort(arr, 0, n - 1, tmp);free(tmp);
}
希望我的讲解能帮助大家理解归并排序。
6>>测试
接下来测试每个排序的时间复杂度

这里能看到快排和归并是最快的,还有堆排序,这边没有导入它的代码,下来可以自己尝试噢,希尔其次,冒泡排序最次,那解决前面的疑问,为什么还要冒泡排序呢?因为它有教学意义,较为简单,更好理解。
test.c
#include"sort.h"
#include<stdio.h>
#include<time.h>
#include<stdlib.h>
void PrintArr(int* arr, int n)
{for (int i = 0; i < n; i++){printf("%d ", arr[i]);}printf("\n");
}void test() {int a[] = { 5, 3, 9, 6, 2, 4, 7, 1, 8 };int n = sizeof(a) / sizeof(a[0]);printf("排序之前:");PrintArr(a, n);MergeSort(a, n);//quicksort(a, 0, n - 1);printf("排序之后:");PrintArr(a, n);
} void TestOP()
{srand(time(0));const int N = 100000;int* a1 = (int*)malloc(sizeof(int) * N);int* a2 = (int*)malloc(sizeof(int) * N);int* a3 = (int*)malloc(sizeof(int) * N);//int* a4 = (int*)malloc(sizeof(int) * N);int* a5 = (int*)malloc(sizeof(int) * N);int* a6 = (int*)malloc(sizeof(int) * N);//int* a7 = (int*)malloc(sizeof(int) * N);for (int i = 0; i < N; ++i){a1[i] = rand();a2[i] = a1[i];a3[i] = a1[i];//a4[i] = a1[i];a5[i] = a1[i];a6[i] = a1[i];//a7[i] = a1[i];}int begin1 = clock();insertsort(a1, N);int end1 = clock();int begin2 = clock();shellsort(a2, N);int end2 = clock();int begin3 = clock();selectsort(a3, N);int end3 = clock();//int begin4 = clock();//HeapSort(a4, N);//int end4 = clock();int begin5 = clock();quicksort(a5, 0, N - 1);int end5 = clock();int begin6 = clock();MergeSort(a6, N);int end6 = clock();//int begin7 = clock();//BubbleSort(a7, N);//int end7 = clock();printf("InsertSort:%d\n", end1 - begin1);printf("ShellSort:%d\n", end2 - begin2);printf("SelectSort:%d\n", end3 - begin3);//printf("HeapSort:%d\n", end4 - begin4);printf("QuickSort:%d\n", end5 - begin5);printf("MergeSort:%d\n", end6 - begin6);//printf("BubbleSort:%d\n", end7 - begin7);//free(a1);//free(a2);//free(a3);//free(a4);//free(a5);//free(a6);//free(a7);
}int main()
{//test();TestOP();return 0;
}
sort.h
#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<stdbool.h>
#include<assert.h>
#include<string.h>
#include"stack.h"
void insertsort(int* arr, int n);//直插排序
void shellsort(int* arr, int n);//希尔排序void selectsort(int* arr, int n);//选择排序
void HeapSort(int* arr, int n);//堆排序void BubbleSort(int* arr, int n);//冒泡排序
void quicksort(int* arr, int left, int right);//快排
void QuickSortNonR(int* arr, int left, int right);//非递归借栈快排void MergeSort(int* arr, int n);//归并排序
sort.c
#include"sort.h"
#include"stack.h"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 (arr[end] > tmp) {arr[end + 1] = arr[end];end--;}else {break;}}arr[end + 1] = tmp;}
}void shellsort(int* arr, int n) {int gap = n;while (gap > 1) {gap = gap / 3 + 1;for (int i = 0; i < n - gap; i++) {int end = i;int tmp = arr[end + gap];while (end >= 0) {if (arr[end] > tmp) {arr[end + gap] = arr[end];end-=gap;}else {break;}}arr[end + gap] = tmp;}}
}
void swap(int* x, int* y) {int tmp = *x;*x = *y;*y = tmp;
}
void selectsort(int* arr, int n) {int end = n - 1;int begin = 0;while(begin<end){int min = begin;int max = begin;for (int i = begin + 1; i <=end; i++) {if (arr[i] < arr[min])min = i;if (arr[i] > arr[max])max = i;}if (begin == max)max = min;swap(&arr[min], &arr[begin]);swap(&arr[max], &arr[end]);begin++;end--;}
}// 你这个是什么方法hoare找基准值
int _quicksort(int* arr, int left, int right) {int keyi = left;left++;while (left <= right) {while (left <= right && arr[right] > arr[keyi])right--;while (left <= right && arr[left] < arr[keyi])left++;if (left<=right)swap(&arr[left++], &arr[right--]);}swap(&arr[right], &arr[keyi]);return right;
}
int _quicksort1(int* arr, int left, int right) {//挖洞法//hole等于初始值,right从右从右往左找小,left找大int hole = left;int key = arr[hole];while (left < right) {while (left<right && arr[right]>key) {right--;}arr[hole] = arr[right];hole = right;while (left<right && arr[left]<key) {left++;}arr[hole] = arr[left];hole = left;}arr[hole] = key;return hole;
}int _quicksort2(int* arr, int left, int right) {//前后指针int prev = left;int key = left;int pcur = left + 1;while (pcur <= right) {if (arr[pcur] < arr[key]&& ++prev != pcur) {swap(&arr[pcur], &arr[prev]);}pcur++;}swap(&arr[prev], &arr[key]);return prev;
}
void quicksort(int* arr, int left, int right) {//找基准值//right从右往左找比基准值小,left从左往右找比基准值大,找到实现交换,// left>right交换right和基准值if (left >= right)return;int keyi = _quicksort(arr,left,right);quicksort(arr, left, keyi-1);quicksort(arr, keyi+1, right);
}void QuickSortNonR(int* arr, int left, int right) {stack st;stinit(&st);stenter(&st, right);stenter(&st, left);while (!stackempty(&st)) {int begin = stackTop(&st);stackpop(&st);int end = stackTop(&st);stackpop(&st);//双指针找基准值划分左右序列int prev = begin;int key = begin;int pcur = begin + 1;while (pcur <= end) {if (arr[pcur] < arr[key] && ++prev != pcur) {swap(&arr[pcur], &arr[prev]);}pcur++;}swap(&arr[prev], &arr[key]);key = prev;//基准值下标为key//划分左序列(左子树)[begin,key-]和右序列[key+1,end]if (key + 1 < end) {stenter(&st, end);stenter(&st, key+1);}if (key - 1 > begin) {stenter(&st, key-1);stenter(&st,begin);}}struin(&st);
}
void _MergeSort(int* arr,int left, int right,int* tmp) {if (left >= right)return; int mid = left + (right - left) / 2;//分成左右序列_MergeSort(arr, left, mid, tmp);_MergeSort(arr, mid+1, right, tmp);//定义两个假有序数组int begin1 = left;int end1 = mid;int begin2 = mid + 1;int end2 = right;int index = begin1;//index表示第二个暂存数组的下标值while (begin1 <= end1 && begin2 <= end2) {if (arr[begin1] > arr[begin2]) {tmp[index++] = arr[begin2++];}else{tmp[index++] = arr[begin1++];}}while (begin1 <= end1) {tmp[index++] = arr[begin1++];}while (begin2 <= end2) {tmp[index++] = arr[begin2++];}//最后把暂存数组值放到原数组for (int i = left; i <= right; i++) {arr[i] = tmp[i];}
}
void MergeSort(int* arr, int n) {int* tmp = (int*)malloc(sizeof(int) * n);_MergeSort(arr, 0, n - 1, tmp);free(tmp);
}
7>>总结
今天讲解了各种类型的排序算法,最常用的还是时间复杂度最小的堆排序、快排、归并排序,希望这篇文章对大家有用,如果有问题欢迎评论区一起探讨,谢谢宝子们的观看,期待与你下篇再见~~
相关文章:
数据结构初阶排序全解
目录 1>>排序前言 2>>插入排序 2.1>>直接插入排序 2.2>>希尔排序 3>>选择排序 3.1>>直接选择排序 3.2>>堆排序 4>>交换排序 4.1冒泡排序 4.2快速排序 5>>归并排序 6>>测试 test.c sort.h sort.c 7…...
MySQL的SQL语句之触发器的创建和应用
触发器 Trigger 一.触发器 作用:当检测到某种数据表发生数据变化时,自动执行操作,保证数据的完整性,保证数据的一致性。 1.创建一个触发器 如上图所示,查看这个create的帮助信息的时候,这个create trig…...
myWebserver 介绍
项目总结 项目准备过程中,主要阅读了《Linux 高性能服务器编程》游双 一书。源码参考的是:TinyWebServer,我在此源码的基础上做了一定的优化和修改。 我的代码:Github: myWebserver: 我的C服务器项目 我的 Webserver 项目总结&…...
钉钉平台开发小程序
一、下载小程序开发者工具 官网地址:小程序开发工具 - 钉钉开放平台 客户端类型 下载链接 MacOS x64 https://ur.alipay.com/volans-demo_MiniProgramStudio-x64.dmg MacOS arm64 https://ur.alipay.com/volans-demo_MiniProgramStudio-arm64.dmg Windows ht…...
九识智能与徐工汽车达成战略合作,共绘商用车未来新蓝图
近日,九识智能与徐工汽车签署战略合作协议,标志着双方在智能驾驶技术与新能源商用车融合应用、联合生产及市场推广等方面迈入深度合作的新篇章,将共同引领智能驾驶技术商业化浪潮。 近年来,在国家智能化发展战略的引领下ÿ…...
Serverless + AI 让应用开发更简单
本文整理自 2024 云栖大会,阿里云智能高级技术专家,史明伟演讲议题《Serverless AI 让应用开发更简单》 随着云计算和人工智能(AI)技术的飞速发展,企业对于高效、灵活且成本效益高的解决方案的需求日益增长。本文旨在…...
外包功能测试就干了4周,技术退步太明显了。。。。。
先说一下自己的情况,大专生,21年通过校招进入武汉某软件公司,干了差不多3个星期的功能测试,那年国庆,感觉自己不能够在这样下去了,长时间呆在一个舒适的环境会让一个人堕落!而我才在一个外包企业干了4周的功…...
外观模式及运用场景
外观模式(Facade Pattern)是一种结构性设计模式,它为复杂子系统提供一个统一的接口,从而简化与这些子系统的交互。通过外观模式,客户端可以更轻松地使用复杂系统,而不必了解其内部实现。接下来,…...
PyQt5实战——多脚本集合包,UI以及工程布局(二)
个人博客:苏三有春的博客 系列往期: PyQt5实战——多脚本集合包,前言与环境配置(一) 布局 2.1 UI页面布局 整体框架分为分为三个部分,垂直分布。 第一个部分为功能选择按钮(如UTF-8转换&#…...
Python 数据结构对比:列表与数组的选择指南
文章目录 💯前言💯Python中的列表(list)和数组(array)的详细对比1. 数据类型的灵活性2. 性能与效率3. 功能与操作4. 使用场景5. 数据结构选择的考量6. 实际应用案例7. 结论 💯小结 💯…...
gem5运行简单RISC-V全系统模拟
简单记录gem5中运行最简单的RISC-V Full System Simulation的过程 首先是编译RISC-V和m5term,这部分不多写了,官网均有对应教程。 之后直接使用官方在configs/example/gem5_library目录下的riscv-fs.py 运行如下命令 ./build/RISCV/gem5.opt configs/…...
洛谷 P1195 口袋的天空
自用。 题目传送门:口袋的天空 - 洛谷 题解:Inori_333 参考题解:题解 P1195 【口袋的天空】 - 洛谷专栏 /*P1195 口袋的天空https://www.luogu.com.cn/problem/P11952024/11/03 submit:inori333 */#include <iostream> #include &…...
ffmpeg视频滤镜:膨胀操作-dilation
滤镜介绍 dilation 官网链接 > FFmpeg Filters Documentation 膨胀滤镜会使图片变的更亮,会让细节别的更明显。膨胀也是形态学中的一种操作,在opencv中也有响应的算子。此外膨胀结合此前腐蚀操作,可以构成开闭操作。 开操作是先腐蚀…...
3.3 windows,ReactOS系统中页面的换出----2,结构体PHYSICAL_PAGE
系列文章目录 提示:这里可以添加系列文章的所有文章的目录,目录需要自己手动添加 例如:第一章 Python 机器学习入门之pandas的使用 提示:写完文章后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目…...
lvgl
lvgl 目录 lvgl Lvgl移植到STM32 -- 1、下载LVGL源码 -- 2、将必要文件复制到工程目录 -- 3、修改配置文件 将lvgl与底层屏幕结合到一块 -- lvgl也需要有定时器,专门给自己做了一个函数,告诉lvgl经过了多长时间(ms(毫秒)级别) 编写代码 lvgl的中文教程手册网站…...
【django】RESTful API 设计指南
目录 一、协议 二、域名 三、版本(Versioning) 四、路径(Endpoint) 五、HTTP动词 5.1 CRUD操作: 5.2 其他动词: 六、过滤信息(Filtering) 七、状态码(Status Co…...
提升大数据量分页查询性能:深分页优化全解
前言 在处理数据量逐渐增大的数据库表时,优化查询性能是一个常见的挑战。朋友们可能会建议说,创建索引不就能解决问题了吗?然而,当数据量达到相当规模时,简单的索引可能不足以应对所有情况。这时,可能会有…...
WPF 实现冒泡排序可视化
WPF 实现冒泡排序可视化 实现冒泡排序代码就不过多讲解,主要是实现动画效果思路,本demo使用MVVM模式编写,读者可自行参考部分核心代码,即可实现如视频所示效果。 对于新手了解算法相关知识应该有些许帮助,至于其它类型…...
Claude 3.5 新功能 支持对 100 页的PDF 图像、图表和图形进行可视化分析
Claude 3.5 Sonnet发布PDF图像预览新功能,允许用户分析长度不超过100页的PDF中的视觉内容。 此功能使用户能够轻松上传文档并提取信息,特别适用于包含图表、图形和其他视觉元素的研究论文和技术文档。 视觉PDF分析:用户现在可以从包含各种视觉…...
正式开源:从 Greenplum 到 Cloudberry 迁移工具 cbcopy 发布
Cloudberry Database 作为 Greenplum 衍生版本和首选开源替代,由 Greenplum 原始团队成员创建,与 Greenplum 保持原生兼容,并能实现无缝迁移,且具备更新的 PostgreSQL 内核和更丰富的功能。GitHub: https://github.com/cloudberry…...
使用docker在3台服务器上搭建基于redis 6.x的一主两从三台均是哨兵模式
一、环境及版本说明 如果服务器已经安装了docker,则忽略此步骤,如果没有安装,则可以按照一下方式安装: 1. 在线安装(有互联网环境): 请看我这篇文章 传送阵>> 点我查看 2. 离线安装(内网环境):请看我这篇文章 传送阵>> 点我查看 说明:假设每台服务器已…...
Java - Mysql数据类型对应
Mysql数据类型java数据类型备注整型INT/INTEGERint / java.lang.Integer–BIGINTlong/java.lang.Long–––浮点型FLOATfloat/java.lang.FloatDOUBLEdouble/java.lang.Double–DECIMAL/NUMERICjava.math.BigDecimal字符串型CHARjava.lang.String固定长度字符串VARCHARjava.lang…...
对WWDC 2025 Keynote 内容的预测
借助我们以往对苹果公司发展路径的深入研究经验,以及大语言模型的分析能力,我们系统梳理了多年来苹果 WWDC 主题演讲的规律。在 WWDC 2025 即将揭幕之际,我们让 ChatGPT 对今年的 Keynote 内容进行了一个初步预测,聊作存档。等到明…...
Python 包管理器 uv 介绍
Python 包管理器 uv 全面介绍 uv 是由 Astral(热门工具 Ruff 的开发者)推出的下一代高性能 Python 包管理器和构建工具,用 Rust 编写。它旨在解决传统工具(如 pip、virtualenv、pip-tools)的性能瓶颈,同时…...
中医有效性探讨
文章目录 西医是如何发展到以生物化学为药理基础的现代医学?传统医学奠基期(远古 - 17 世纪)近代医学转型期(17 世纪 - 19 世纪末)现代医学成熟期(20世纪至今) 中医的源远流长和一脉相承远古至…...
RabbitMQ入门4.1.0版本(基于java、SpringBoot操作)
RabbitMQ 一、RabbitMQ概述 RabbitMQ RabbitMQ最初由LShift和CohesiveFT于2007年开发,后来由Pivotal Software Inc.(现为VMware子公司)接管。RabbitMQ 是一个开源的消息代理和队列服务器,用 Erlang 语言编写。广泛应用于各种分布…...
第7篇:中间件全链路监控与 SQL 性能分析实践
7.1 章节导读 在构建数据库中间件的过程中,可观测性 和 性能分析 是保障系统稳定性与可维护性的核心能力。 特别是在复杂分布式场景中,必须做到: 🔍 追踪每一条 SQL 的生命周期(从入口到数据库执行)&#…...
为什么要创建 Vue 实例
核心原因:Vue 需要一个「控制中心」来驱动整个应用 你可以把 Vue 实例想象成你应用的**「大脑」或「引擎」。它负责协调模板、数据、逻辑和行为,将它们变成一个活的、可交互的应用**。没有这个实例,你的代码只是一堆静态的 HTML、JavaScript 变量和函数,无法「活」起来。 …...
苹果AI眼镜:从“工具”到“社交姿态”的范式革命——重新定义AI交互入口的未来机会
在2025年的AI硬件浪潮中,苹果AI眼镜(Apple Glasses)正在引发一场关于“人机交互形态”的深度思考。它并非简单地替代AirPods或Apple Watch,而是开辟了一个全新的、日常可接受的AI入口。其核心价值不在于功能的堆叠,而在于如何通过形态设计打破社交壁垒,成为用户“全天佩戴…...
Ubuntu系统复制(U盘-电脑硬盘)
所需环境 电脑自带硬盘:1块 (1T) U盘1:Ubuntu系统引导盘(用于“U盘2”复制到“电脑自带硬盘”) U盘2:Ubuntu系统盘(1T,用于被复制) !!!建议“电脑…...
