希尔排序详解
1、希尔排序思想
- 希尔排序是通过设置间隔,而对于每个间隔上的元素再进行插入排序,一趟排序后,间隔变小,再继续对此次间隔上的元素进行插入排序,直到间隔为1,此时就是一次完整的直接插入排序。(直接插入排序可以看我另一篇博客)
- 这里的代码和图解都是降序。
2、排序过程图解
-
间隔为3时的部分图解:每趟都是间隔为3的元素之间的一次直接插入排序。其中
i
控制end
的走法,end
和gap
控制每次排序的走法。
-
间隔为1时的部分图解:每趟都是间隔为1的元素之间的一次直接插入排序。
3、排序代码
-
方法一:这里
j
控制i
,i
控制end
。第一层for
循环控制i
走到gap-1
,即使得下面j
能访问到所有元素。第二层for
循环用来对end
从j
开始到n-gap-1
间隔为gap
的所有排序。//希尔排序 void ShellSort(int *arr, int n) { int gap = n; while (gap > 1) { gap = gap / 3 + 1; //这里控制每次gap递减 for (int j = 0; j < gap; ++j) { //这里控制 i 走到 gap-1 for (int i = j; i < n - gap; i += gap) {//一趟排序把所有这次gap的元素排序好 int end = i; int tmp = arr[end + gap]; while (end >= 0) { if (tmp < arr[end]) { arr[end + gap] = arr[end]; } else { break; } end -= gap; } arr[end + gap] = tmp; } } } }
-
方法二:这里代码就和上面的图解思想一样,我们注意到这里
gap
每次减少是用这个gap = gap / 3 + 1
表达式,这个递减方法的效率相对于其他表达式要更高。如下图示。- 其中,希尔排序的时间复杂度是O(n^1.3),这是官方的解释。
//希尔排序 void ShellSort(int *arr, int n) { int gap = n; while (gap > 1) { gap = gap / 3 + 1; for (int i = 0; i < n - gap; i += gap) { int end = i; int tmp = arr[end + gap]; while (end >= 0) { if (tmp < arr[end]) { arr[end + gap] = arr[end]; } else { break; } end -= gap; } arr[end+gap] = tmp; } } }
递减表达式
gap = gap / 3 + 1
处理1亿条数据的时间:递减表达式
gap = gap / 2
处理1亿条数据的时间: -
方法三:这个方法可以看到,只有一处与上处代码不一样,也就是for循环里面分别为
i + = gap
和i++
。但是这个方法和上面方法的效率是一样的。排序图解下面浅浅的展示一下。//希尔排序 void ShellSort(int *arr, int n) { int gap = n; while (gap > 1) { gap = gap / 3 + 1; //预排序 for (int i = 0; i < n - gap; i++) { int end = i; int tmp = arr[end + gap]; while (end >= 0) { if (tmp < arr[end]) { arr[end + gap] = arr[end]; } else { break; } end -= gap; } arr[end + gap] = tmp; } } }
此处仅演示
gap = 3
的部分情况排序,gap = 1
的排序情况自行思考。- 时间复杂度计算:O(n^1.3) —>官方解释
- 空间复杂度计算:由于没有开辟额外空间来辅助数组排序,故空间复杂度为O(1)。
- 算法稳定性:不稳定,考虑序列(2,2,1),排序后序列为(1,2,2),我们发现
2
的相对位置发生了变化,所以是不稳定的排序算法。
ok,希尔排序算法就到这里。下面是我的github主页,里面记录了我的学习代码和leetcode的一些题的题解,有兴趣的可以看看。