Java数据结构——八大排序

article/2025/8/13 13:47:24

排序

  • 插⼊排序
  • 希尔排序
  • 直接选择排序
  • 堆排序
  • 冒泡排序
  • 快速排序
  • 归并排序
  • 计数排序

排序的概念
排序:就是将一串东西,按照要求进行排序,按照递增或递减排序起来

稳定性:就是比如排序中有两个相同的数,如果排序后,这两个相同的数相对位置不变,这说明是稳定的,反之不稳定
在这里插入图片描述

插⼊排序

思想:就是将一个后面未排序的数,从后向前面有序的数进行扫描,找到对应为止插入,就像平时玩扑克牌一样
在这里插入图片描述

1.遍历整个数组,定义一个临时遍历tem存储当前要排序的值的值
2.当前元素与其前面元素进行比较
如果当前值大于tem就将这个值向后移动,反之就找到了退出,将该下标后面的值赋值为tem,array[ j + 1] = tem

public class Test {public static void main(String[] args) {int[] array = {3,1,2,3,4};insertSort(array);System.out.println(Arrays.toString(array));}//快速排序public static void insertSort(int[] array){for (int i = 1; i < array.length; i++) {int tem = array[i];int j = i-1;for (; j >=0 ; j--) {//如果存在下标j的值大于tem//就将这个值向后移动if(array[j]>tem){array[j+1] = array[j];}else {break;}}//最后将找到的j的后面那个值赋值给temarray[j+1] = tem;}}
}

运行结果如下
在这里插入图片描述

时间复杂度:O(N^2) ,因为这里最慢是1+2+3……+n,求和 n(n+1) / 2
空间复杂度:O(1),这里就多开辟了tem
稳定性:稳定,这里再遇到<=tem就会退出循环,所以说遇到相同的并不会改变位置
并且可以发现如果元素集合越接近有序,其方式更高效

希尔排序

希尔排序(Shell Sort)是插入排序的一种改进版本
思想:它通过将待排序的列表分成若干子列表,对每个子列表进行插入排序,逐步缩小子列表的间隔,最终完成整个序列的排序

1.先选择增量序列:选择gap,用于将其分为若干子序列,经常就是采用(n / 2 ,n/4……1)
2 .分组进行插入排序,逐渐的缩小增量,直到增量为1,在对整个序列进行一次插入排序,就完成排序了

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

public class Test {public static void main(String[] args) {int[] array = {3,1,2,3,4};shellSort(array);System.out.println(Arrays.toString(array));}public static void shellSort(int[] array){int gap = array.length;//先进行分组进行插入排序while(gap>1){gap = gap/2;//确定分几组shell(array,gap);}}public  static void shell(int[] array,int gap){for (int i = gap; i < array.length; i++) {//根据组进行排序int tem = array[i];int j = i-gap;for (; j >=0 ; j-=gap) {//如果存在下标j的值大于tem//就将这个值向后移动if(array[j]>tem){array[j+gap] = array[j];}else {break;}}array[j+gap] = tem;}}
}

运行结果如下
在这里插入图片描述

希尔排序是直接插入排序的一种优化
稳定性:不稳定
时间复杂度:O(N) ~ O(N ^ 2)
空间复杂度:O(1)

直接选择排序

思想:每次从待排序数据元素中找到最小或最大的一个元素,放在待排序的起始位置,直到全部都排完
实现:遍历整个待排序列,记录当前下标i,再遍历其后面的元素,判断是否有比它小的,如果有记录当前下标,然后进行交换
在这里插入图片描述

public class Test {public static void main(String[] args) {int[] array = {3,1,2,3,4};selectSort(array);System.out.println(Arrays.toString(array));}//选择排序public static void selectSort(int[] array){for (int i = 0; i < array.length; i++) {int minIndex = i;for (int j = i+1; j <array.length ; j++) {if(array[j]<array[minIndex]){//记录最小元素下标minIndex=j;}}//进行交换swap(array,i,minIndex);}}public static void swap(int[] array,int i,int j){int tem = array[i];array[i] = array[j];array[j] = tem;}
}

在这里插入图片描述

时间复杂度:O(N^2)
空间复杂度:O(1)
稳定性:不稳定

堆排序

思想:就是利用堆这种数据结构进行排序
大根堆:用于排升序序列
小根堆:用于排降序序列
在这里插入图片描述

思路:以排升序为例
1.先将其数组创建为大根堆
2.定义一个end表示最后一个元素下标,每次堆顶元素都是最大的,将堆顶元素和堆底元素交换,将end–,相当于将堆底元素删除,这时就还要重新向下调整为大根堆
3.直到end为0的时候截止

public class Test {public static void main(String[] args) {int[] array = {3,1,2,3,4};heapSort(array);System.out.println(Arrays.toString(array));}//堆排序//从小到大,就使用大堆,每次把最后一个元素确定public static void heapSort(int[] array){//创建大根堆creatHeap(array);//每次将最后一个与第一个交换int end = array.length-1;while(end>0){swap(array,0,end);siftDown(array,0,end);//去掉最后一个从新排序end--;}}//建立大根堆堆private static void creatHeap(int[] array) {//从最下面的父亲节点开始调整for (int parent = (array.length-1-1)/2; parent >=0 ; parent--) {siftDown(array,parent,array.length);}}//向下调整private static void siftDown(int[] array, int parent, int length) {int child = 2*parent+1;while (child<length){if(child+1<length&&array[child]<array[child+1]){child++;}if(array[child]>array[parent]){swap(array,child,parent);parent = child;child = 2*parent+1;}else{break;}}}public static void swap(int[] array,int i,int j){int tem = array[i];array[i] = array[j];array[j] = tem;}
}

在这里插入图片描述

时间复杂度:O(N*logN),调整堆O(logN),需要遍历整个数组,每次可能都要调整堆
空间复杂度:O(1)
稳定性:不稳定

冒泡排序

冒泡排序(Bubble Sort)是一种简单的排序算法,它通过重复地遍历待排序的列表,比较相邻的元素并交换它们的位置来实现排序。每遍历一次就确定一个最后一个元素
在这里插入图片描述

public class Test {public static void main(String[] args) {int[] array = {3,1,2,3,4};bubberSort(array);System.out.println(Arrays.toString(array));}public static void bubberSort(int[] array){//外层确定比较几趟/for (int i = 0; i < array.length-1; i++) {//内层确定每趟比较次数for (int j = 0; j < array.length-i-1; j++) {if(array[j]>array[j+1]){int tem = array[j];array[j] = array[j+1];array[j+1] = tem;}}}}
}

快速排序

快速排序(Quick Sort)是一种高效的排序算法,使用的是分治法的思想
就是找到一个基准值,将列表分为两部分,左边一部分是小于基准值,右边一部分是大于基准值,分别在此基准值的左边和右边,重复同样的操作
因此这里可以是使用递归来写的

1.通常以第一个为基准值,然后进行调整左右
2.递归其这个基准值下标左边 和 右边 ,直到左边下标>=右边下标就结束递归
3.这里找到基准值使用Hoare方法来来进行调整,就是high下标先从右向左找到比基准值小的值,low下标从左向右找到比基准值大的值,进行交换,low >= high ,就说明结束了,将此时的low下标与基准值进行交换

public class Test {public static void main(String[] args) {int[] array = {3,1,2,11,4};QuickSort(array);System.out.println(Arrays.toString(array));}//快速排序public static void QuickSort(int[] array){Quick(array, 0, array.length-1);}public static void Quick(int[] array,int left,int right){//截止条件if(left>=right){return;}//递归,先将其以key为界限分为两组//key左右两边又可以分组int key = Hoare(array,left,right);Quick(array,left,key-1);//递归左边Quick(array,key+1,right);//递归右边}//调整基准值位置,并返回其下标private static int Hoare(int[] array, int low, int high) {int i = low;int tem = array[low];while (low<high){//1.后面找到比前面基准值小的while (low<high&&array[high]>=tem){high--;}//2.从前面找比基准值大的while (low<high&&array[low]<=tem){low++;}//2.交换swap(array,low,high);}//与基准值进行交换swap(array,i,low);return low;}public static void swap(int[] array,int i,int j){int tem = array[i];array[i] = array[j];array[j] = tem;}
}

上面是使用的是Hoare方法来调整其列表
在这里插入图片描述
当然这里也可以使用挖坑法
挖坑法:就是先定义一个临时变量来存放我们的基准值
1.先从后向前找high下标一个小于临时变量的值,将这个值放入放入low下标
2.从前向后找low下标一个大于临时变量的值,将这个值放入high下标地方
重复操作,直到low>=high就结束,最后将low下标的值修改为tem基准值

这里调整基准值方法不仅可以使用Hoare方法,这里也可以使用挖坑法
1.先将基准值拿出来
2.先从后向左找一个小于基准值的值放入坑中,此时该位置就便相当于为坑
3.在从左向右找一个大于基准值的值放入坑中, 此时该位置就便相当于为坑

在这里插入图片描述

//挖坑法private static int parttion(int[] array,int low,int high){int tem =array[low];while (low<high){while (low<high&&array[high]>=tem){high--;}array[low] = array[high];while (low<high&&array[low]<=tem){low++;}array[high] = array[low];}array[low] = tem;return low;}

快速排序优化:三数取中
比如序列:1 2 3 4进行快速排序,这样会使其时间复杂度为N^2,因为其快速排序像构建二叉树一样,这样会浪费时间,因此可以使用一个方法
将low 、mid 和 high下标的值其中最中间的值作为基准值

在这里插入图片描述
上面找基准值就是找其第一个元素,但有时候第一个元素并不是最好的,所以可以找第一个、中间、最后一个其中中间的值,作为基准值这样更合理

//这里利用三数取中,获取其三个钟最中间元素的下标private static int mid(int[] array, int low, int high) {int mid = (low+high)/2;if(array[low]<array[high]){if(array[mid]<array[low]){return low;}else if(array[mid]>array[high]){return high;}else {return mid;}}else{if(array[mid]<array[high]){return high;}else if(array[mid]>array[low]){return low;}else {return mid;}}}

时间复杂度:O(N * log N) ~ O (N ^2),因为每次进行基准值调整就像在构建一颗完全二叉树,构建数的复杂度为log N
这里又要重复N次 ,但是如果其数列有序的话,时间复杂度可能为O (N ^2)
空间复杂度:O(log N),因为底层就像一颗二叉树
稳定性:不稳定

快速排序非递归形式
这里采用非递归,但是还是要使用挖坑法或者Hoare 方法进行基准值调整
这里是使用stack进行操作,这里如果符合条件就将其下标入栈,缩小其基准值调整范围,一部分一部分调整,不断的将下标入栈和出栈操作,当栈为空的时候就结束了

在这里插入图片描述

在这里插入图片描述

public class Test {public static void main(String[] args) {int[] array = {3,1,2,11,99,33,22,11,4,7,8};quickSortNor(array);System.out.println(Arrays.toString(array));}//快速排序非递归public static void quickSortNor(int[] array) {int start = 0;int end = array.length-1;int par = parttion(array,0,end);Stack<Integer> stack = new Stack<>();if(par>start+1){stack.push(start);stack.push(par-1);}if(par<end-1){stack.push(par+1);stack.push(end);}while (!stack.isEmpty()){end = stack.pop();start = stack.pop();par = parttion(array,start,end);if(par>start+1){stack.push(start);stack.push(par-1);}if(par<end-1){stack.push(par+1);stack.push(end);}}}private static int parttion(int[] array,int low,int high){int tem =array[low];while (low<high){while (low<high&&array[high]>=tem){high--;}array[low] = array[high];while (low<high&&array[low]<=tem){low++;}array[high] = array[low];}array[low] = tem;return low;}
}

运行结果如下
在这里插入图片描述

归并排序

归并排序(Merge sort)就是采用分治法将其分为子序列,先将子序列变为有序,在将子序列归并进行排序,最终整体就有序了

在这里插入图片描述
就是分解合并两个操作
先将其分解到不能分解,合并过程中并且注意合并成是有序的数列,就这样一直和并子序列,最后整体的序列就有序了
在这里插入图片描述

public class Test {public static void main(String[] args) {int[] array = {3,1,2,11,4,7,8};mergeSort(array);System.out.println(Arrays.toString(array));}//归并排序public static void mergeSort(int[] array){mergeSortChild(array,0,array.length-1);}//使用递归实现private static void mergeSortChild(int[] array, int left, int right) {if(left>=right){return;}//每次将其分为两部分进行排序int mid = (left+right)/2;//递归左边mergeSortChild(array,left,mid);//递归右边mergeSortChild(array,mid+1,right);//合并merge(array,left,mid,right);}//合并private static void merge(int[] array,int left,int mid,int right){int tem[] = new int[right-left+1];int k = 0;int s1 = left;int e1 = mid;int s2 = mid+1;int e2 = right;//将这两个合并成一个有序数组while (s1<=e1&&s2<=e2){if(array[s1]<=array[s2]){tem[k++] = array[s1++];}else {tem[k++] = array[s2++];}}//最后将另一个没有放进去的放进去while (s1<=e1){tem[k++] = array[s1++];}while (s2<=e2){tem[k++] = array[s2++];}//最后将这个合并好的放入array数组中for (int i = 0;i<tem.length;i++){array[i+left] = tem[i];}}
}

运行结果如下
在这里插入图片描述

时间复杂度:O(N*log N),和快速排序一样
空间复杂度:O(N)
稳定性能:稳定

计数排序

上面的排序都是不断的进行比较移动进行排序,而计数排序是非比较型
1.他就是通过数组下标来存放对应的值,如果这个值和某个下标相同就将该下标的对应的值++,相当于用一个count数组来记录每个数出现的次数放在对应下标上
2. 全部放完以后,循环这个count数组,如果对应count[i] ! = 0 ,说明此下标存放值了,就将下标放入array数组中

注意这里再对应下标存放的时候,可能出现92  -  99这样范围的序列
因此这里再存放的时候下标可以减去最前面的值,下标-92,将这个作为下标
最后取出放入array数组的时候,要加上92

)

public class Test {public static void main(String[] args) {int[] array = {3,1,2,11,4,7,8};countSort(array);System.out.println(Arrays.toString(array));}//计算排序public static void countSort(int[] array){int min = array[0];int max = array[0];//1.获取其最大值和最小值for (int i = 1; i < array.length; i++) {if(array[i]>max){max = array[i];}if(array[i]<min){min = array[i];}}//确定数组长度//在对应下标存放于下标相同的值int range = max - min + 1;int[] count = new int[range];for (int i = 0; i < array.length; i++) {//将array[i]对应的值为count的下标,遇到就++int index = array[i];//这里之所以要减去min,是因为这里可能出现越界问题//如果是92 - 99的值,但是下标并不是这样的,减去最前面的值count[index-min]++;}int k = 0;for (int i = 0; i < count.length; i++) {while (count[i]!=0){//由于前面下标减去一个min,这里要加回来array[k] = i+min;count[i]--;k++;}}}
}

时间复杂度:O(n + k),n是列表长度,k是数据范围
空间复杂度:O(n + k) ,需要额外的计数数组和结果数组

排序方式最好最坏空间复杂度稳定性
冒泡排序O(N^2)O(N^2)O(1)稳定
插入排序O(N)O(N^2)O(1)稳定
选择排序O(N^2)O(N^2)O(1)不稳定
希尔排序O(N)O(N^2)O(1)不稳定
堆排序O(N*logN)O(N*logN)O(1)不稳定
快速排序O(N*logN)O(N^2)O(logN~N)不稳定
归并排序O(N*logN)O(N*logN)O(N)稳定

http://www.hkcw.cn/article/mNyLyAcSlR.shtml

相关文章

【Linux】Linux文件系统详解

目录 Linux系统简介 Linux常见发行版&#xff1a; Linux/windows文件系统区别 Linux文件系统各个目录用途 Linux系统核心文件 系统核心配置文件 用户与环境配置文件 系统运行与日志文件 Linux文件名颜色含义 Linux文件关键信息解析 &#x1f525;个人主页 &#x1f52…

2023年6月6级第一套第一篇

虽然&#xff0c;不重要题干定位到主句信息了&#xff0c;往下走&#xff0c;看强调什么信息看最后一句&#xff0c;优先看主干信息&#xff0c;先找谓语然后找主语和宾语&#xff0c;也是和人有关&#xff0c;后面出现的名词信息是修饰部分&#xff0c;非主干信息不看 A选项&…

Langchaine4j 流式输出 (6)

Langchaine4j 流式输出 大模型的流式输出是指大模型在生成文本或其他类型的数据时&#xff0c;不是等到整个生成过程完成后再一次性 返回所有内容&#xff0c;而是生成一部分就立即发送一部分给用户或下游系统&#xff0c;以逐步、逐块的方式返回结果。 这样&#xff0c;用户…

代谢组数据分析(二十六):LC-MS/MS代谢组学和脂质组学数据的分析流程

禁止商业或二改转载,仅供自学使用,侵权必究,如需截取部分内容请后台联系作者! 文章目录 介绍加载R包依赖包安装包加载需要的R包数据下载以及转换mzML数据预处理代谢物注释LipidFinder过滤MultiABLER数据预处理过滤补缺失值对数变换数据标准化下游数据分析总结系统信息参考介…

常量指真,指针常量 ,

const int*p&#xff1b;//const int 值不能变 指向可以变 int *const p&#xff1b;//const p 指向不可以变 值能变

智能指针unique

什么是智能指针&#xff1a; 就像是一个自动管家 帮你管理内存 自动清理不需要的内存 防止内存泄漏 unique_ptr 的特点&#xff1a; 独占所有权&#xff1a;一个资源只能被一个 unique_ptr 管理 不能复制&#xff1a;只能移动 自动释放&#xff1a;当 unique_ptr 被销毁…

并发执行问题 下

这段例子 是让S3 在S2后面运行 写完数据 通知后 另一个进程 竞争使用资源 独占资源 shell解释器 科学语言才有并发语句语言 C语言没有 使用多线程和多进程实现并发运行

[JS逆向] 福建电子交易平台

博客配套代码发布于github&#xff1a;福建电子交易平台 相关知识点&#xff1a;[爬虫知识] 密码学&#xff1a;通往JS逆向路上必会的一环 相关爬虫专栏&#xff1a;JS逆向爬虫实战 爬虫知识点合集 爬虫实战案例 此案例目标为对福建省电子公共服务平台逆向&#xff0c;并爬…

Mask_RCNN 环境配置及训练

目录 一、Mask_RCNN代码及权重 1、源码下载 2、权重获取 二、环境配置 1、创建虚拟环境 2、安装必要的包 三、测试环境 1、使用coco 2、使用balloon 四、测试 1、使用coco 2、使用balloon 一、Mask_RCNN代码及权重 均从github获取&#xff0c;以下是相关链接&#…

72.编辑用户消息功能之前端实现

大体设想 我想实现的一个功能是在用户发出的消息下面有一个图标是编辑&#xff0c;按下那个图标之后&#xff0c;用户可以修改对应的那个消息&#xff0c;修改完成点击确认之后&#xff0c;用户下面对用的那个AI的回答可以重新生成 之前已经介绍了后端实现&#xff0c;这篇博…

第303个Vulnhub靶场演练攻略:Thales1

Thales1 Vulnhub 演练 “Thales”是 Vulnhub 上的夺旗挑战赛。MachineBoy 开发了这款机器&#xff0c;功不可没。https://www.vulnhub.com/entry/thales-1,749/在本教程中&#xff0c;我们将学习如何利用 Tomcat 应用程序管理器实例中的漏洞获取系统访问权限&#xff0c;以及如…

vscode + cmake + ninja+ gcc 搭建MCU开发环境

vscode cmake ninja gcc 搭建MCU开发环境 文章目录 vscode cmake ninja gcc 搭建MCU开发环境1. 前言2. 工具安装及介绍2.1 gcc2.1.1 gcc 介绍2.1.2 gcc 下载及安装 2.2 ninja2.2.1 ninja 介绍2.2 ninja 安装 2.3 cmake2.3.1 cmake 介绍2.3.2 cmake 安装 2.4 VScode 3. 上手…

GNSS终端授时之四:高精度的PTP授时

我们在GNSS终端的授时之三&#xff1a;NTP网络授时中介绍了NTP网络授时的基本原理。我们知道了NTP授时的精度跟网络环境相关&#xff0c;即使在局域网中NTP授时的精度也只能到ms级别。如果广域网&#xff0c;经过多级交换机&#xff0c;路由器&#xff0c;由于传输路径和延时的…

Amazon Augmented AI:人类智慧与AI协作,破解机器学习审核难题

在人工智能日益渗透业务核心的今天&#xff0c;你是否遭遇过这样的困境&#xff1a;自动化AI处理海量数据时&#xff0c;面对模糊、复杂或高风险的场景频频“卡壳”&#xff1f;人工审核团队则被低效、重复的任务压得喘不过气&#xff1f;Amazon Augmented AI (A2I) 的诞生&…

OS10.【Linux】yum命令

目录 1.安装软件的几种方法 直接编译源代码,得到可执行程序 使用软件包管理器 2.yum yum list命令 参数解释 yum install命令 yum remove命令 下载链接存放的位置 扩展yum源 实验:安装sl小火车命令 sl命令的选项 方法1:man sl 方法2:读源代码 3.更新yum源 查看…

网络协议的原理及应用层

网络协议 网络协议目的为了减少通信成本&#xff0c;所有的网络问题都是传输距离变长的问题。 协议的概念&#xff1a;用计算机语言来发出不同的信号&#xff0c;信号代表不同的含义&#xff0c;这就是通信双方的共识&#xff0c;便就是协议。 协议分层&#xff08;语言层和…

【计算机网络】第3章:传输层—可靠数据传输的原理

目录 一、PPT 二、总结 &#xff08;一&#xff09;可靠数据传输原理 关键机制 1. 序号机制 (Sequence Numbers) 2. 确认机制 (Acknowledgements - ACKs) 3. 重传机制 (Retransmission) 4. 校验和 (Checksum) 5. 流量控制 (Flow Control) 协议实现的核心&#xff1a;滑…

RV1126-OPENCV 图像叠加

一.功能介绍 图像叠加&#xff1a;就是在一张图片上放上自己想要的图片&#xff0c;如LOGO&#xff0c;时间等。有点像之前提到的OSD原理一样。例如&#xff1a;下图一张图片&#xff0c;在左上角增加其他图片。 二.OPENCV中图像叠加常用的API 1. copyTo方法进行图像叠加 原理…

Java流【全】

IO流分类 AA、根据数据流动的方向:输入流和输出流 如:打开一个新的记事本并输入一些内容,而这些内容是在内存里面的,没有存储到磁盘中,当点击保存之后,数据才会从内存流向磁盘;当双击打开磁盘文件的时候,数据才会从磁盘流向内存【数据存储有一个特点:内存一旦断电数…

大模型登《情报学报》!大模型驱动的学术文本挖掘!

武汉大学信息管理学院、武汉大学信息检索与知识挖掘研究所的陆伟、刘寅鹏、石湘、刘家伟、程齐凯、黄永和汪磊共同研究的《大模型驱动的学术文本挖掘——推理端指令策略构建及能力评测》在《情报学报》中发表。论文以学术文本挖掘任务为切入点&#xff0c;构建涵盖文本分类、信…