Hibbard增量序列的概念理解:
- 首先什么Hibbard增量序列长什么样?
例如:1,3,7,15,……,2^n-1,……
一般情况Hibbard应用在希尔排序里面,达到的时间效率更趋于稳定。
在希尔排序中使用Hibbard增量序列时,通常从一个较小的 k 值开始,使得增量尽可能大但又不超过数组长度的一定比例(如不超过数组长度的一半):
效率方面
- 大步长快速移动元素
- 从一个较小的 k 值开始生成较大的增量,能让元素在排序初期进行大幅度的移动。例如,当增量较大时,如 2k−1 中 k 较大时对应的增量值,数组中相距较远的元素能够直接进行比较和交换。这有助于快速地将那些明显较大或较小的元素移动到数组的大致正确位置,减少后续排序的工作量。例如在一个长度为100的数组中,初始增量如果是31(对应 k=5,因为 25−1=31),那么可以快速调整相距31个位置的元素顺序,使数组整体更接近有序状态。
- 小步长精细调整
- 随着排序的进行,增量逐渐减小。在接近排序完成时,小的增量能够对数组进行精细的调整。当增量最终减小到1时,实际上就是进行普通的插入排序。由于前期大增量的处理已经使数组基本有序,此时插入排序能高效地完成最后的排序工作。如果一开始就使用非常小的增量,元素移动的幅度很小,排序效率会很低,而一开始就使用过大的增量可能导致无法有效处理局部的无序情况。
算法稳定性方面
- 避免过度打乱
- 如果增量过大,超过数组长度的一定比例(如超过一半),可能会过度打乱数组元素之间的相对顺序。这可能破坏一些已经部分有序的子结构,使得后续的排序工作更加困难。 (后面还会再次提到)例如,假设数组前半部分已经相对有序,如果增量过大,会将前半部分和后半部分元素频繁交换,导致前半部分的有序性被破坏,增加了排序的复杂性。
- 保证收敛性
- 限制增量不超过数组长度的一定比例,有助于保证希尔排序算法的收敛性。如果增量过大且不加以限制,算法可能无法在有限的步骤内收敛到一个有序的数组状态。通过合理控制增量的大小,随着增量逐渐减小,数组能够逐步地向有序状态逼近,最终实现完全排序。
内存和性能平衡方面
- 减少不必要的比较和移动
- 过大的增量可能导致在某些情况下进行大量不必要的比较和移动操作。例如,如果增量接近数组长度,可能会对数组中很多元素进行无效的比较,因为这些元素在当前阶段并不需要进行排序操作。控制增量大小可以在保证排序效果的同时,减少不必要的计算资源消耗,提高算法的整体性能。
void shell_sort_hibbard(int *arr,int l,int r){
int step=1,n=r-l;
while(step*2+1<=n/2) step=step*2+1;
do{
for(int i=l,I=l+step;i<I;i++){
insert(arr,i,r,step);
}
step/=2;
}while(step>1);
return;
}
为什么要进行第一步的while循环,为什么要将step不断地进行step=step*2+1,而且,step*2+1,为什么要小于n/2 ?
——为什么进行第一步while循环,而且要将step不断地进行step=step*2+1:
1、首先是为了找到适合原来数据量n的Hibbard最大的值,比如:
-
举例说明(以无序序列
[64, 34, 25, 12, 22, 11, 90]为例)- 确定初始增量:
- 首先,计算合适的初始增量。假设数组长度 n=7。从增量 step=1 开始,根据规则
while (step * 2 + 1 <= n / 2)来增大增量。 - 当 step=1 时,step∗2+1=3,3<=7/2=3.5,所以 step 更新为 step=step∗2+1=3。
- 当 step=3 时,step∗2+1=7,7>7/2=3.5,此时初始增量确定为 step=3。
- 首先,计算合适的初始增量。假设数组长度 n=7。从增量 step=1 开始,根据规则
- 基于增量进行分组插入排序(增量 step=3):
- 数组被分成 3 个组:
- 第一组:arr[0],arr[3],arr[6],即 [64,12,90]。
- 第二组:arr[1],arr[4],即 [34,22]。
- 第三组:arr[2],arr[5],即 [25,11]。
- 对每个组进行插入排序:
- 第一组 [64,12,90] 排序后变为 [12,64,90]。
- 第二组 [34,22] 排序后变为 [22,34]。
- 第三组 [25,11] 排序后变为 [11,25]。
- 此时数组变为 [12,22,11,64,34,25,90]。
- 数组被分成 3 个组:
- 减小增量并重复排序(增量 step=1):
- 增量 step 减小为 step/=2=1。此时进行普通的插入排序。
- 插入排序从第二个元素开始,依次将每个元素插入到已排序的子数组中的合适位置。
- 经过插入排序后,数组最终变为 [11,12,22,25,34,64,90],完成排序。
- 确定初始增量:
——为什么step/=2 :
分析 step /= 2 的原因
-
- 在生成初始增量时,通过
while(step * 2 + 1 <= n / 2) step = step * 2 + 1;来得到合适的初始增量,这个过程是按照Hibbard增量序列的生成方式进行的。当要减小增量时,为了保持序列特性,需要将当前的增量 step 除以 2。因为 step=2k−1,当 k 减小时,新的增量就是原来增量除以 2 左右(例如 15÷2=7.5,向下取整为 7,符合 23−1=7 到 24−1=15 的逆过程)。所以在do - while循环中使用step /= 2可以正确地生成递减的Hibbard增量序列。
重点在你一开始while循环,内部构造最大的step值时,采用的step = step * 2 + 1,就是一个奇数,所以当最大步距为step=15时,后面的step/=2,使用的就是15/2=7.5,向下取整就是7,同理取到3和1,符合Hibbard增量序列,进而完成希尔排序。
- 在生成初始增量时,通过
——为什么要小于n/2:
因为让step的步距分配合理,不让它直接把1号元素和末尾元素直接分派,避免 :增量过大,超过数组长度的一定比例(如超过一半),可能会过度打乱数组元素之间的相对顺序。这可能破坏一些已经部分有序的子结构,使得后续的排序工作更加困难。

被折叠的 条评论
为什么被折叠?



