通过Java实现插入排序(直接插入,希尔)与选择排序(直接选择,堆排)

news2025/1/12 3:58:59

目录

(一)插入排序

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){
        //设定间隔增量gap
        int 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){
        //设定间隔增量gap
        int 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.coloradmin.cn/o/1968876.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

C# 串口控制 校验

1. 串口控制 using System; using System.IO.Ports; using System.Windows.Forms;namespace 串口控制 {public partial class Form1 : Form{//device1const byte DeviceOpen1 0x01;const byte DeviceClose1 0x81;//device2const byte DeviceOpen2 0x02;const byte DeviceCl…

【Canvas与艺术】六角大楼

【成图】 【代码】 <!DOCTYPE html> <html lang"utf-8"> <meta http-equiv"Content-Type" content"text/html; charsetutf-8"/> <head><title>六角大楼</title><style type"text/css">.cen…

【WOA】鲸鱼优化算法详细解读

鲸鱼优化算法的详细解读 目录 一、引言 二、鲸鱼优化算法的原理 三、鲸鱼优化算法的主要步骤 四、鲸鱼优化算法的特点 五、Python代码实现 一、引言 在当今的优化问题中&#xff0c;随着问题复杂性的增加&#xff0c;传统的优化方法往往难以找到全局最优解。近年来&#…

【计算机毕业设计】​720图书馆智能选座系统

&#x1f64a;作者简介&#xff1a;拥有多年开发工作经验&#xff0c;分享技术代码帮助学生学习&#xff0c;独立完成自己的项目或者毕业设计。 代码可以私聊博主获取。&#x1f339;赠送计算机毕业设计600个选题excel文件&#xff0c;帮助大学选题。赠送开题报告模板&#xff…

仓库物品与装备物品替换

思路 1、创建UI面板以承载仓库中的物品和已装备的物品&#xff0c;以及物品名称和物品描述&#xff1b; 2、创建ItemData.cs装载物品的缩略图、描述并创建ItemData对象 3、创建一个脚本&#xff0c;声明并定义承载ItemData对象的数组、承载缩略图的数组。 4、显示缩略图、文…

6.key的层级结构

redis的key允许多个单词形成层级结构&#xff0c;多个单词之间用:隔开&#xff0c;格式如下&#xff1a; 项目名:业务名:类型:id 这个格式并非固定的&#xff0c;可以根据自己的需求来删除或添加词条。 例如&#xff1a; taobao:user:1 taobao:product:1 如果value是一个java对…

【Golang 面试 - 进阶题】每日 3 题(十一)

✍个人博客&#xff1a;Pandaconda-CSDN博客 &#x1f4e3;专栏地址&#xff1a;http://t.csdnimg.cn/UWz06 &#x1f4da;专栏简介&#xff1a;在这个专栏中&#xff0c;我将会分享 Golang 面试中常见的面试题给大家~ ❤️如果有收获的话&#xff0c;欢迎点赞&#x1f44d;收藏…

提升SEO排名的谷歌外链策略

​要提升SEO排名&#xff0c;谷歌外链策略必须聚焦于外链数量和质量的合理搭配。市场上那些SEO表现优秀的网站&#xff0c;无一例外地拥有数万甚至数十万的外链&#xff0c;而且这些外链在结构上表现出复杂和多样化。这不仅仅是因为数量众多&#xff0c;还因为这些外链质量的高…

酷家乐--应用频繁报出cause java.net.SocketTimeoutException: Read timed out怎么办

涉及到网络层面的问题一般都比较复杂&#xff0c;场景多&#xff0c;定位难&#xff0c;成为了大多数开发的噩梦&#xff0c;应该是最复杂的了。下面通过一个实际的例子来阐述遇到了要怎么办。 现象 部署在矩阵机房的较多应用频繁报出toad异常&#xff0c;Encounter unknown …

嵌入式学习第13天——C语言循环结构break和continue

break和continue break 功能&#xff1a; 1.用在switch中&#xff0c;用来跳出switch的case语句;如果case没有break&#xff0c;可能会产生case穿透。 2.用在循环中(while、do..while、for..)&#xff0c;提前结束循环&#xff0c;也就是跳出整个循环。 说明&#xff1a; …

spring的三级缓存与源码分析--解决循环依赖

三级缓存介绍 Spring 通过三层缓存来处理循环依赖&#xff0c;这些缓存分别是&#xff1a; 一级缓存&#xff08;内存中的 singletonObjects&#xff09; 二级缓存&#xff08;earlySingletonObjects&#xff09; 三级缓存&#xff08;singletonFactories&#xff09; 1. 一…

深入理解C语言结构体

目录 引言 一. 结构体的基本概念 1.结构体的声明 2. 结构体变量的创建和初始化 3. 结构体成员访问操作符 4.结构体的特殊声明 1. 匿名结构体 2. 嵌套结构体 3.结构体自引用 4. typedef 声明 二、结构体内存对⻬ 1.对⻬规则 2.为什么存在内存对⻬? 3.修改默认对齐…

ffmpeg命令-Windows下常用最全

查询命令 参数 说明 -version 显示版本。 -formats 显示可用的格式&#xff08;包括设备&#xff09;。 -demuxers 显示可用的demuxers。 -muxers 显示可用的muxers。 -devices 显示可用的设备。 -codecs 显示libavcodec已知的所有编解码器。 -decoders 显示可用…

基于SpringBoot+Vue的小区物业管理系统(带1w+文档)

基于SpringBootVue的小区物业管理系统(带1w文档) 基于SpringBootVue的小区物业管理系统(带1w文档) 小区物业管理系统采用B/S(Browser/Server)架构和MVC模型进行设计开发。在B/S架构下&#xff0c;用户在浏览器端进行使用&#xff0c;主要工作通过服务器端进行实现&#xff0c;用…

电脑缺少dll文件怎么解决?10款dll修复工具大盘点,赶紧收藏起来!

电脑缺少dll文件怎么解决&#xff1f;DLL&#xff08;动态链接库&#xff09;是一种重要文件&#xff0c;包含了一系列指令&#xff0c;用于运行几乎所有 Win10、Win8和 Win7的程序。如果Windows 操作系统中缺少DLL文件&#xff0c;您可能会无法启动所需的程序或应用。在 Win10…

【AndroidStudio】修改app名称、版本号、图标

文章目录 1. 修改app名称(AndroidManifest.xml-app_name字段)2. 修改app版本号和版本名称3. 修改app图标4. 修改app启动过渡图片 1. 修改app名称(AndroidManifest.xml-app_name字段) 2. 修改app版本号和版本名称 通常是app目录下的build.gradle文件找到“versionCode”和“ver…

基于域名+基于ip+基于端口的虚拟主机+上线商务系统

一、回顾 1.jdk环境 tomcat服务器需要jdk环境 版本对应 ​ tomcat9>jdk1.8 配置系统变量JAVA_HOME sed -i $aexport JAVA_HOME/usr/local/jdk22/ /etc/profile sed -i $aexport PATH$JAVA_HOME/bin:$PATH /etc/profile ​ source /etc/profile ​ java -version java…

LeetCode | 441 | 排列硬币 | 二分查找

&#x1f64b;大家好&#xff01;我是毛毛张! &#x1f308;个人首页&#xff1a; 神马都会亿点点的毛毛张 今天分享的是LeetCode中一道标签为简单的算法题&#xff0c;本质是一道数学题 文章目录 1.题目描述2.题解2.1 公式解法2.2 暴力解法2.3 二分查找 LeetCode链接&#…

【 问题 】 AT32 F413CB 设置SRAM大小为64KB 导致Flash后64KB代码执行变慢 解决办法

背景 AT32的SRAM可以设置为16KB/32KB/64KB的不同大小&#xff0c;设置SRAM大小将导致Flash的部分空间的读写速度减缓&#xff0c;如下图&#xff1a; 这个问题看似不是很大&#xff0c;但是当运行一些很保证实时性&#xff0c;速度性的代码时&#xff0c;就会产生一些问题。 …

react-日期选择器封装

文件 import { useMemo, useState, useEffect } from "react" import dayjs, { Dayjs } from "dayjs" import "dayjs/locale/zh-cn" import "./App.css" dayjs.locale("zh-cn")function SimpleCalendar() {// 当前时间对象…