一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第14天,点击查看活动详情。
希尔排序
希尔排序是插入排序的升级版。插入排序一步步对数据进行比较+平移+插入,希尔排序就是为解决对于多余移动进行优化的一种算法形式。
| 索引0 | 索引1 | 索引2 | 索引3 | 索引4 | 索引5 | 索引6 | 索引7 | 索引8 | 索引9 |
|---|---|---|---|---|---|---|---|---|---|
| 7 | 6 | 3 | 9 | 1 | 4 | 5 | 2 | 0 | 8 |
- 首先对数组进行分组操作:10个数 / 2 = 5。将数组分为5组。【7,4】【6,5】【3,2】【9,0】【1,8】
- 然后对五组分别进行插入排序操作,将小的数据放在各自组的前面【4,7】【5,6】【2,3】【0,9】【1,8】
- 接着继续分组 5/2 = 2,将数组分为2组。【4,5,2,0,1】【7,6,3,9,8】
- 重复2的操作进行插入排序。【0,1,2,4,5】【3,6,7,9,8】
- 接着分组 2 / 2 = 1,将数组分为1组。【0,1,2,4,5,3,6,7,9,8】
- 最后在执行一次插入排序就完成整个过程。(以上分组示意不代表当前排序)
原理
希尔排序将数组进行分组操作,对每个组进行插入排序算法,紧接着对分组数不断降低最终分组变为一组后排序就完成了。
- 如之上所示对数组分组,例如组数为5那么
索引0值7就对应了索引5(0+5)4。
- 执行插入排序
- 接着分组为2,
索引0值4就对应了索引2(0+2)2。
- 再执行一次插入排序
- 最后在执行一次插入排序
算法代码
交换方式
gap表示分组数。i表示当前是第几组。j表示每个组里的最小下标
void main(){
int[] datas;
int length = datas.length(); // 长度
for(int gap = datas.length()/2;gap > 0;gap /=2){ // 分组数进行循环
for(int i = gap;i<datas.length;i++){ // 每个分组的循环
int j = i - gap; // 每个分组起始下标
while(j >= 0 && datas[j] > datas[j+gap]){ // 插入排序算法
int temp = datas[j];
datas[j] = datas[j+gap];
datas[j+gap] = temp;
j -= gap;
}
}
}
}
移位方式
gap表示分组数,组数越少一组内的数据就越多。
- 第一个for循环表示分组大小,循环中对数组减半处理,直到分组大小为1
- 第二个for循环表示每个分组的循环,gap值分组中第一组第二个数据。
- while循环执行插入排序,比较中比插入数据大就执行交换数据。
void main(){
int[] datas;
for(int gap = datas.length()/2;gap > 0;gap /=2){ // 分组数进行循环
for(int i = gap;i<datas.length;i++){ // 每个分组的循环
int temp = datas[i]; // 每个分组最大值
int index = i - gap; // 分组起始下标
int newIndex = index; // 开始的下标
while(index >= 0 && temp < datas[index]){// 插入排序算法
datas[index + gap] = datas[index];
index -= gap;
}
if(index != newIndex){
datas[index + gap] = temp;
}
}
}
}
推导得出大O记法是O(),若经过算法优化增量序列例如Hibbard最坏情况会是是O(N ^ (3/2) )
总结
希尔排序比插入排序更快,基于插入排序算法目的解决是解决了当排序数组待续需要插入排序没必要的操作。其核心算法就是先将待排序数组分组处理小数据排序,但最后分组越小时数组基本上大致有序。最后在执行插入排序可以减少移动和交换次数,提升了排序效率。