响应群众需求,今天安排上「希尔排序」。
阅读本文前,请先服用「插入排序」,效果更加。
Content
- 实现过程
- 关键思路
- 代码实现
- 结合代码和中间过程,再体会
- 算法评价
- 「希尔排序」🆚 「插入排序」
- 听说有彩蛋❓
👀「更多排序」
《冒泡排序 | 通俗易懂的理解排序过程》
《选择排序 | 通俗易懂的理解排序过程》
《快速排序 | 通俗易懂的理解排序过程》
《插入排序 | 通俗易懂的理解排序过程》
1. 实现过程
对[5,4,1,3,2]
升序排列,中间步骤 ⬇️
2. 关键思路
「希尔排序」—— 按固定间隔划分子序列,对每个子序列实现简单插入排序。
这么来看,「希尔排序」可以拆解成两大步骤 :
- 划分子序列;
- 在子序列中简单插入排序。
Q:怎么划分子序列?
- 等间隔划分。本例中,初始下标之差按照
d= n//2
计算,之后d = d//2
循环更新。 - 先做远距离的移动,再逐渐缩小间隔。这样,可以减少移动的次数,因为极端情况,间隔为1时即退化为简单插入排序。
Q:简单插入排序怎么操作?
- 简单概括 —— 比较、后移、腾位置、插入target。没看过简单插入排序的,点这里。
3. 代码实现
def ShellSort(arr):
def ssort(arr, d):
n = len(arr)
for i in range(d, n):
j = i-d # 被比较数下标
target = arr[i] # target
while j>=0 and arr[j] > target: # 比较、后移、腾位置
arr[j+d] = arr[j]
j -= d
if j != i-d:
arr[j+d] = target # 插入
n = len(arr)
if n <= 1:
return arr
d = n//2 # 子序列的初始下标之差
while d >= 1:
ssort(arr, d)
d = d//2 # 循环更新
return arr
4. 结合代码和中间过程,再体会
5. 评价
- 算法是不稳定的,因为跳跃性插入;
- 平均时间复杂度 O(nlogn);(不理解,待解释~)
- 额外空间开销出在插入过程数据移动需要的一个暂存,空间复杂度O(1)。
6. 「希尔排序」🆚 「简单插入排序」
- 联系
「希尔排序」在划分子序列后,对每个子序列实现简单插入排序。
- 区别
「希尔排序」优化了「简单插入排序」,移动次数更少。
同样以 [5,4,1,3,2]
升序排列为例,「简单插入排序」中共移动了8次,「希尔排序」中共移动了4次。
Q:移动次数,节省再哪里了?效率是如何提高的呢?
Example:
从[5,4,1,3,2]
调整成 [1,4,5,3,2]
, 两者的中间过程是如何的?(过程截图于完整的实现过程)
- 「简单插入排序」
因为是挨个比较、移动、腾位置,每次插入都要移动大量数据。
- 「希尔排序」
因为实现了跨越的(等间隔)的比较、移动、腾位置,可以一步到位。
不过,如果序列基本有序,简单插入排序就不必做很多移动操作,此时效率还是很高的。所以,「希尔排序」先做远距离移动使序列基本有序,再不断缩小划分间隔,直至最后为间隔为1时的简单插入排序。
7. Good things will come ~
你不知道收到这张卡片的我当时内心翻腾得有多厉害,暖流从心底涌上,直接泪崩。
我和舍友把它贴在门后,每每需要力量的时候,就再读一遍,'Good things will come’ 是我们所有的信念。
想起一句话 —— 「所有的事情最后都会是好事,如果现在没有,说明还没到最后」。
最后,在哪儿呢?
来源:https://blog.csdn.net/weixin_44915703/article/details/100543920