【数据结构与算法】希尔排序

【数据结构与算法】希尔排序

👨‍💻 关于作者:会编程的土豆

“不是因为看见希望才坚持,而是坚持了才看见希望。”

你好,我是会编程的土豆,一名热爱后端技术的Java学习者。

📚 正在更新中的专栏:

💕作者简介:后端学习者

概念

希尔排序 = 插入排序 + 分组跳跃

它不是一次只和前面相邻的元素比,而是先隔着很远比,然后慢慢缩小距离,最后变成普通的插入排序

为什么需要希尔排序?

简单插入排序有个明显的软肋:当较小的数都堆在数组尾部时,排序效率会很低。因为插入排序每次只能交换相邻元素,要把尾部的小数挪到前面,需要一步一步“冒泡”过去,非常耗时。

看一下插入排序的代码:

public static void insertionSort(int[] arr) { int len = arr.length; for (int i = 1; i < len; i++) { int j = i; int temp = arr[j]; while (j > 0 && arr[j - 1] > temp) { //如果前一个元素比当前元素大,则将前一个元素向后移动一位。 //将 j 的值减1,继续比较前一个元素。 arr[j] = arr[j - 1]; j--; } arr[j] = temp; } } 

而希尔排序通过引入增量(Gap)的概念,允许元素跳跃式移动,让小数能更快地“蹦”到前面,从而解决了这一问题

希尔排序代码

void shellSort(vector<int>& arr) { int n = arr.size(); // 外层循环:控制 gap 从 n/2 开始,每次减半 for (int gap = n / 2; gap > 0; gap /= 2) { // 内层循环:对每个分组做插入排序 // 注意这里从 gap 开始,而不是从 1 开始 for (int i = gap; i < n; ++i) { int key = arr[i]; int j = i - gap; // 和前面相隔 gap 的元素比较 while (j >= 0 && arr[j] > key) { arr[j + gap] = arr[j]; // 大的往后挪 gap 个位置 j -= gap; // 往前跳 gap 个位置继续比 } arr[j + gap] = key; // 插入到正确位置 } } }

插入排序 vs 希尔排序 代码对比

插入排序希尔排序
for (int i = 1; i < n; ++i)for (int gap = n/2; gap > 0; gap/=2)
for (int i = gap; i < n; ++i)
int j = i - 1;int j = i - gap;
while (j >= 0 && arr[j] > key)while (j >= 0 && arr[j] > key)
arr[j + 1] = arr[j];arr[j + gap] = arr[j];
j -= 1;j -= gap;
arr[j + 1] = key;arr[j + gap] = key;

唯一的区别:把插入排序里的 1 全部换成了 gap,再套一层 gap 递减的循环。

复杂度与特点

特性说明
时间复杂度取决于 gap 序列,经典希尔增量 O(n²),优化序列可达 O(n^1.3)
空间复杂度O(1),原地排序
稳定性不稳定(跳跃式交换可能打乱相同元素的顺序)
适用场景中等规模数据,嵌入式设备等内存敏感场景

一句话总结

希尔排序 = 带间隔的插入排序,通过"大步跳跃 + 逐步逼近"来优化小数后移的问题。

希尔排序的核心原理

核心概念:增量(Gap)

希尔排序引入了一个间隔 gap,它不是让元素和相邻的比,而是和相隔 gap 个位置的元素比。

过程分三步:

  1. 分组:按 gap 把数组分成若干组
  2. 组内插入排序:对每组分别做插入排序
  3. 缩小 gap:gap 减半,重复上述过程,直到 gap = 1

当 gap = 1 时,就是普通的插入排序,但此时数组已经基本有序,插入排序会非常快。

图解演示

数组:[80, 93, 60, 12, 42, 30, 68, 85, 10]

第一轮:gap = 4

把相隔 4 个位置的元素分为一组:

下标: 0 1 2 3 4 5 6 7 8 值: 80 93 60 12 42 30 68 85 10 分组情况(相同标记的为一组): 组1 (▲): 80, 42, 10 → 下标 0, 4, 8 组2 (●): 93, 30 → 下标 1, 5 组3 (■): 60, 68 → 下标 2, 6 组4 (◆): 12, 85 → 下标 3, 7

对每组分别做插入排序:

  • 组1 排序后:10, 42, 80
  • 组2 排序后:30, 93
  • 组3 排序后:60, 68
  • 组4 排序后:12, 85

放回原数组:

排序前: [80, 93, 60, 12, 42, 30, 68, 85, 10] 排序后: [10, 30, 60, 12, 42, 93, 68, 85, 80]

观察:最小的 10 从最后一位(下标 8)跳到了第一位(下标 0)!一步跨了 8 个位置,这就是希尔排序的效率来源。


第二轮:gap = 2

当前: [10, 30, 60, 12, 42, 93, 68, 85, 80] 分组: 组1 (▲): 10, 60, 42, 68, 80 → 下标 0,2,4,6,8 组2 (●): 30, 12, 93, 85 → 下标 1,3,5,7

分别插入排序后放回:

排序前: [10, 30, 60, 12, 42, 93, 68, 85, 80] 排序后: [10, 12, 42, 30, 60, 85, 68, 93, 80]

第三轮:gap = 1

就是普通的插入排序:

排序前: [10, 12, 42, 30, 60, 85, 68, 93, 80] 排序后: [10, 12, 30, 42, 60, 68, 80, 85, 93]

此时数组已经基本有序,插入排序只需要很少的移动就能完成。

注意排序时:

每一组元素只在属于自己组的下标位置之间进行排序和交换,完全不碰其他组的元素

说人话就是:比如说这一组数,就是在原来数组的这一组的基础之上排序换位置,而其他组的位置丝毫不影响,只在自己的那个gap组里面换;

Read more

最新更新版本,OpenClaw v2026.4.2 深度解读剖析:Task Flow 重磅回归与安全架构的全面硬化

最新更新版本,OpenClaw v2026.4.2 深度解读剖析:Task Flow 重磅回归与安全架构的全面硬化

文档版本:v1.0 分析基准日期:2026年4月3日 字数统计:约20,000字 分析维度:架构演进、功能解析、安全机制、生态影响、升级指南、未来展望 第一章:版本总览——一次功能与安全并重的里程碑式更新 1.1 发布背景与战略定位 2026年4月3日,OpenClaw 正式发布 v2026.4.2 版本。这并非一次常规的迭代更新,而是在经历了2026年3月一系列架构大手术(v2026.3.7 的 ContextEngine 插件化、v2026.3.31 的核心架构重塑)之后,项目进入**"能力回归与安全硬化"**阶段的关键里程碑。 从版本号演进来看,v2026.4.2

Python + Selenium + AI 智能爬虫:自动识别反爬与数据提取

Python + Selenium + AI 智能爬虫:自动识别反爬与数据提取

结合 Selenium 浏览器自动化与 AI 大模型能力,构建能够自动识别反爬机制、智能解析页面的新一代爬虫系统。 1. 系统架构 验证码 登录墙 正常页面 种子 URL 队列 调度器 Selenium WebDriver 反检测模块 页面渲染 AI 反爬识别 AI 验证码破解 自动登录 AI 数据提取 数据清洗管道 存储 MongoDB / CSV 数据看板 2. 反爬机制分布 35%25%20%10%7%3%常见反爬机制占比(Top 500 网站统计)JS 动态渲染请求频率限制验证码(图形/滑块)User-Agent 检测IP

014、文本到图像生成:CLIP引导与潜在对齐

一、从一次深夜调试说起 上周在复现一个文本到图像的生成实验时,遇到了一个典型问题:模型生成的图像看起来“还行”,但总感觉和输入文本差了那么点意思。比如输入“一只戴着墨镜的柴犬在沙滩上晒太阳”,出来的图像柴犬倒是像,但墨镜时有时无,沙滩背景也经常混入奇怪的植被。损失函数在下降,指标看着也正常,但就是不对劲。 这种“不对劲”往往不是模型结构的问题,而是文本和图像两个模态的“对齐”没做好。今天要聊的CLIP引导和潜在对齐,就是解决这个问题的关键思路。 二、CLIP为什么能成为“翻译官” CLIP(Contrastive Language-Image Pre-training)本身是一个多模态模型,它的训练方式很巧妙:让模型学会判断哪些文本和哪些图像是配对的。它不生成任何东西,只做“匹配判断”。这个特性让它成了文本和图像之间的一个高质量“翻译官”。 在扩散模型中引入CLIP,核心目的是用CLIP的跨模态理解能力,来约束图像生成过程,让生成的图像在语义上更贴近文本描述。这里常见的做法是在扩散过程的采样阶段,用CLIP的文本编码和图像编码计算相似度,作为额外的引导信号。 三、CLI

每日两道力扣,day6

每日两道力扣,day6

每日两道力扣,day6 每日两道力扣,day6 每日两道力扣,今天是: 11. 盛最多水的容器 - 力扣(LeetCode) 15. 三数之和 - 力扣(LeetCode) 第一题:盛最多水的容器 11. 盛最多水的容器 - 力扣(LeetCode) 1.思路: 在写这个题之前,咱们需要了解一个经典原理——木桶效应。 显然,在相同底面积的情况下,木桶盛水的最大值,由最短的那块板决定。这个题很明显是双指针算法的应用场景。因为这个题目给出的是一个平面切割图,咱们定义left,right左右两个指针。底面积S = right - left。高度应是min(height[left],height[right]),所以体积v就是这二者的乘积。观察题目给的示例图,当height[left] <