在排序算法的大家庭中,希尔排序(Shell Sort)以其独特的 "分组插入" 思想占据着重要地位。它是对插入排序的创造性改进,通过引入 "增量分组" 策略,大幅提升了排序效率。本文将带你深入理解希尔排序的工作原理,解析其 Java 实现代码,并探讨其时间复杂度特性。

什么是希尔排序?

希尔排序由计算机科学家 Donald Shell 于 1959 年提出,其核心思想是:将数组按一定间隔(增量)分为多个子序列,对每个子序列执行插入排序;然后逐步缩小间隔,重复上述过程;最后当间隔为 1 时,执行一次完整的插入排序,完成整个数组的排序

这种 "先宏观调整,再微观优化" 的策略,有效克服了插入排序在处理大规模逆序数组时的低效问题,让元素能够快速跨越较长距离移动到大致正确的位置。

希尔排序的 Java 实现

下面是一个完整的希尔排序实现代码,我们将以此为基础进行深入解析:

public class Shell {public static void main(String[] args) {Integer[] integers = new Integer[10];for (int i = 0; i < integers.length; i++) {integers[i] = (int) (Math.random() * 100);}System.out.println("排序前:");for (Integer integer : integers) {System.out.print(integer+" ");}sort(integers);System.out.println();System.out.println("排序后");for (Integer integer : integers) {System.out.print(integer+" ");}}public static void sort(Comparable[] a){//根据数组长度确定增长量的初始值int h = 1;while (h<a.length/2){h = 2*h+1;}//外层循环控制增长量while (h>=1){//内存循环将产出的数据与已排序的作比较for (int i = h; i < a.length; i++) {// 内层循环,将 a[i] 与前面间隔为 h 的元素比较for (int j = i; j >= h ; j -= h) {if ( greater(a[j - h], a[j])){exchange(a, j - h, j);}}}//增长量减半h = h/2;}}//比较v是否大于n;public static boolean greater(Comparable v,Comparable n){//调用comparable的方法//v大于n是返回 1,//v等于n时返回 0,//v小时n时返回 -1int i = v.compareTo(n);return i>0;}//交换方法public static void exchange(Comparable[] a,int i,int j){Comparable temp = a[i];a[i] = a[j];a[j] = temp;}}

希尔排序的工作原理详解

让我们逐步解析希尔排序的执行流程,理解其核心机制:

1. 增量序列的初始化

希尔排序的第一步是确定初始增量值 h:

int h = 1;
while (h < a.length / 2) {h = 2 * h + 1;
}

这段代码生成的是2h+1 增量序列(1, 3, 7, 15, 31...),特点是每个增量都是前一个的 2 倍加 1。对于长度为 10 的数组,初始 h 值为 7(因为 7 < 10/2=5 不成立,最终 h=3? wait,这里计算有误,正确计算应该是:当数组长度为 10 时,初始 h 从 1 开始:

  • 第一次循环:h=1 < 5 → h=2*1+1=3
  • 第二次循环:h=3 < 5 → h=2*3+1=7
  • 第三次循环:h=7 < 5 不成立,退出循环,初始 h=7

所以对于长度为 10 的数组,初始增量 h=7。

2. 多轮增量排序过程

希尔排序的核心是多轮不同增量的排序,每轮都包含三个层次的循环:

外层循环:控制增量变化
while (h >= 1) {// 排序逻辑...h = h / 2;  // 缩小增量
}

外层循环负责将增量 h 从初始值逐步缩小到 1,每轮排序后 h 都会减半(整数除法)。对于初始 h=7 的情况,增量变化序列为:7 → 3 → 1。

中层循环:遍历待排序元素
for (int i = h; i < a.length; i++) {// 内层排序逻辑...
}

中层循环从索引 h 开始遍历数组,确保我们处理的是每个子序列中除第一个元素外的所有元素。

内层循环:子序列插入排序
for (int j = i; j >= h; j -= h) {if (greater(a[j - h], a[j])) {exchange(a, j - h, j);}
}

这是希尔排序的关键部分,它对每个子序列执行插入排序:

  • j 从 i 开始,每次向前移动 h 步(在子序列内移动)
  • 比较当前元素与子序列中前一个元素(间隔 h)
  • 如果逆序则交换元素,直到找到正确位置

3. 实例演示:增量 h=3 时的排序过程

假设数组为[60, 30, 80, 40, 10, 90, 20, 50, 70](长度 9),当 h=3 时:

分组情况

数组按间隔 3 分为 3 个独立子序列:

  • 第 1 组:[60, 40, 20](索引 0、3、6)
  • 第 2 组:[30, 10, 50](索引 1、4、7)
  • 第 3 组:[80, 90, 70](索引 2、5、8)
子序列排序过程(以第 1 组为例)
  1. 处理 i=3(元素 40):

    • j=3,比较 a [0](60)和 a [3](40)
    • 60>40,交换后组变为[40, 60, 20]
  2. 处理 i=6(元素 20):

    • j=6,比较 a [3](60)和 a [6](20)→ 交换,组变为[40, 20, 60]
    • j=3,比较 a [0](40)和 a [3](20)→ 交换,组变为[20, 40, 60]

经过本轮排序,第 1 组已完全有序。其他组也会执行相同逻辑,最终整个数组会更接近有序状态。

希尔排序的性能分析

时间复杂度

希尔排序的时间复杂度分析较为复杂,它高度依赖于增量序列的选择:

增量序列最坏情况时间复杂度平均情况时间复杂度
原始希尔增量(h/2)O(n²)O(n^1.5)
Hibbard 增量(2^k-1)O(n^(3/2))O(n^(5/4))
Knuth 增量(3h+1)O(n^(3/2))O(n log²n)
Sedgewick 增量O(n^1.3)O(n^1.2)

我们实现中使用的 2h+1 增量序列(类似 Knuth 增量)在最坏情况下的时间复杂度约为 O (n^(3/2)),远好于插入排序的 O (n²)。

空间复杂度

希尔排序是一种原地排序算法,仅需要常数级别的额外空间:

  • 几个变量用于控制循环(h、i、j)
  • 一个临时变量用于元素交换

因此,希尔排序的空间复杂度为O(1)

稳定性

希尔排序是不稳定的排序算法,因为在不同增量的排序过程中,相等元素的相对位置可能会发生变化。

希尔排序的优化与应用场景

优化方向

  1. 选择更优的增量序列:Sedgewick 增量序列在实际应用中性能更优,但实现稍复杂
  2. 添加提前退出机制:在内层循环中,当元素找到正确位置后可立即退出
    if (greater(a[j - h], a[j])) {exchange(a, j - h, j);
    } else {break;  // 已找到正确位置,提前退出
    }
    
  3. 缓存增量计算结果:对于大型数组,可预计算增量序列并存入数组

适用场景

希尔排序特别适合:

  • 中等规模的数据集(n=1000~10000)
  • 对空间复杂度要求严格的场景(O (1) 空间)
  • 嵌入式系统或内存受限的环境
  • 作为更复杂排序算法的子过程

在 Java 的 Arrays.sort () 方法中,希尔排序曾被用于处理中等规模的数组排序。

总结

希尔排序通过 "增量分组 + 插入排序" 的创新思想,成功突破了插入排序在大规模数据上的性能瓶颈。其核心优势在于:

  1. 实现简单,代码简洁易懂
  2. 原地排序,空间复杂度低(O (1))
  3. 对部分有序的数据效率更高
  4. 性能优于其他简单排序算法(插入、选择、冒泡)

本文解析的实现代码使用 2h+1 增量序列,通过三层循环结构清晰地实现了希尔排序的核心逻辑。理解希尔排序的工作原理,不仅能帮助你在实际开发中选择合适的排序算法,更能启发你对算法优化思想的思考 —— 通过巧妙的策略调整,即使是简单算法也能获得显著的性能提升。

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

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

相关文章

Android 欧盟网络安全EN18031 要求对应的基本表格填写

Android 欧盟网络安全EN18031 要求对应的基本表格填写 文章目录Android 欧盟网络安全EN18031 要求对应的基本表格填写一、背景二、18031认证预填表格三、其他1、Android EN 18031 要求对应的基本表格小结2、EN 18031的要求表格内容填写3、一定要做三方认证&#xff1f;4、欧盟网…

《Attention-driven GUI Grounding》论文精读笔记

论文链接&#xff1a;[2412.10840] Attention-driven GUI Grounding: Leveraging Pretrained Multimodal Large Language Models without Fine-Tuning 摘要 近年来&#xff0c;多模态大型语言模型&#xff08;Multimodal Large Language Models&#xff0c;MLLMs&#xff09;的…

PIDGenRc函数中lpstrRpc的由来和InitializePidVariables函数的关系

第一部分&#xff1a;./base/ntsetup/syssetup/setupp.h:404:#define MAX_PID30_RPC 5BOOL InitializePidVariables() {//// Get the Pid from HKEY_LOCAL_MACHINE\SYSTEM\Setup\Pid//Error RegOpenKeyEx( HKEY_LOCAL_MACHINE,((MiniSetup || OobeSetup) ? szFinalPidKeyNa…

Nginx学习笔记(七)——Nginx负载均衡

⚖️ Nginx学习笔记&#xff08;七&#xff09;——Nginx负载均衡 &#x1f4cc; 一、负载均衡核心概念 架构定位&#xff1a; #mermaid-svg-00aCvwmJ40DHNd66 {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-00aC…

MQ积压如何处理

处理消息队列&#xff08;MQ&#xff09;积压是一个需要系统化分析的运维挑战。下面我将结合常见原因&#xff0c;分步骤说明处理方案&#xff0c;并区分应急措施和根本解决方案&#xff1a;​一、快速诊断积压原因&#xff08;核心&#xff01;&#xff09;​​​监控告警分析…

Unity与OpenGL中的材质系统详解

引言 在现代3D图形开发中&#xff0c;材质是定义物体外观的核心元素。Unity引擎提供了强大且直观的材质系统&#xff0c;使得开发者能够轻松实现复杂的视觉效果。然而&#xff0c;对于熟悉OpenGL的开发者来说&#xff0c;理解Unity材质系统的工作原理以及如何在OpenGL中实现类…

k8s安装DragonflyDB取代redis

数据库类型线程模型吞吐量 (QPS)延迟 (μs)内存效率适用场景兼容性Memcached纯内存键值存储多线程100K - 500K10 - 100高缓存、会话存储无原生密码认证DragonflyDB多协议内存数据库多线程1M50 - 200中高高吞吐缓存、Redis 替代兼容 RedisKeyDBRedis 多线程分支多线程500K - 1M5…

Horse3D游戏引擎研发笔记(五):在QtOpenGL环境下,仿three.js的BufferGeometry管理VAO和EBO绘制四边形

一、背景介绍 在三维图形渲染中&#xff0c;几何形状的管理是引擎的核心功能之一。Three.js通过BufferGeometry接口实现了对顶点数据和索引数据的高效管理&#xff0c;而OpenGL则通过顶点数组对象&#xff08;VAO&#xff09;和元素数组对象&#xff08;EBO&#xff09;来实现…

Ping32 与 IP-GUARD 深度对比:Ping32,引领企业数据安全新方向

在数字化时代&#xff0c;企业数据宛如珍贵的宝藏&#xff0c;是推动业务发展、保持竞争优势的核心资产。但与此同时&#xff0c;数据安全威胁也如影随形&#xff0c;内部员工的误操作、恶意窃取&#xff0c;外部黑客的攻击&#xff0c;都可能让企业数据面临泄露风险&#xff0…

洛谷 P2842 纸币问题 1 -普及-

题目描述 某国有 nnn 种纸币&#xff0c;每种纸币面额为 aia_iai​ 并且有无限张&#xff0c;现在要凑出 www 的金额&#xff0c;试问最少用多少张纸币可以凑出来&#xff1f; 输入格式 第一行两个整数 n,wn,wn,w&#xff0c;分别表示纸币的种数和要凑出的金额。 第二行一行 nn…

第十四节:物理引擎集成:Cannon.js入门

第十四节&#xff1a;物理引擎集成&#xff1a;Cannon.js入门 引言 物理引擎为3D世界注入真实感&#xff0c;让物体遵循重力、碰撞和动量等物理规律。Cannon.js是Three.js生态中最强大的物理引擎之一&#xff0c;本文将深入解析其核心机制&#xff0c;并通过Vue3实现物理沙盒系…

二十四、Mybatis-基础操作-删除(预编译SQL)

mybatis环境准备概述与注意事项&#xff08;springboot项目引入三项必要的起步依赖&#xff09;项目目录结构mybatis基础操作-删除对应EmpMapper&#xff08;接口&#xff09;代码 package com.itheima.mapper;import org.apache.ibatis.annotations.*;Mapper public interface…

JavaScript 核心基础:类型检测、DOM 操作与事件处理

JavaScript 作为松散类型语言&#xff0c;掌握类型检测规则、DOM 元素获取方式及事件处理逻辑&#xff0c;是写出健壮代码的基础。本文系统梳理 JS 高频基础知识点&#xff0c;结合实战场景解析原理与用法&#xff0c;帮你建立清晰的知识框架。 一、JS 数据类型与类型检测&…

软件开发过程中的维护活动

软件开发过程中的维护活动软件维护是软件生命周期中持续时间最长、成本最高的阶段&#xff0c;它并非简单的“修理”&#xff0c;而是一系列旨在延长软件生命周期、保持其价值和适应性的工程化活动。研究表明&#xff0c;软件维护成本可占总成本的60%以上。理解并有效管理维护活…

STC8单片机驱动I2C屏幕:实现时间、日期与温湿度显示

STC8 单片机驱动 I2C 屏幕&#xff1a;实现时间、日期与温湿度显示 在单片机项目中&#xff0c;“数据可视化” 是核心需求之一 —— 将时间、温湿度等关键信息实时显示在屏幕上&#xff0c;能让项目更具实用性。本文以STC8 系列单片机为核心&#xff0c;搭配 I2C 接口的 OLED…

基于SpringBoot+Vue的智能消费记账系统(AI问答、WebSocket即时通讯、Echarts图形化分析)

&#x1f388;系统亮点&#xff1a;AI问答、WebSocket即时通讯、Echarts图形化分析&#xff1b;一.系统开发工具与环境搭建1.系统设计开发工具后端使用Java编程语言的Spring boot框架 项目架构&#xff1a;B/S架构 运行环境&#xff1a;win10/win11、jdk17前端&#xff1a; 技术…

[论文笔记] WiscKey: Separating Keys from Values in SSD-Conscious Storage

阅读 WiscKey 论文时随手记录一些笔记。 这篇论文的核心思想理解起来还是很简单的&#xff0c;但是具体涉及到实现还有一些想不明白的地方&#xff0c;后来看到 TiKV 的 Titan 实现也很有趣&#xff0c;索性把这些问题都记录下来并抛出来。 本文中和论文相关的内容&#xff0…

week1-[循环嵌套]画正方形

week1-[循环嵌套]画正方形 题目描述 输入一个正整数 nnn&#xff0c;请使用数字 000 到 999 拼成一个这样的正方形图案&#xff08;参考样例输入输出&#xff09;&#xff1a;由上至下、由左至右依次由数字 000 到 999 填充。每次使用数字 999 填充后&#xff0c;将从头使用数字…

在 Vue2 中使用 pdf.js + pdf-lib 实现 PDF 预览、手写签名、文字批注与高保真导出

本文演示如何在前端&#xff08;Vue.js&#xff09;中结合 pdf.js、pdf-lib 与 Canvas 技术实现 PDF 预览、图片签名、手写批注、文字标注&#xff0c;并导出高保真 PDF。 先上demo截图&#xff0c;后续会附上代码仓库地址&#xff08;目前还有部分问题暂未进行优化&#xff0…

tomcat 定时重启

tomcat 定时重启 定时重启的目的是:修复内存泄漏等问题,tomcat 长时间未重启,导致页面卡顿,卡死,无法访问,影响用户访问 1.编写脚本 su - tomcat [tomcat@u1abomap02 ~]$ ls restart_tomcat_gosi.sh tomcat_gosi.log vi restart_tomcat_gosi.sh #!/bin/bash# 定义日志目…