赞
踩
希尔排序是插入排序的一种改进版本。
可以看做是一种分组的插入排序,分组的变化受小组尾索引 i i i 和分组步长 s t e p step step控制,分好组后,对每个小组的元素执行插入排序即可。
时间复杂度分析:(希尔排序的时间性能优于插入排序的原因:)
当数组初态基本有序时直接插入排序所需的比较和移动次数都较少。
当n值较小时,n和n2的差别也较小,即直接插入排序的最好时间复杂度O(N)和最坏时间复杂度O(N2)差别不大。
在希尔排序刚开始元素很无序的时候,步长step较大,分组较多,每组的元素数目少,所以组内插入排序速度很快。后面当元素基本有序了,步长step较小,虽然分组数减少,每组的元素数量多,但由于数组较接近于有序状态,所以新的一趟插入排序效率较高。
稳定性:
一次插入排序是稳定的,不会改变相同元素的相对顺序,但在不同组的插入排序中,相同元素可能在各自的插入排序中移动,最后稳定性就会被打乱,所以希尔排序是不稳定的。
应用:
希尔算法在最坏的情况下和平均情况下执行效率相差不是很多,与此同时快速排序在最坏的情况下执行的效率会非常差。因此可以在几乎任何排序工作刚开始时,先使用希尔排序,若在实际使用中证明它不够快,再改成快速排序这样更高级的排序算法。
代码如下:
public class Main {
public static void main(String[] args) {
int[] arr = {3, 3, 5, 6, 2, 1};
System.out.print("排序前:");
arrPrint(arr);
ShellSort(arr);
System.out.print("排序后:");
arrPrint(arr);
}
// 希尔排序
// 希尔排序是插入排序的改进版,意为将数组arr按照递减的步长step分组,
// 同组的元素距离相邻一个step的长度,小组的尾索引记为i,每组分别使用插入排序,
// 每次排序完步长step将按照减半来递减,构成新分组,再每组分别进行插入排序,
// 以此循环下去,直至step=1,分组到最后只剩1组,小组尾索引i不断右移,即遍历完数组arr
//
// 实例中step将分别等于3,1。(想看过程可以利用arrPrint函数将中间过程打印)
// 第一层for循环将step初始化为数组长度的一半,后循环减半,
// 第二层for循环,锚定分组后每组的尾索引为i=j+step。由于同组元素距离为一个step,
// 所以同组的元素可以通过j循环减step获得。
public static void ShellSort(int[] arr) {
int len = arr.length;
for (int step = len / 2; step >= 1; step /= 2) {
GroupInsertSort(arr, len, step);
}
}
// (分组)插入排序
// 还记得原来插入排序中i是从1开始的,就相当于分组后的i是从step开始的。
// 遍历数为arr[i]=temp,这也就是待排序元素。
// j = i - step为同组的i前一个元素索引,根据插入排序算法,
// 如果j = i - step索引的元素arr[i]大于i索引的元素temp,
// 则在同组内用while循环将大于temp的左边元素全部右移。
// 注意指针要移动一个step的距离。
private static void GroupInsertSort(int[] arr, int len, int step) {
for (int i = step; i < len; i++) {
int j = i - step;
int temp = arr[j + step];
while (j >= 0 && arr[j] > temp) {
arr[j + step] = arr[j];
j -= step;
}
if (j != i - step)
arr[j + step] = temp;
}
}
// 辅助函数:将int[] 打印出来
private static void arrPrint(int[] arr) {
StringBuilder str = new StringBuilder();
str.append("[");
for (int v : arr) {
str.append(v + ", ");
}
str.delete(str.length() - 2, str.length());
str.append("]");
System.out.println(str.toString());
}
}
实例的动画演示如下:
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。