通过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实现插入排序(直接插入,希尔)与选择排序(直接选择,堆排)的全部内容,如有不当,敬请斧正!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/391400.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

MQ消息队列篇:三大MQ产品的必备面试种子题

MQ有什么用&#xff1f; MQ&#xff08;消息队列&#xff09;是一种FIFO&#xff08;先进先出&#xff09;的数据结构&#xff0c;主要用于实现异步通信、削峰平谷和解耦等功能。它通过将生产者生成的消息发送到队列中&#xff0c;然后由消费者进行消费。这样&#xff0c;生产…

(四)activit5.23.0修复跟踪高亮显示BUG

一、先看bug 在 &#xff08;三&#xff09;springboot2.7.6集成activit5.23.0之流程跟踪高亮显示 末尾就发现高亮显示与预期不一样&#xff0c;比如上面的任务2前面的箭头没有高亮显示。 二、分析原因 具体分析步骤省略了&#xff0c;主要是ProcessInstanceHighlightsResour…

【iOS】暑假第二周——网易云APP 仿写

目录 前言首页关于UINavigationBarAppearance “我的”账号夜间模式——多界面传值遇到的问题所用到的其他知识整理NSNotificationreloadData各种键盘模式 总结 前言 有了之前仿写ZARA的基础&#xff0c;本周我们仿写了网易云APP&#xff0c;在这里对多界面传值进行了首次应用—…

算力共享中神经网络切片和算力分配策略

目录 神经网络切片 按照算力的分布进行网络层数切片;就是算力越强,运算神经网络层数越多 神经网络切片和算力占比进行映射 算力分配策略 get_current_shard 神经网络切片 按照算力的分布进行网络层数切片;就是算力越强,运算神经网络层数越多 神经网络切片和算力占比进…

【MySQL】索引——索引的引入、认识磁盘、磁盘的组成、扇区、磁盘访问、磁盘和MySQL交互、索引的概念

文章目录 MySQL1. 索引的引入2. 认识磁盘2.1 磁盘的组成2.2 扇区2.3 磁盘访问 3. 磁盘和MySQL交互4. 索引的概念4.1 索引测试4.2 Page4.3 单页和多页情况 MySQL 1. 索引的引入 海量表在进行普通查询的时候&#xff0c;效率会非常的慢&#xff0c;但是索引可以解决这个问题。 -…

PHP中关于排名和显示的问题

&#x1f3c6;本文收录于《CSDN问答解惑-专业版》专栏&#xff0c;主要记录项目实战过程中的Bug之前因后果及提供真实有效的解决方案&#xff0c;希望能够助你一臂之力&#xff0c;帮你早日登顶实现财富自由&#x1f680;&#xff1b;同时&#xff0c;欢迎大家关注&&收…

RabbitMQ应用场景及特性

RabbitMQ是一款开源的消息队列中间件&#xff0c;拥有非常好用的管理控制面板&#xff0c;类似使用navicat一样&#xff0c;简便的操纵数据库。 应用场景 一、流量削峰 在一些并发量较高的场景下&#xff0c;比如秒杀活动&#xff0c;抢票等&#xff0c;同一时间访问量急剧增…

【Linux】shell命令与Linux权限的概念

目录 一、shell命令二、Linux权限的概念2.1 Linux权限的概念2.1.1 用户2.1.2 指令2.1.2.1 su指令2.1.2.2 sudo指令 2.2 Linux权限管理2.2.1 文件访问者的分类&#xff08;人&#xff09;2.2.2 文件类型和访问权限&#xff08;事物属性&#xff09;2.2.2.1 文件类型2.2.2.2 基本…

[240804] OpenTofu 1.8.0 发布,带来更友好的编码体验 | 生成式 AI 滥用现象分析

目录 OpenTofu 1.8.0 发布&#xff0c;带来更友好的编码体验生成式 AI 滥用现象分析 OpenTofu 1.8.0 发布&#xff0c;带来更友好的编码体验 OpenTofu 1.8.0 现已发布&#xff0c;主要功能包括&#xff1a; 变量和局部值的早期求值: 现在可以在模块源、后端配置和状态加密等更…

使用 1panel面板 部署 springboot 和 vue

代码仓库&#xff1a;还没弄 目录 网站介绍安装步骤1. 准备云服务器2. 准备域名&#xff08;可跳过&#xff09;3. 安装1panel面板4. 服务器开放端口5. 进入1panel面板6. 安装并启动软件&#xff08;服务器和面板开放端口&#xff09;7. 打包并上传项目7.1 打包 Java项目&#…

网页保护用户 小tips

在使用创建web开发的过程中&#xff0c;直接使用用户名url&#xff0c;容易造成用户信息的被攻击&#xff0c;例如对方直接访问 ../../.../username 的网页&#xff0c;可以窃取用户信息&#xff0c;然而把usename变成一堆乱码就安全的多 效果&#xff1a; 代码&#xff1a;…

想做抖音短视频,视频素材去哪里找啊?

各位抖音上的短视频创作者们&#xff0c;是否曾幻想过自己的作品能够在全网爆火&#xff0c;却常因为缺少那些能够让视频更加生动的素材而感到困扰&#xff1f;不用担心&#xff0c;今天我要为大家介绍几个优秀的视频素材网站&#xff0c;让你的抖音之路顺风顺水&#xff01; …

星环科技与宁夏银行“大数据联合实验室”揭牌,持续打造金融科技新范式

5月30-31日&#xff0c;2024向星力未来数据技术峰会期间&#xff0c;在峰会现场来宾共同见证下&#xff0c;星环科技与宁夏银行“大数据联合实验室”正式揭牌&#xff0c;宁夏银行股份有限公司首席信息官崔彦刚与星环科技副总裁邱磊共同为联合实验室揭牌。 星环科技与宁夏银行借…

【每日刷题】Day92

【每日刷题】Day92 &#x1f955;个人主页&#xff1a;开敲&#x1f349; &#x1f525;所属专栏&#xff1a;每日刷题&#x1f34d; &#x1f33c;文章目录&#x1f33c; 1. 面试题 16.05. 阶乘尾数 - 力扣&#xff08;LeetCode&#xff09; 2. 取近似值_牛客题霸_牛客网 (n…

4. 最长公共前缀

4. 最长公共前缀 题目题目分析 题目 题目分析 首先要对字符串数组进行分析&#xff0c;字符串数组元素的最长公共前缀肯定不会超过最小元素长度&#xff0c;并如存在公共前缀则需遍历整个字符串元素&#xff0c;有点像二维数组&#xff0c;最后加上截取字符串加上判空操作就完…

测试——Selenium

内容大纲: 什么是自动化测试 什么是Selenium Selenium工作原理 Selenium环境搭建 Selenium API 目录 1. 什么是自动化测试 2. 什么是Selenium 3. Selenium工作原理 4. Selenium环境搭建(java) 5. Selenium API 5.1 定位元素 5.1.1 CSS选择器定位元素 5.1.2 XPath定位元…

Kubernets(k8s) 网络原理三:同主机内Pod相互访问

前两篇文章中我们介绍了pod怎么和宿主机通信以及pod怎么访问外网&#xff0c;这两种通信是理解pod间通信的基础。 关于pod间的相互访问&#xff0c;这里还需要细化一下。回想一下pod在k8s节点中的分布&#xff0c;两个pod可能分布在同一台宿主机上&#xff0c;也可能分布在不同…

ECMAScript 12 (ES12, ES2021) 新特性

还是大剑师兰特&#xff1a;曾是美国某知名大学计算机专业研究生&#xff0c;现为航空航海领域高级前端工程师&#xff1b;CSDN知名博主&#xff0c;GIS领域优质创作者&#xff0c;深耕openlayers、leaflet、mapbox、cesium&#xff0c;canvas&#xff0c;webgl&#xff0c;ech…

PXE实验

实验前准备 关闭VMware的dhcp 点击 编辑 点击 虚拟网络编辑器 选择 NAT模式 将dhcp取消勾选 准备两台虚拟机 一台试验机&#xff0c;&#xff08;网络环境正常并且有图形化的界面的rhel7&#xff09; 一台测试机 init 5 --------------> 开启图形化界面 如…

element-plus框架+vue3+echart——后台页面

一、图表样式 图表组件&#xff1a;echarts https://echarts.apache.org/examples/zh/index.html element-plus框架&#xff1a; https://www.cwgj.xyz/zh-CN/ 1、折线图 栅格 一共24。 12代表占一半50%&#xff0c; 当页面缩小到一定程度 占整个屏幕的100%。 id"mo…