Java数据结构(八)——插入排序、希尔排序

news2024/12/24 8:43:59

文章目录

  • 插入排序
    • 算法介绍
    • 代码实现
    • 复杂度和稳定性
  • 希尔排序
    • 算法介绍
    • 代码实现
    • 复杂度和稳定性

将这两种排序放在一起的原因是它们都属于 “插入”(式)排序
还有很多排序思想,这里不放在一篇文章介绍是因为会导致篇幅过长,我们会按分类多次介绍不同的排序方法,最终会合并为一个排序总集。

插入排序

算法介绍

这里的插入排序指的是直接插入排序是一种简单直观的排序算法。它的工作方式是通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入。 插入排序在实现上,通常采用in-place排序(即只需用到O(1)的额外空间的排序),因而在从后向前扫描过程中,找到排序位置后,需要将已排序元素逐步向后挪位,为新元素提供插入空间。

具体步骤如下:

  1. 从第一个元素开始,该元素可以认为已经被排序。
  2. 取出下一个元素,在已经排序的元素序列中从后向前扫描。
  3. 如果该元素(已排序)大于新元素,将该元素移到下一位置。
  4. 重复步骤3,直到找到已排序的元素小于或者等于新元素的位置。
  5. 将新元素插入到该位置后。
  6. 重复步骤2~5

【图示】

在这里插入图片描述


代码实现

代码演示均采用升序

    public void insertSort(int[] array) {
        //从第2个位置开始向有序序列中插入
        for(int i = 1; i < array.length; i++) {
            int tmp = array[i];
            int pos = i - 1;
            //向前扫描
            for(int j = i - 1; j >= 0; j--) {
                if(array[j] > tmp) {
                    array[j+1] = array[j];
                    pos--;
                }else {
                    break;
                }
            }
            array[pos+1] = tmp;
        }
    }

复杂度和稳定性

时间复杂度

  • 平均情况:O(N^2)
  • 最好情况:O(N),当序列基本有序时

空间复杂度O(1)

稳定性稳定,我们可以通过控制条件达到稳定:相等就不再继续向前扫描比较。


希尔排序

算法介绍

希尔排序,又称缩小增量排序,是插入排序的一种更高效的改进版本。

当序列基本有序时,直接插入排序的时间复杂度可以达到O(n),而当原序列随机性很大时直接插入排序的时间复杂度为O(n^2),基于这一点,在直接插入排序前,进行预排序,使得原序列接近有序,再执行直接插入排序,那么效率就比直接插入排序高。

希尔排序的基本思想就是将整个待排序的记录序列分割成为若干个子序列分别进行直接插入排序,待整个序列中的记录“基本有序”时,再对全体记录进行一次直接插入排序。


分割为若干个子序列意味着需要分组,为什么要分组?

如果一下对所有元素(假设n个)进行直接插入排序,时间性能差;但是,当分组后,对每个组内的元素进行直接插入排序,相当于n值变小了,有时可以忽略,性能有所提升。

那么怎样分割出若干个子序列,怎么分组?分组后怎么做?

分组需要确定gap值,gap值在希尔排序中用于定义元素间比较的“跳跃”距离,关于gap值的选取是另一个问题,我们先选择gap = gap / 2的方案(按照gap = gap / 2来不断减小gap值,直到最后为1)来讲解分组,如图:

在这里插入图片描述

如图,希尔排序采用的是跳跃式分组,如34和45为一组,而不是34和紧挨的78为一组,跳跃式分组后,采用组内(相同颜色的线连接)直接插入排序,跳跃式分组并排序的好处是:能让较小的数更快地到达前面,较大的数更快地到达后面。且gap的值越大,小数就能越快地到达前面,大数就能越快地到达后面。

在这里插入图片描述

如图,这一趟完成了gap = 5时的组内排序,每个组内元素组成的序列均有序,使得整体变得有序起来。

接着,以gap = 5 / 2 = 2分组并排序:

在这里插入图片描述

gap = 2 / 2 = 1 进行整体的直接插入排序,此时整体已经基本有序,时间性能较好。

在这里插入图片描述


算法步骤如下:

  1. 选择增量序列:首先取一个整数d1=n/2(n是数组的长度),将元素分为d1个组,每组相邻元素之间相隔d1个位置。在各组内进行直接插入排序;
  2. 重复分组排序:然后,取第二个增量d2=d1/2,重复上述的分组排序过程,直到di=1,即所有记录在同一组内进行直接插入排序。

关于gap值,通常采用gap = gap / 2或者gap = gap / 3 + 1的方案,但没有最优解:

在这里插入图片描述


【图示】

在这里插入图片描述


代码实现

代码演示均采用升序:

    public void shellSort() {
        int gap = array.length;
        //gap值(增量)会不断缩小
        while(gap > 1) {
            gap /= 2;
            //直接插入排序,但有所修改
            for(int i = gap; i < array.length; i++) {
                int tmp = array[i];
                int pos = i - gap;
                for(int j = i - gap; j >= 0; j-=gap) {
                    if(array[j] > tmp) {
                        array[j+gap] = array[j];
                        pos-=gap;
                    }else {
                        break;
                    }
                }
                array[pos+gap] = tmp;
            }
        }
    }
  • 直接插入排序逻辑部分,外层for循环的循环变量要初始化为gap,因为要寻找组内"第二个"元素(第一个元素已经有序),内层for循环的循环变量从int j = i - 1修改为int j = i - gap,同时j-=gap保证了在组内进行比较,包括后面array[pos+gap] = tmp也是保证"组内性"

  • 值得讨论的就是外层的for循环为什么使用i++,而不是i+=gap,通过图来解释一下:

    在这里插入图片描述

    第一次进入外层for循环,i 的值为45,这一次循环对[34, 45]组进行了组内排序;

    完成后,如果循环变量的变化为i+=gap,此时 i 的值变化为10,直接结束了gap = 5时的这一次预排序,但实际上还有4组没有完成组内排序;如果循环变量的变化为i++,此时 i 的值变化为6,即指向63的位置,继续进行预排序。

    继续观察gap = 2

    在这里插入图片描述

    此时,第一次进入外层for循环,i 指向29,按照i++的变化,第二次进入外层for循环时,i 指向15,指向了另一个分组,有影响吗?

    其实不影响,这种 组间跳跃式排序最终可以完成任务,因为有j-=gap的约束,即使跳组了,也是在该组内进行排序。


复杂度和稳定性

时间复杂度O(N*log2N)

实际上,希尔排序的时间复杂度并不是严格的O(N*log2N)。希尔排序的性能与所选择的增量序列有很大关系,不同的增量序列会导致算法的性能有所不同。其最坏时间复杂度仍可能是O(N^2),还是依赖于增量序列的选择,但 通常被认为是比O(N^2)要好,但不如O(N*log2N)那么优秀。 具体一点,其平均时间复杂度在O(N^1.3)~O(N^1.5)

在这里插入图片描述

空间复杂度O(1)

稳定性不稳定,由于子序列的划分和插入排序的特性,相同大小的元素可能会在不同的子序列中被重新排序,从而改变了它们之间的相对位置。


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

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

相关文章

[数据集][目标检测]智慧农业草莓叶子病虫害检测数据集VOC+YOLO格式4040张9类别

数据集格式&#xff1a;Pascal VOC格式YOLO格式(不包含分割路径的txt文件&#xff0c;仅仅包含jpg图片以及对应的VOC格式xml文件和yolo格式txt文件) 图片数量(jpg文件个数)&#xff1a;4040 标注数量(xml文件个数)&#xff1a;4040 标注数量(txt文件个数)&#xff1a;4040 标注…

HTTP协议 HTTPS协议 MQTT协议介绍

目录 一&#xff0e;HTTP协议 1. HTTP 协议介绍 基本介绍&#xff1a; 协议&#xff1a; 注意&#xff1a; 2. HTTP 协议的工作过程 基础术语&#xff1a; 客户端&#xff1a; 主动发起网络请求的一端 服务器&#xff1a; 被动接收网络请求的一端 请求&#xff1a; …

基于MinerU的PDF解析API

基于MinerU的PDF解析API - MinerU的GPU镜像构建 - 基于FastAPI的PDF解析接口支持一键启动&#xff0c;已经打包到镜像中&#xff0c;自带模型权重&#xff0c;支持GPU推理加速&#xff0c;GPU速度相比CPU每页解析要快几十倍不等 主要功能 删除页眉、页脚、脚注、页码等元素&…

实验记录 | 点云处理 | K-NN算法3种实现的性能比较

引言 K近邻&#xff08;K-Nearest Neighbors, KNN&#xff09;算法作为一种经典的无监督学习算法&#xff0c;在点云处理中的应用尤为广泛。它通过计算点与点之间的距离来寻找数据点的邻居&#xff0c;从而有效进行点云分类、聚类和特征提取。本菜在复现点云文章过程&#xff…

【OpenCV2.2】图像的算术与位运算(图像的加法运算、图像的减法运算、图像的融合)、OpenCV的位运算(非操作、与运算、或和异或)

1 图像的算术运算 1.1 图像的加法运算 1.2 图像的减法运算 1.3 图像的融合 2 OpenCV的位运算 2.1 非操作 2.2 与运算 2.3 或和异或 1 图像的算术运算 1.1 图像的加法运算 add opencv使用add来执行图像的加法运算 图片就是矩阵, 图片的加法运算就是矩阵的加法运算, 这就要求加…

notepad下载安装教程

一、强大高效的代码编辑器 Notepad 是一款功能强大的代码编辑器&#xff0c;专为程序员和开发人员设计。无论是编写代码、处理文本文件&#xff0c;还是进行快速编辑&#xff0c;Notepad 都能提供卓越的性能和便利的功能&#xff0c;极大提升您的工作效率。 二、安装详细教程…

双指针(5)_单调性_有效三角形的个数

个人主页&#xff1a;C忠实粉丝 欢迎 点赞&#x1f44d; 收藏✨ 留言✉ 加关注&#x1f493;本文由 C忠实粉丝 原创 双指针(5)_单调性_有效三角形的个数 收录于专栏【经典算法练习】 本专栏旨在分享学习C的一点学习笔记&#xff0c;欢迎大家在评论区交流讨论&#x1f48c; 目录…

c++stack和list 介绍

stack介绍 堆栈是一种容器适配器&#xff0c;专门设计用于在 LIFO 上下文&#xff08;后进先出&#xff09;中运行&#xff0c;其中元素仅从容器的一端插入和提取。 堆栈作为容器适配器实现&#xff0c;容器适配器是使用特定容器类的封装对象作为其基础容器 的类&#xff0c;提…

mysql可重复读不能解决幻读吗?

1、可重复读和幻读的概念 1.1、可重复读 可重复读是数据库的四个隔离级别之一,可重复读可以保证在一个事物之内读取到的数据永远是相同的(通过mvcc表快照实现的),哪怕这期间有其它事务对数据做了修改,也不会影响当前事务的查询。 1.2、幻读 网上有不少博客说:幻读是一个事物内…

正规表达式例题

解析&#xff1a;从题意可知&#xff0c;a可以有零个或多个&#xff0c;b有1个或多个 选项A&#xff1a;这里a至少有1个&#xff0c;不符合题意 选项B&#xff1a;a^*bb^*&#xff0c;a是0个或多个&#xff0c;b可以是1个或多个&#xff0c;符合题意 选项C和选项D&#xff0…

Jenkins 通过 Version Number Plugin 自动生成和管理构建的版本号

步骤 1&#xff1a;安装 Version Number Plugin 登录 Jenkins 的管理界面。进入 “Manage Jenkins” -> “Manage Plugins”。在 “Available” 选项卡中搜索 “Version Number Plugin”。选中并安装插件&#xff0c;完成后可能需要重启 Jenkins。 步骤 2&#xff1a;配置…

尚品汇-支付宝下单接口显示二维码实现(四十六)

目录&#xff1a; &#xff08;1&#xff09;支付功能实现 &#xff08;2&#xff09;保存支付信息 &#xff08;3&#xff09;编写支付宝支付接口 &#xff08;1&#xff09;支付功能实现 支付宝有了同步通知为什么还需要异步通知&#xff1f; 同步回调两个作用 第一是从支付…

密保管家-随机密码本地生成

下载 简介 安全无忧:采用先进的加密算法,确保您的密码安全不外泄。 随机性强:每次生成的密码都是完全随机的,避免模式化,增加破解难度。 易于管理:简洁的界面设计让您轻松管理所有账号的密码。 独立运行:无需网络连接,所有数据本地存储,保护隐私的同时提供便捷的密…

【MATLAB】模拟退火算法

模拟退火算法的MATLAB实现 模拟退火算法简介模拟退火算法应用实例关于计算结果 模拟退火算法简介 1982年&#xff0c;Kirkpatrick 将退火思想引入组合优化领域&#xff0c;提出了一种能够有效解决大规模组合优化问题的算法&#xff0c;尤其对 NP 完全问题表现出显著优势。模拟…

FreeRTOS 优先级翻转以及互斥信号量

优先级翻转&#xff1a; 高优先级的任务反而慢执行&#xff0c;低优先级的任务反而优先执行 优先级翻转在抢占式内核中是非常常见的&#xff0c;但是在实时操作系统中是不允许出现优先级翻转的&#xff0c;因为优先级翻转会破坏任务的预期顺序&#xff0c;可能会导致未知的严重…

react | 自学笔记 | 持续更新

React自学速学笔记 数据单向流动事件为什么上述例子&#xff0c;是onClick{()>shoot("goal!")}而不是onClick{shoot("goal")}?event对象 条件渲染if方法&&?: 三元表达式 纯小白自学笔记&#xff0c;有不对的欢迎指正。 数据单向流动 单向流动…

如何确保光伏电站EPC施工的质量

说到保证EPC施工的质量&#xff0c;我们得先了解什么是EPC施工&#xff0c;是指&#xff1a;指总承包商按照合同约定&#xff0c;承担工程项目的设计、采购、施工等工作&#xff0c;并对工程的质量、安全、工期和造价全面负责。 EPC施工还有几个特点&#xff1a; 一体化服务&…

单片机毕业设计基于stm32的蔬菜大棚智能监控系统设计

文章目录 前言资料获取设计介绍功能介绍程序代码部分参考 设计清单具体实现截图参考文献设计获取 前言 &#x1f497;博主介绍&#xff1a;✌全网粉丝10W,CSDN特邀作者、博客专家、CSDN新星计划导师&#xff0c;一名热衷于单片机技术探索与分享的博主、专注于 精通51/STM32/MSP…

2.2.3 UDP的可靠传输协议QUIC 2

udp可靠传输 kcp协议 网络通畅下&#xff0c;kcp比tcp慢 这里直接看课件图片&#xff0c; 延迟ack比非延迟减少应答包数量&#xff0c;但是慢 kcp 讲解 kan代码ikcp.c 按照readme指南编译一下&#xff01;&#xff01; mkdir build cd build cmake .. make第一遍报错&#xf…

ant-design-vue中实现a-tree树形控件父子关联选中过滤的算法

在使用ant-design-vue的框架时&#xff0c;a-tree是比较常用的组件&#xff0c;比较适合处理树形结构的数据。 但是在与后台数据进行授权交互时&#xff0c;就不友好了。 在原生官方文档的例子中&#xff0c;若子项被勾选&#xff0c;则父级节点会被关联勾选&#xff0c;但这勾…