通过Java实现插入排序(直接插入,希尔)与选择排序(直接选择,堆排)
目录
(一)插入排序
1.直接插入排序
(1)核心思想:
(2)代码实现(以从小到大排序为例):
(3)代码分析:
2.希尔排序(缩小增量排序)
(1)核心思想:
(2)代码实现(以从小到大排序为例):
(3)代码分析:
(二)选择排序
1.直接选择排序
(1)核心思想:
(2)代码实现(以从小到大排序为例):
(3)代码分析:
2.堆排序
(1)核心思想:
(2)代码实现(以从小到大排序为例):
(3)代码分析:
(三)示例以及各算法耗时参考
1.代码结构
2.程序源码
(1)Sort类:
(2)Test类:
3.测试结果
编辑
(一)插入排序
1.直接插入排序
(1)核心思想:
直接插入排序,顾名思义,即将非有序部分的元素按大小规律逐个插入到有序的部分中,最终使得整体有序。举个简单的例子:当我们在玩扑克牌逐个摸牌时,就会将新牌插入到原来有序的手牌中,此时其实就用到了插入排序的思想。
(2)代码实现(以从小到大排序为例):
public static void insertSort(int[] array){//遍历非有序部分数组for(int i=1;i<array.length;i++){//取出非有序部分的第一个元素并向前逐个比对int tmp=array[i];int j=i-1;for(;j>=0;j--){if(tmp<array[j]){//若该元素比前面某元素小,则某元素后移,该元素继续向前比对array[j+1]=array[j];}else{//相反若该元素比前面某元素大,则退出循环break;}//内层循环结束说明已经为该元素找到合适位置,直接插入即可array[j+1]=tmp;}}}
(3)代码分析:
1)时间复杂度:最好情况下(数组本身有序):O(N);最坏情况下(数组本身逆序):O(N^2)。
2)空间复杂度:O(1)(即并未申请额外内存)。
3)稳定性:稳定。
由上可知对于越有序的数组,使用直接插入排序更有优势。
2.希尔排序(缩小增量排序)
(1)核心思想:
希尔排序,又称为缩小增量排序,其本质为直接插入排序的优化形式,在直接插入排序的基础上采取了分治(即分而治之,分组考虑)的思想:通过设定元素下标间隔增量gap来将一组元素分为多组,分别进行直接插入排序,将每个组排完序后将gap减小重复上述过程,直到gap为1,上述全过程整租元素都在不断趋于有序,最终实现排序效果。
例如:数组元素为10时且设定第一次gap为5的情况:
(2)代码实现(以从小到大排序为例):
//实现希尔排序方法public static void shellSort(int[] array){//设定间隔增量gapint gap=array.length;while (gap > 1) {//每次循环缩小间隔增量gap/=2;//以间隔增量对数组进行分组插入排序shellSortChild(array,gap);}}//实现希尔排序的底层方法private static void shellSortChild(int[]array,int gap){//遍历非有序部分数组,i++表示对每组进行交替排序for(int i=gap;i<array.length;i++){//取出非有序部分的第一个元素并向前逐个比对int tmp=array[i];int j=i-gap;for(;j>=0;j-=gap){if(tmp<array[j]){//若该元素比前面某元素小,则某元素后移,该元素继续向前比对array[j+gap]=array[j];}else{//相反若该元素比前面某元素大,则退出循环break;}//内层循环结束说明已经为该元素找到合适位置,直接插入即可array[j+gap]=tmp;}}}
(3)代码分析:
1)时间复杂度:目前无法严格证明,原因是该算法根据gap的取法不同而不同(本题中gap取法为二分法,即不断除以二),并且当gap较大时每组遍历次数较少,gap较小时整体又更有序,无法进行严格计算,但有学者通过大量实验证明希尔排序的时间复杂度应该介于N^1.25~1.6N^1.25之间,可以估计为O(N^1.3)。
2)空间复杂度:O(1)(即并未申请额外内存)。
3)稳定性:不稳定。
联系直接插入排序可知,希尔排序可以克服传统直接插入排序在完全逆序情况下时间复杂度过高的劣势。
(二)选择排序
1.直接选择排序
(1)核心思想:
直接选择排序,顾名思义,即每一次从非有序部分的元素中选出最小(或最大)的一个元素,存放在非有序部分的起始位置,直到全部非有序部分元素全部排完,此时整组元素有序。
(2)代码实现(以从小到大排序为例):
//实现直接选择排序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[i]){minIndex=j;}}//循环结束证明已经找到最小值下标,与非有序部分起始位置交换int tmp=array[minIndex];array[minIndex]=array[i];array[i]=tmp;}}
(3)代码分析:
1)时间复杂度:无论何时均为O(N^2)。
2)空间复杂度:O(1)(即并未申请额外内存)。
3)稳定性:不稳定。
2.堆排序
(1)核心思想:
利用堆的优先级特性,升序排列建大堆,降序排列建小堆,每次将堆顶元素和未排序堆尾元素互换后进行向上调整(这样堆尾元素一定是当前堆的最值),最终整个堆有序。(思路类似于直接选择排序或者冒泡排序,即每次都将未排序的部分中的最值放于末尾,如此最终整个数组有序)。
(2)代码实现(以从小到大排序为例):
//实现堆排序//创建一个大根堆的方法public static void createMaxHeap(int[] array){//从最后一棵子树倒序调整for(int parent=((array.length-1-1)/2);parent>=0;parent--){//调用向下调整的底层方法maxSiftDown(array,parent,array.length-1);}}//创建大根堆时调用到的向下调整的底层方法private static void maxSiftDown(int[]array,int parent,int end){//默认子女中的最大值为左子女int child=2*parent+1;while(child<end){//判断右子女是否为二者中最大值if(child+1<end){if(array[child]<array[child+1]){child++;}}if(array[parent]<array[child]){//子女节点中最大值大于双亲则进行交换调整int temp=array[parent];array[parent]=array[child];array[child]=temp;//向下迭代parent=child;child=2*parent+1;}else{//子女节点中最大值小于双亲说明该树已经为大根堆,无需向下调整,直接中断即可break;}}}//利用创建的大根堆实现堆排序public static void heapSort(int[]array){createMaxHeap(array);int end= array.length-1;while(end>0){//将堆顶元素和堆尾元素交换int temp=array[end];array[end]=array[0];array[0]=temp;//利用大根堆向下调整的方法maxSiftDown(array,0,end);end--;}}
(注:堆的创建,向上调整部分具体思路及讲解可见本人博客:通过Java模拟实现堆(大根堆与小根堆)及其相关操作http://t.csdnimg.cn/JZlWL )
(3)代码分析:
1)时间复杂度:O(N^log N)。
2)空间复杂度:O(1)(即并未申请额外内存,注意建堆也是在原数组上进行操作的)。
3)稳定性:不稳定。
(三)示例以及各算法耗时参考
1.代码结构
1. Sort类:内部实现直接选择排序,希尔排序,直接插入排序,堆排序相关方法(即上文实现的四种算法)
2.Test类:实现创建顺序数组,逆序数组,随机数组的方法(用来测试四种算法)以及测量四种算法耗时的方法,并在main方法中进行示例演示。
2.程序源码
(1)Sort类:
public class Sort {//实现直接插入排序方法public static void insertSort(int[] array){//遍历非有序部分数组for(int i=1;i<array.length;i++){//取出非有序部分的第一个元素并向前逐个比对int tmp=array[i];int j=i-1;for(;j>=0;j--){if(tmp<array[j]){//若该元素比前面某元素小,则某元素后移,该元素继续向前比对array[j+1]=array[j];}else{//相反若该元素比前面某元素大,则退出循环break;}//内层循环结束说明已经为该元素找到合适位置,直接插入即可array[j+1]=tmp;}}}//实现希尔排序方法public static void shellSort(int[] array){//设定间隔增量gapint gap=array.length;while (gap > 1) {//每次循环缩小间隔增量gap/=2;//以间隔增量对数组进行分组插入排序shellSortChild(array,gap);}}//实现希尔排序的底层方法private static void shellSortChild(int[]array,int gap){//遍历非有序部分数组,i++表示对每组进行交替排序for(int i=gap;i<array.length;i++){//取出非有序部分的第一个元素并向前逐个比对int tmp=array[i];int j=i-gap;for(;j>=0;j-=gap){if(tmp<array[j]){//若该元素比前面某元素小,则某元素后移,该元素继续向前比对array[j+gap]=array[j];}else{//相反若该元素比前面某元素大,则退出循环break;}//内层循环结束说明已经为该元素找到合适位置,直接插入即可array[j+gap]=tmp;}}}//实现直接选择排序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[i]){minIndex=j;}}//循环结束证明已经找到最小值下标,与非有序部分起始位置交换int tmp=array[minIndex];array[minIndex]=array[i];array[i]=tmp;}}//实现堆排序//创建一个大根堆的方法public static void createMaxHeap(int[] array){//从最后一棵子树倒序调整for(int parent=((array.length-1-1)/2);parent>=0;parent--){//调用向下调整的底层方法maxSiftDown(array,parent,array.length-1);}}//创建大根堆时调用到的向下调整的底层方法private static void maxSiftDown(int[]array,int parent,int end){//默认子女中的最大值为左子女int child=2*parent+1;while(child<end){//判断右子女是否为二者中最大值if(child+1<end){if(array[child]<array[child+1]){child++;}}if(array[parent]<array[child]){//子女节点中最大值大于双亲则进行交换调整int temp=array[parent];array[parent]=array[child];array[child]=temp;//向下迭代parent=child;child=2*parent+1;}else{//子女节点中最大值小于双亲说明该树已经为大根堆,无需向下调整,直接中断即可break;}}}//利用创建的大根堆实现堆排序public static void heapSort(int[]array){createMaxHeap(array);int end= array.length-1;while(end>0){//将堆顶元素和堆尾元素交换int temp=array[end];array[end]=array[0];array[0]=temp;//利用大根堆向下调整的方法maxSiftDown(array,0,end);end--;}}
}
(2)Test类:
import java.util.Arrays;
import java.util.Random;public class Test {//生成一个顺序数组的方法public static void order(int[] array){for(int i=0;i< array.length;i++){array[i]=i;}}//生成一个逆序数组的方法public static void reverseOrder(int[] array){for(int i=0;i<array.length;i++){array[i]= array.length-i;}}//生成一个随机数数组的方法public static void randomOrder(int[] array){Random random=new Random();for(int i=0;i<array.length;i++){array[i]= random.nextInt(10_0000);}}//测试直接插入排序时间的方法public static void testInsertSort(int[]array){//拷贝一个新的数组array= Arrays.copyOf(array,array.length);//获取起始时间戳long starttime=System.currentTimeMillis();Sort.insertSort(array);//获取终止时间戳long endtime=System.currentTimeMillis();//输出耗时System.out.println("直接插入排序耗时:"+(endtime-starttime));}//测试希尔排序时间的方法public static void testShellSort(int[]array){//拷贝一个新的数组array= Arrays.copyOf(array,array.length);//获取起始时间戳long starttime=System.currentTimeMillis();Sort.shellSort(array);//获取终止时间戳long endtime=System.currentTimeMillis();//输出耗时System.out.println("希尔排序耗时:"+(endtime-starttime));}//测试直接选择排序时间的方法public static void testSelectSort(int[]array){//拷贝一个新的数组array= Arrays.copyOf(array,array.length);//获取起始时间戳long starttime=System.currentTimeMillis();Sort.selectSort(array);//获取终止时间戳long endtime=System.currentTimeMillis();//输出耗时System.out.println("直接选择排序耗时:"+(endtime-starttime));}//测试直接选择排序时间的方法public static void testHeapSort(int[]array){//拷贝一个新的数组array= Arrays.copyOf(array,array.length);//获取起始时间戳long starttime=System.currentTimeMillis();Sort.heapSort(array);//获取终止时间戳long endtime=System.currentTimeMillis();//输出耗时System.out.println("堆排序耗时:"+(endtime-starttime));}public static void main(String[] args) {int[]array=new int[10_0000];//测试顺序数组情况System.out.println("***********************");System.out.println("顺序数组情况:");order(array);testInsertSort(array);testShellSort(array);testSelectSort(array);testHeapSort(array);System.out.println("***********************");//测试逆序数组情况System.out.println("逆序数组情况:");reverseOrder(array);testInsertSort(array);testShellSort(array);testSelectSort(array);testHeapSort(array);System.out.println("***********************");//测试随机数组情况System.out.println("随机数组情况:");randomOrder(array);testInsertSort(array);testShellSort(array);testSelectSort(array);testHeapSort(array);System.out.println("***********************");}
}
3.测试结果
上图可以直观感受各算法在不同情况下的表现。
以上便是通过Java实现插入排序(直接插入,希尔)与选择排序(直接选择,堆排)的全部内容,如有不当,敬请斧正!
相关文章:

通过Java实现插入排序(直接插入,希尔)与选择排序(直接选择,堆排)
目录 (一)插入排序 1.直接插入排序 (1)核心思想: (2)代码实现(以从小到大排序为例): (3)代码分析: 2.希尔排序(…...

大型分布式B2B2C多用户商城7.0企业版源码分享【java语言、方便二次开发】
项目介绍 项目基于SpringBoot开发,运营端和商户端采用ElementVue,买家使用采用VueIviewnuxt服务端渲染。使用到的中间件有Redis、RabbitMQ、ElasticSearch、FastDFS、Mongodb等。主要功能包括有运营管理、商品管理、订单管理、售后管理、会员管理、财务…...
C++的结构体、联合体、枚举类型(一)
1.C++的结构体 2.C++的联合体 3.C++的枚举类型 1.C++的结构体 (1)C++中定义结构体变量,可以省略struct关键字 struct XX{…}; XX x;//定义结构体变量直接省略struct(2)C++结构体中可以直接定义函数,谓之成员函数(又叫方法)(3)在成员函数中可以直接访问该结构体的成员变…...

搭建高可用OpenStack(Queen版)集群(一)之架构环境准备
一、搭建高可用OpenStack(Queen版)集群之架构环境准备 一、架构设计 二、初始化基础环境 1、管理节点创建密钥对(方便传输数据) 所有控制节点操作 # ssh-keygen #一路回车即可 Generating public/private rsa key pair. Enter f…...

通过Stack Overflow线程栈溢出的问题实例,详解C++程序线程栈溢出的诸多细节
目录 1、问题说明 2、从Visual Studio输出窗口中找到了线索,发生了Stack Overflow线程栈溢出的异常 3、发生Stack Overflow线程栈溢出的原因分析 4、线程占用的栈空间大小说明 5、引发线程栈溢出的常见原因和场景总结 6、在问题函数入口处添加return语句&…...

LeetCode刷题笔记 | 3 | 无重复字符的最长子串 | 双指针 | 滑动窗口 | 2025兴业银行秋招笔试题 | 哈希集合
🙋大家好!我是毛毛张! 🌈个人首页: 神马都会亿点点的毛毛张 这是一道银行的面试题,就是简单?! LeetCode链接:3. 无重复字符的最长子串 1.题目描述 给定一个字符串 s ,…...
验证cuda和pytorch都按照成功了
要验证您的PyTorch是否能够调用CUDA,您可以执行以下步骤: 1. **检查CUDA是否可用**: 在Python中运行以下代码来检查CUDA是否可用: python import torch print(torch.cuda.is_available()) 如果输出为 True&…...
iOS开发如何自己捕获Crash
为了在iOS中捕获和处理未捕获的Objective-C异常和系统信号引起的崩溃,可以使用NSSetUncaughtExceptionHandler和标准的Unix信号处理机制来实现。这能帮助你记录绝大部分的崩溃信息。以下是详细的实现步骤和代码示例: 一、系统崩溃处理 通过NSSetUncaug…...
雪花算法(Snowflake Algorithm)
雪花算法(Snowflake Algorithm)是一种分布式唯一ID生成算法,主要用于生成全球唯一的ID,广泛应用于分布式系统中,例如在数据库中作为主键。这个算法最初由Twitter提出,并且被广泛使用在很多大规模系统中。有…...

〖任务1〗ROS2 jazzy Linux Mint 22 安装教程
前言: 本教程在Linux系统上使用。 目录 一、linux安装二、linux VPN安装三、linux anaconda安装(可选)四、linux ROS2 安装五、rosdep init/update 解决方法六、安装GUI 一、linux安装 移动硬盘安装linux:[LinuxToGo教程]把ubunt…...
图像增强:使用周围像素填充掩码区域
制作图像需要填充的掩码区域,对需要填充的位置的mask赋值非0,不需要填充赋值为0使用cv2.inpaint对图像掩码mask中非0元素位置的图像像素进行修复。从而实现使用周围像素填充掩码区域cv2.inpaint 是 OpenCV 库中的一个函数,用于图像修复(inpainting),即填充图像中的损坏区…...

给虚拟机Ubuntu扩展硬盘且不丢数据
1.Ubuntu关机状态下先扩展,如扩展20GB 2.进入ubuntu,切换root登录,必须是root全选,否则启动不了分区工具gparted 将新的20GB创建好后,选择ext4,primary; 3.永久挂载 我的主目录在/并挂载到/dev/sda1 从图…...
Oracle(41)如何使用PL/SQL批量处理数据?
在PL/SQL中,批量处理数据是一种高效的方法,可以在数据库中处理大量数据,而无需逐行操作。批量处理数据的关键技术包括: PL/SQL表(索引表):在内存中存储数据以进行批量操作。FORALL语句…...

JavaEE 第2节 线程安全知识铺垫1
目录 一、通过jconsole.exe查看线程状态的方法 二、Thread类的几种常见属性 三、线程状态 一、通过jconsole.exe查看线程状态的方法 通过jconsole查看线程状态非常实用的方式 只要你安装了jdk,大致按照这个目录就可以找到这个可执行程序: 然后双击这…...
LeetCode Hot100 零钱兑换
给你一个整数数组 coins ,表示不同面额的硬币;以及一个整数 amount ,表示总金额。 计算并返回可以凑成总金额所需的 最少的硬币个数 。如果没有任何一种硬币组合能组成总金额,返回 -1 。 你可以认为每种硬币的数量是无限的。 示…...

微信小程序接口实现语音转文字
一、效果展示 我们有一个按钮,点击“开始录音”按钮,此时按钮变成“停止录音”并开始计时,点击停止录音后,界面上即可展示返回的文字 二、代码实现 完整代码实现见github 1.小程序端代码 // index.js const recorderManager…...
[Spark Streaming] 读取 Kafka 消息, 插入到 MySQL
以下是一个简单的使用 Spark Streaming 读取 Kafka 消息、统计数据后插入到 MySQL 中的 Scala 代码示例: import org.apache.spark.SparkConf import org.apache.spark.streaming.{Seconds, StreamingContext} import org.apache.spark.streaming.kafka.KafkaUtils…...

精选3款国内wordpress 主题,建站首选
WordPress作为一款功能强大且易于使用的建站平台,已经成为了许多企业和个人搭建网站的首选。为了帮助大家更好地选择适合自己的WordPress主题,小编将为大家推荐三款国内优秀的WordPress主题:子比主题、OneNav主题和RiTheme主题。 1.子比主题…...
JavaScript之 Uint8Array 类型数组(solana pda场景中的大小端)
文章目录 JavaScript之 Uint8Array 类型数组numberToUint8Array 数字转换为Uint8Array为什么要把数字转换为Uint8Array数字转换为Uint8Array的大小端问题solana pda场景中的大小端JavaScript之 Uint8Array 类型数组 Uint8Array 数组类型表示一个8位无符号整型数组,创建时内容…...

《Windows API每日一练》24.1 WinSock简介
本节将逐一介绍WinSock的主要特性和组件,套接字、WinSock动态库的使用。 本节必须掌握的知识点: Windows Socket接口简介 Windows Socket接口的使用 第178练:网络时间校验 24.1.1 Windows Socket接口简介 ■以下是WinSock的主要特性和组件…...

基于ASP.NET+ SQL Server实现(Web)医院信息管理系统
医院信息管理系统 1. 课程设计内容 在 visual studio 2017 平台上,开发一个“医院信息管理系统”Web 程序。 2. 课程设计目的 综合运用 c#.net 知识,在 vs 2017 平台上,进行 ASP.NET 应用程序和简易网站的开发;初步熟悉开发一…...

Ascend NPU上适配Step-Audio模型
1 概述 1.1 简述 Step-Audio 是业界首个集语音理解与生成控制一体化的产品级开源实时语音对话系统,支持多语言对话(如 中文,英文,日语),语音情感(如 开心,悲伤)&#x…...
代理篇12|深入理解 Vite中的Proxy接口代理配置
在前端开发中,常常会遇到 跨域请求接口 的情况。为了解决这个问题,Vite 和 Webpack 都提供了 proxy 代理功能,用于将本地开发请求转发到后端服务器。 什么是代理(proxy)? 代理是在开发过程中,前端项目通过开发服务器,将指定的请求“转发”到真实的后端服务器,从而绕…...

算法:模拟
1.替换所有的问号 1576. 替换所有的问号 - 力扣(LeetCode) 遍历字符串:通过外层循环逐一检查每个字符。遇到 ? 时处理: 内层循环遍历小写字母(a 到 z)。对每个字母检查是否满足: 与…...

day36-多路IO复用
一、基本概念 (服务器多客户端模型) 定义:单线程或单进程同时监测若干个文件描述符是否可以执行IO操作的能力 作用:应用程序通常需要处理来自多条事件流中的事件,比如我现在用的电脑,需要同时处理键盘鼠标…...
日常一水C
多态 言简意赅:就是一个对象面对同一事件时做出的不同反应 而之前的继承中说过,当子类和父类的函数名相同时,会隐藏父类的同名函数转而调用子类的同名函数,如果要调用父类的同名函数,那么就需要对父类进行引用&#…...
安卓基础(Java 和 Gradle 版本)
1. 设置项目的 JDK 版本 方法1:通过 Project Structure File → Project Structure... (或按 CtrlAltShiftS) 左侧选择 SDK Location 在 Gradle Settings 部分,设置 Gradle JDK 方法2:通过 Settings File → Settings... (或 CtrlAltS)…...
SpringAI实战:ChatModel智能对话全解
一、引言:Spring AI 与 Chat Model 的核心价值 🚀 在 Java 生态中集成大模型能力,Spring AI 提供了高效的解决方案 🤖。其中 Chat Model 作为核心交互组件,通过标准化接口简化了与大语言模型(LLM࿰…...
土建施工员考试:建筑施工技术重点知识有哪些?
《管理实务》是土建施工员考试中侧重实操应用与管理能力的科目,核心考查施工组织、质量安全、进度成本等现场管理要点。以下是结合考试大纲与高频考点整理的重点内容,附学习方向和应试技巧: 一、施工组织与进度管理 核心目标: 规…...

客户案例 | 短视频点播企业海外视频加速与成本优化:MediaPackage+Cloudfront 技术重构实践
01技术背景与业务挑战 某短视频点播企业深耕国内用户市场,但其后台应用系统部署于东南亚印尼 IDC 机房。 随着业务规模扩大,传统架构已较难满足当前企业发展的需求,企业面临着三重挑战: ① 业务:国内用户访问海外服…...