从“理扑克牌”到“分组优化”——直接插入排序与希尔排序详解

一、引言

排序(Sorting)是计算机科学中最基础、应用最广的算法之一。

想想这些场景:

  • 从通讯录里找一个人 → 按姓氏排序后,二分查找秒到

  • 淘宝按价格从低到高筛选商品 → 价格排序

  • 考试后按分数排名 → 成绩排序

如果没有排序,这些操作都会变得极其低效。

本篇文章是排序系列的第一篇 ,我会带你从最简单、最直观的直接插入排序 开始,然后逐步递进到它的升级版------希尔排序

学完这篇,你不仅能掌握这两种排序的实现,还能理解一个非常重要的算法设计思想:如何从朴素的思路出发,通过"预排序"来优化性能

二、直接插入排序

核心思想

直接插入排序的核心思想,可以用一个生活场景完美解释------理扑克牌

当你打牌时,会一张一张摸牌,每摸到一张新牌,你会从右向左跟手里已有的牌比较,找到合适的位置插进去,始终保持手里的牌是有序的。

  • 将待排序的数据分为"已排序区间"和"未排序区间"。每次从未排序区间取出第一个元素,插入到已排序区间中的正确位置,使已排序区间仍然保持有序。

下面我们来画图理解

理解了直接插入排序的思想之后,我们来用代码实现一下

cpp 复制代码
void InsertSort(int* arr, int n)
{
	for (int i = 0;i < n - 1;i++)
	{
		int end = i;
		int tmp = arr[end + 1];
		while (end >= 0)
		{
			if (arr[end] > tmp)
			{
				arr[end + 1] = arr[end];
				end--;
			}
			else
			{
				break;
			}
		}
		arr[end + 1] = tmp;
	}
}

接下来我们来分析一下直接插入排序的时间复杂度:

情况 复杂度 说明
最好(数组已经有序) O(n) 每个元素只比较一次,不移动
最坏(数组逆序) O(n²) 第 i 个元素需要比较 i 次,总次数 = 1+2+...+(n-1) ≈ n²/2
平均 O(n²) 随机数据,平均比较和移动约 n²/4

平均O(n²)的时间复杂度太高了,面对无序且大量的数据,直接插入排序的效率很低,因此在此排序思想的基础上,又演变出了一种更加高效的排序方法:希尔排序

三、希尔排序

核心思想

希尔排序也叫**"缩小增量排序"**(Diminishing Increment Sort)。它的核心思想是:

先进行"预排序",让数组变得"基本有序",最后再进行一次直接插入排序。

怎么预排序?------按间隔分组

例如图中的一组数据,我们以 gap = 4 的间隔来分组,可以分为四个组。

对每一组分别进行插入排序 。这样,元素就可以一次跨越 gap 步到达相对正确的位置。

预排序完成后,缩小 gap,继续分组排序......直到 gap = 1,此时就是标准的直接插入排序。但因为数组已经基本有序,插入排序的效率很高(接近 O(n))。

对于 gap,我们通常让 gap = gap / 3 + 1

下面是希尔排序的代码实现

cpp 复制代码
void ShellSort(int* arr, int n)
{
	int gap = n;
	while (gap > 1)
	{
		gap = gap / 3 + 1;
		for (int i = 0;i < n - gap;i++)
		{
			int end = i;
			int tmp = arr[end + gap];
			while (end >= 0)
			{
				if (arr[end] > tmp)
				{
					arr[end + gap] = arr[end];
					end -= gap;
				}
				else
				{
					break;
				}
			}
			arr[end + gap] = tmp;
		}
	}
}

时间复杂度的分析:

情况 复杂度 说明
最好 O(n log n) 数据基本有序时
最坏 O(n²) 取决于 gap 序列,某些序列可避免 O(n²)
平均 O(n^1.3 ~ n^1.5) 远优于 O(n²)

由此可见,在优化后的希尔排序比优化之前的直接插入排序时间复杂度要小很多,这就是这种预排序思想所带来的好处

五、总结

一句话记住这两种排序:

直接插入排序 :像理扑克牌,一张一张插入到正确位置。

希尔排序:先分组排序让数组"大概有序",再最后插入排序收尾。

建议:

  1. 先熟练掌握直接插入排序

  2. 理解希尔排序的"预排序 + 最终插入"思想------这是重要的算法设计思路

  3. 一定要手写一遍代码,感受两种排序的异同

相关推荐
起个破名想半天了2 小时前
算法与数据结构之DFS深度优先遍历
数据结构·算法·深度优先
李伟_Li慢慢2 小时前
凸包算法
人工智能·算法
徐礼昭|商派软件市场负责人2 小时前
AI Agent 蜂群协作与经验基因化:从单体执行到自组织系统的工程路径
大数据·人工智能·算法·智能体·多agent
来者皆善2 小时前
【位RAW图像处理五】任意位深位图像的中值模糊快速实现及其应用。
图像处理·人工智能·算法
happyprince2 小时前
第三观 · 深刻不忘观 —— AngelSpec 哲学与升华
人工智能·算法
鱼子星_2 小时前
【C++】深入剖析list:list及其双向迭代器实现
开发语言·数据结构·c++·笔记·stl·list
脚踏实地皮皮晨2 小时前
003002004_WPF Panel 基类 官方类定义
开发语言·windows·算法·c#·wpf·visual studio
啊真真真3 小时前
ArgoCD:我的GitOps探索之旅与未来展望
java·算法·argocd
江屿风3 小时前
【C++笔记】【二叉搜索树】流食般投喂
开发语言·数据结构·c++·笔记