经典排序算法
创始人
2025-05-31 11:04:14

文章目录

  • 序言
    • 算法分类
    • 算法复杂度
  • 1、交换排序
    • 1.1 冒泡排序(Bubble Sort)
    • 1.2 快速排序(Quick Sort)
  • 2、选择排序
    • 2.1 直接选择排序
  • 3、插入排序
    • 3.1 直接插入排序
    • 3.2 希尔排序
  • 4、归并排序

序言

算法分类

排序算法可以分为两大类:

  • 比较类排序:通过相互比较来决定各元素之间的顺序,由于其时间复杂度不能突破O(nlogn),即非线性时间比较类排序。主要包含交换排序、插入排序、选择排序和归并排序;
  • 非比较类排序:不通过比较来决定元素间的顺序,它可以突破基于比较排序的时间下界,以线性时间运行,即线性时间非比较类排序。主要包含计数排序、桶排序和基数排序;

在这里插入图片描述

算法复杂度

排序算法时间复杂度(平均)时间复杂度(最差)时间复杂度(最好)空间复杂度稳定性
冒泡排序O(n2)O(n2)O(n)O(1)稳定
快速排序O(n㏒2n)O(n2)O(n㏒2n)O(n㏒2n)不稳定
简单插入排序O(n2)O(n2)O(n)O(1)稳定
希尔排序O(n1.3)O(n2)O(n)O(1)不稳定
简单选择排序O(n2)O(n2)O(n2)O(1)不稳定
堆排序O(n㏒2n)O(n㏒2n)O(n㏒2n)O(1)不稳定
归并排序O(n㏒2n)O(n㏒2n)O(n㏒2n)O(n)稳定
计数排序O(n+k)O(n+k)O(n+k)O(n+k)稳定
桶排序O(n+k)O(n2)O(n)O(n+k)稳定
基数排序O(n2k)O(n2k)O(n2k)O(n+k)稳定

稳定性: 排序过程中,相等的两个值对比时是否会发生前后变化。

  • (1)稳定:如果a原本在b前面,而a=b,排序之后a仍然在b的前面。
  • (2)不稳定:如果a原本在b的前面,而a=b,排序之后 a 可能会出现在 b 的后面。

时间复杂度: 对排序数据的总的操作次数。反映当n变化时,操作次数呈现什么规律。
空间复杂度: 是指算法在计算机内执行时所需存储空间的度量,它也是数据规模n的函数。

1、交换排序

1.1 冒泡排序(Bubble Sort)

描述:
          冒泡排序 是一种简单的排序算法。重复遍历需要排序的数列,每次比较两个元素,按照升序或降序的方式交换位置,直到没有再需要交换的值,即该数列已经排序完成。这个算法的名字由来是因为越小的元素会经由交换慢慢“浮”到数列的顶端。

图形效果:
在这里插入图片描述
设计思想(分治法)::

使用双重循环遍历该数组,外层循环控制遍历次数,内层循环每个位置相互比较,按照升序或降序的规则,对比时小的或大的数值将被放在前面或后面。

性能:

  • 时间复杂度:最好时间:O(n) 最坏:O(n2) 平均:O(n2)
  • 辅助空间:O(1)
  • 稳定性:稳定

代码实现:

/*** 交换排序:冒泡排序* @param array* @return*/
public static int[] bubbleSort(int[] array){for (int i = 0; i < array.length; i++) {//外层循环控制遍历的位置数量for (int j = 0; j < array.length - i - 1; j++) {//内层循环对比是否交换位置if(array[j] > array[j+1]){int tmp = array[j];array[j] = array[j+1];array[j+1] = tmp;}}}return array;
}

1.2 快速排序(Quick Sort)

描述:

快速排序使用分治法来把一个串(list)分为两个子串(sub-lists)。

  • 从数列中挑出一个元素,称为 “基准”(pivot);
  • 重新排序数列,所有元素比基准值小的摆放在基准前面,所有元素比基准值大的摆在基准的后面(相同的数可以到任一边)。在这个分割结束之后,对基准值的排序就已经完成;
  • 递归地(recursive)把小于基准值元素的子数列和大于基准值元素的子数列排序。

图形效果:
在这里插入图片描述

设计思想(分治法):

  • (1)对于一组给定的记录,通过一趟排序后,将原序列分为两部分,其中前一部分的所有记录均比后一个序列的记录小;
  • (2)再依次对前后两部分的记录进行快速排序;

性能::

  • 时间复杂度:最坏时间:O(n2); 最好和平均:O(n*log n)
  • 辅助空间:O(log n)
  • 稳定性:不稳定

代码实现:

/*** 交换排序:快速排序* 时间复杂度:最坏时间:O(n^2); 最好和平均:O(n*log n) 辅助空间:O(log n)  稳定性:不稳定* @param array 待排序数组* @return 排序后的数组*/
public static int[] quickSort(int[] array) {if(array!=null&&array.length>0){quickSorts(array,0,array.length-1);}return array;
}
private static void quickSorts(int[] array, int low, int high){//递归结束条件if(low >= high){return ;}//定义基准值int key = array[low];//从前往后遍历的索引坐标int i = low;//从后往前遍历的索引坐标int j = high;//从基准开始出发,i、j两个值从前和从后两个方向向中间遍历,直到相遇(交叉)即停止遍历,代表此次找到新的基准和分段排序while(i < j){//循环从后往前遍历获取比基准key小的值,记录下标jwhile(i < j && array[j] > key){j--;}//循环从前往后遍历获取比基准key大的值,记录下标iwhile(i < j && array[i] <= key){i++;}/*** 找到基准key进行上述i和j的循环需要交换的值* i为找到大于等于基准的下标索引* j为找到小于基准的下标索引* i大于等于j则表明不需要交换,不满足交换i、j条件*/if(i < j){int temp = array[i];array[i] = array[j];array[j] = temp;}}// 调整key的基准即int temp = array[low];array[low] = array[i];array[i] = temp;//排序i左边quickSorts(array,low,i-1);//排序i右边quickSorts(array,i+1,high);
}

2、选择排序

2.1 直接选择排序

描述:

          选择排序就是每次找未排序数组中的最小(最大)的记录并固定其位置。

图形效果:
在这里插入图片描述

设计思想:

  • (1)对于给定的一组记录,经过第一轮比较后得到最小的记录,然后将该记录与第一个记录的位置进行交换;
  • (2)接着对不包括第一个记录之外的其他记录进行第二轮比较,得到最小的记录并与第二个记录进行位置交换;
  • (3)重复该过程,直到全部排序

性能::

  • 时间复杂度:最好和最坏的平均时间均为O(n2)
  • 辅助空间:O(1)
  • 稳定性:不稳定

代码实现:

/*** 选择排序:直接选择排序* 时间复杂度:最好和最坏平均时间均为O(n^2) 辅助空间:O(1) 稳定性:不稳定* @param array* @return*/
public static int[] chooseSort(int[] array){//控制外层循环即N次交换值,将剩余后的最小值与i位置交换for(int i = 0; i < array.length - 1; i++){//记录每次循环的最小值索引int min = i;//遍历找到每次循环的最小值for(int j = i + 1; j < array.length; j++){if(array[j] < array[min]){//记录本次外层循环的最小值min = j;}}//将最小值和第i个位置进行对换int temp = array[i];array[i] = array[min];array[min] = temp;}return array;
}

3、插入排序

3.1 直接插入排序

描述:
          将一个记录插入到已经排好序的有序表中,从而一个新的、记录数增1的有序表。在其实现过程使用双层循环,外层循环对除了第一个元素之外的所有元素,内层循环对当前元素前面有序表进行待插入位置查找并进行移动。

图形效果:
在这里插入图片描述

设计思想:

  • 初始时假设第一个记录自成一个有序序列,其余记录均为无序序列;
  • 接着从第二个记录开始,按照记录的大小依次将当前处理的记录插入到其之前的有序序列中;
  • 直到最后一个记录插入到有序序列中为止;

性能::

  • 时间复杂度:最好时间:O(n);最坏:O(n2) 平均:O(n2)
  • 辅助空间:O(1)
  • 稳定性:稳定

代码实现:

/*** 插入排序:直接插入排序* @param array* @return*/
public static int[] insertSort(int[] array){//待插入的节点int insertNode = 0;//遍历索引int leftIndex = 0;//循环遍历,从数组中第二个节点开始,默认第一个节点是有序的即一个数字本身就是有序的for(int i = 1; i < array.length; i++){//循环设置插入值insertNode = array[i];//获取有序列表中的最右下标索引leftIndex = i - 1;/*** 从有序列表中的最右下标索引开始与插入值对比* 仅当下标索引不为负数才使用下标索引值对比插入值即索引仍在有序列表中* 索引值大于插入值,则说明继续往左边遍历*/while(leftIndex >= 0 && array[leftIndex] > insertNode){//移动有序列表中的值array[leftIndex+1] = array[leftIndex];//索引自减leftIndex--;}/*** 将插入值放入索引位置,因遍历初进行i-1,说明本身leftIndex是在i的左侧* 若while循环未进入,也能保证leftIndex+1 等于i,相当于自身重复赋值* 若满足while条件,则说明leftIndex又被减少过,即说明插入点索引也是需要加1*/array[leftIndex+1] = insertNode;}return array;
}

3.2 希尔排序

描述:
          希尔排序也叫“缩小增量排序”,是插入排序的改进版

图形效果:

在这里插入图片描述

设计思想:

  • 首先选择一个步长序列,t1=n/2, t2=t1/2, … ,tk=1;如n=10,则步长序列{5,2,1}
  • 然后按步长序列个数k,对待排序序列进行k趟排序;
  • 每趟排序,根据对应步长ti,将待排序列分成ti个子序列,分别对各个子序列进行直接插入排序。

性能::

  • 时间复杂度:最坏平均:O(n*log n)或 O(n^1.25)
  • 辅助空间:O(1)
  • 稳定性:不稳定

代码实现:

/*** 插入排序:希尔排序* @param a 排序的数组* @param n 排序的长度,用以确定步长序列{t1=n/2,t2=t1/2,...}*/
public static void shellSort(int[] array,int n){//获取步长for (int gap = n/2; gap > 0 ; gap = gap/2) {//对每一个步长,进行遍历,按步长对应的索引下标对比for (int i = gap; i < n; i++) {// 索引对应值对比if(array[i] < array[i-gap]){//存储步长目标值int temp = array[i];//获取步长的上一个索引下标int k = i - gap;/*** 循环对比步长索引值即关键遍历步长对比* (1)每次对比必须步长前索引值大于目标索引值才进行赋值* (2)每次进行赋值后,需要对步长对比索引向前推进步长单位,然后再次进行对比* (3)直到对比索引越界后结束或对比索引值与目标索引小,则结束while循环*/while (k >= 0 && array[k] > temp){//将目标值小于对比索引值的进行赋值即将小值放在前一个步长array[k+gap] = array[k];//继续按步长进行推进,再次对比,检查是否需要继续向前对比k -= gap;}//将步长目标值设置到原位置或上述while变更的位置array[k+gap] = temp;}}}
}

4、归并排序

描述:

          将待排序列使用递归进行折半分组,使用辅助数组按照从小到大进行两两合并;

图形效果:

在这里插入图片描述

设计思想:
          利用递归与分治技术将数据序列划分为越来越小的半子表,再对半子表排序,最后用递归将排好序的半子表合并为越来越大的有序序列。

  • (1) “归”–表示递归,即递归将数组折半地分为单个数组;
  • (2) “并”–表示合并,即将分开的数据按照从小到大或从大到小的顺序再放到一个数组中;
  • (3)对于给定一组记录,首先将每两个相邻的长度为1的子序列进行归并,得到【n/2】(向上取整)个长度为2或者1的有序子序列,再将其两两归并;反复执行此过程直到得到一个有序序列;

性能:

  • 时间复杂度:最好、最坏、平均时间:O(n*log n)
  • 辅助空间:O(n)
  • 稳定性:稳定

实现代码:

/*** 2.二路归并排序,递归实现* @param array 待排序数组* @param first 起点* @param last 终点* @param temp 辅助数组*/
public static void mergeSort(int[] array,int first,int last,int[] temp){//二路排序递归终止条件if(first >= last){return ;}//获取本次需要排序的起点和终点的中间点int mid = (first + last)/2;//开启中间点左边排序即一路mergeSort(array,first,mid,temp);//开启中间点右边排序即另一路mergeSort(array,mid+1,last,temp);//对上述两路排序进行合并排序mergeArray(array,first,mid,last,temp);
}/*** 1.将两个有序数组进行合并,a[first,mid] 和 a[mid+1,last]* @param array 待排序数组* @param first 起点* @param mid 中间点* @param last 终点* @param temp 辅助数组*/
public static void mergeArray(int[] array,int first,int mid,int last,int[] temp){//定义第一路即中间点左边数组起始点int i = first;//定义第二路即中间点右边数组起始点int j = mid + 1;//定义辅助数组起始点int k = 0;//循环遍历两路,将小值陆续放入辅助数组while (i <= mid && j <= last){//左路与右路遍历对比if(array[i] <= array[j]){//放入辅助数组temp[k++] = array[i++];}else {//放入辅助数组temp[k++] = array[j++];}}//检查上面while不满足条件时是否存在左路未遍历完while (i <= mid){temp[k++] = array[i++];}//检查上面while不满足条件时是否存在右路未遍历完while (j <= last){temp[k++] = array[j++];}for (int s = 0;s < k;s++) {array[first+s] = temp[s];}
}

相关内容

热门资讯

Linux学习之端口、网络协议... 端口:设备与外界通讯交流的出口 网络协议:   网络协议是指计算机通信网...
kuernetes 资源对象分... 文章目录1. pod 状态1.1 容器启动错误类型1.2 ImagePullBackOff 错误1....
STM32实战项目-数码管 程序实现功能: 1、上电后,数码管间隔50ms计数; 2、...
TM1638和TM1639差异... TM1638和TM1639差异说明 ✨本文不涉及具体的单片机代码驱动内容,值针对芯...
Qt+MySql开发笔记:Qt... 若该文为原创文章,转载请注明原文出处 本文章博客地址:https://h...
Java内存模型中的happe... 第29讲 | Java内存模型中的happen-before是什么? Java 语言...
《扬帆优配》算力概念股大爆发,... 3月22日,9股封单金额超亿元,工业富联、鸿博股份、鹏鼎控股分别为3.0...
CF1763D Valid B... CF1763D Valid Bitonic Permutations 题目大意 拱形排列࿰...
SQL语法 DDL、DML、D... 文章目录1 SQL通用语法2 SQL分类3 DDL 数据定义语言3.1 数据库操作3.2 表操作3....
文心一言 VS ChatGPT... 3月16号,百度正式发布了『文心一言』,这是国内公司第一次发布类Chat...
CentOS8提高篇5:磁盘分...        首先需要在虚拟机中模拟添加一块新的硬盘设备,然后进行分区、格式化、挂载等...
Linux防火墙——SNAT、... 目录 NAT 一、SNAT策略及作用 1、概述 SNAT应用环境 SNAT原理 SNAT转换前提条...
部署+使用集群的算力跑CPU密... 我先在开头做一个总结,表达我最终要做的事情和最终环境是如何的,然后我会一...
Uploadifive 批量文... Uploadifive 批量文件上传_uploadifive 多个上传按钮_asing1elife的...
C++入门语法基础 文章目录:1. 什么是C++2. 命名空间2.1 域的概念2.2 命名...
2023年全国DAMA-CDG... DAMA认证为数据管理专业人士提供职业目标晋升规划,彰显了职业发展里程碑及发展阶梯定义...
php实现助记词转TRX,ET... TRX助记词转地址网上都是Java,js或其他语言开发的示例,一个简单的...
【分割数据集操作集锦】毕设记录 1. 按要求将CSV文件转成json文件 有时候一些网络模型的源码会有data.json这样的文件里...
Postman接口测试之断言 如果你看文字部分还是不太理解的话,可以看看这个视频,详细介绍postma...
前端学习第三阶段-第4章 jQ... 4-1 jQuery介绍及常用API导读 01-jQuery入门导读 02-JavaScri...
4、linux初级——Linu... 目录 一、用CRT连接开发板 1、安装CRT调试工具 2、连接开发板 3、开机后ctrl+c...
Urban Radiance ... Urban Radiance Fields:城市辐射场 摘要:这项工作的目标是根据扫描...
天干地支(Java) 题目描述 古代中国使用天干地支来记录当前的年份。 天干一共有十个,分别为:...
SpringBoot雪花ID长... Long类型精度丢失 最近项目中使用雪花ID作为主键,雪花ID是19位Long类型数...
对JSP文件的理解 JSP是java程序。(JSP本质还是一个Servlet) JSP是&#...
【03173】2021年4月高... 一、单向填空题1、大量应用软件开发工具,开始于A、20世纪70年代B、20世纪 80年...
LeetCode5.最长回文子... 目录题目链接题目分析解题思路暴力中心向两边拓展搜索 题目链接 链接 题目分析 简单来说࿰...
unity的C#学习——浮点常... 浮点常量 在C#中,一个浮点常量是由整数部分、小数点、小数部分和指数部分组成。浮点常量...
Angular 开发NPM第三... 准备工作 首先已经安装过node以及angular以及注册过npm账号 新建项目 ng new ...
【Linux Manpage】... NAME libi2c - publicly accessible functions provid...