高级排序算法之希尔排序
Posted alonwang
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了高级排序算法之希尔排序相关的知识,希望对你有一定的参考价值。
前言
希尔排序是对插入排序的改进,引入维基百科的说明:
希尔排序是基于插入排序的以下两点性质而提出改进方法的:
-
插入排序在对几乎已经排好序的数据操作时,效率高,即可以达到线性排序的效率
-
但插入排序一般来说是低效的,因为插入排序每次只能将数据移动一位
原文链接希尔排序,本文将介绍希尔排序的实现思路,时间复杂度,稳定性等.
正文
希尔排序
主要思路
要改进插入排序,就要解决"插入排序每次只能将数据移动一位",并且利用"插入排序在几乎已经排好序的数据操作时,效率高"的特性.逻辑描述如下
1. 初始时将数组划分为subArrsNum=arr.length/2个子数组
2. 对这些子数组subArrs使用插入排序排序
3. subArrsNum缩减为一半,缩减后如果subArrsNum>=1,跳转到2,否则 结束.
subArrs的定义如下
subArrs[i]={arr[i],arr[i+k],arr[i+2k],...arr[i+nk]} i<k,arr.length/2 >= k >=1
这里的难点在于对逻辑子数组的理解,下面结合subArrs的定义,举例说明
arr={1,4,6,5,2,7,3,8}
初始时subArrsNum = arr.length/2=8/2=4,四个子数组分别为
subArrs[0]={1,2}={arr[0],arr[0+4]}
subArrs[1]={4,7}={arr[1],arr[1+4]}
subArrs[2]={6,3}={arr[2],arr[2+4]}
subArrs[3]={5,8}={arr[3],arr[3+4]}
如果我们想遍历某个子数组,跨度应该等同于subArrsNum而非1.达成"每次可以将数据移动不止一位"的效果",
而随着子数组数量越来越少,数组也越来越有序,更好的利用"插入排序在几乎已经排好序的数据操作时,效率高"这个特性,代码如下
public void sort(Comparable[] arr) {
if (arr.length <= 1) {
return;
}
for (int subArrsNum = arr.length / 2; subArrsNum >= 1; subArrsNum /= 2) {
//处理每个子数组
for (int secondElePos = subArrsNum; secondElePos < 2 * subArrsNum; secondElePos++) {
for (int elePos = secondElePos; elePos < arr.length; elePos += subArrsNum) {
int toInsertELePos = elePos;
Comparable toInsertEleVal = arr[elePos];
for (int orderedElePos = elePos - subArrsNum; orderedElePos >= 0; orderedElePos -= subArrsNum) {
if (toInsertEleVal.compareTo(arr[orderedElePos]) < 0) {
arr[orderedElePos + subArrsNum] = arr[orderedElePos];
toInsertELePos = orderedElePos;
} else {
toInsertELePos = orderedElePos + subArrsNum;
break;
}
}
arr[toInsertELePos] = toInsertEleVal;
}
}
}
}
时间复杂度
最坏情况下优于O(n^2),详见 希尔排序&选择排序&时间复杂度分析
是原地排序吗
是的,希尔排序不需要使用额外空间
稳定吗
不稳定,插入排序是稳定的,但是希尔排序不是,划分子数组后,是对各个子数组分别进行插入排序,这时可能两个相同的元素在不同的子数组的排序位置是不同的,例如[1,4,3,3,6,5],一次排序后为[1,3,3,4,6,5],"3"的相对位置发生了变化
源码地址github
以上是关于高级排序算法之希尔排序的主要内容,如果未能解决你的问题,请参考以下文章