0


八大排序算法--希尔排序(动图理解)

希尔排序

概念

希尔排序是插入排序的一种,是对直接插入排序的优化。其特点在于分组排序。

算法思路

希尔排序是按照其设计者希尔的名字命名的,他对插入排序的效率进行了分析,得出如下结论:

  1. 1.在最坏情况下即待排序序列为逆序时,需要消耗O(n^2)的时间
  2. 2.在最好情况下即待排序序列为顺序时,需要消耗O(n)的时间

于是希尔就想:若是能先将待排序序列进行一次预排序,使待排序序列接近有序,然后再对该序列进行一次插入排序。因此此时直接插入排序的时间复杂度为O(n),那么只需控制预排序阶段的时间复杂度小于O(n^2)那么整体的时间复杂度就比插入排序的时间复杂度低了。

那具体的预排序应该怎么做才会时间复杂度满足要求呢?

  1. 1.先选定一个小于n的整数gap(一般情况下是将n/2作为gap)作为第一增量,然后将所有距离为gap的元素分为一组,并对每一组进行插入排序
  2. 2.重复步骤1,直到gap等于1停止,这时整个序列被分到了一组,进行一次直接插入排序,排序完成

你可能会疑惑,为什么gap由大到小?

因为gap越大,数据挪动的越快,耗时少;gap越小,数据挪动的越慢,耗时多。前期让gap较大,可以让数据快速移动到自己对应位置附近,减少挪动次数。

动画演示

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3Is6Arbg-1668608929993)(希尔排序.assets/动画.gif)]

我们来举一个实例:

首先gap取5,此时相隔距离为5的元素分到了一组(一共五组,每组两个元素),然后对每一组分别进行插入排序

gap折半为2,此时相隔距离为2的元素被分到了一组(一共两组,每组五个元素),然后对每一组分别进行插入排序

gap再次折半为1,此时所有元素被分到了一组,对它进行插入排序,至此插入排序完成

本例中前两趟就是希尔排序的预排序,最后一趟就是希尔排序的插入排序

代码如下

  1. public static void shellSort(int[] a){
  2. int gal = a.length;
  3. while(gal>1) {
  4. int j = 0;
  5. gal/=2; //特别之处gal 分组排序 5 3 1.。。
  6. //核心
  7. for (int i = gal; i < a.length; i++) {
  8. j = i-gal;
  9. if(a[j] > a[i]) {
  10. int tmp = a[j];
  11. a[j] = a[i];
  12. a[i] = tmp;
  13. }
  14. }
  15. }
  16. }

复杂度分析

希尔排序的时间复杂度并不好计算,因为gap的取值方法很多中,导致很难去计算,下面是两位老师书中给出的解释。

《数据结构**-用面向对象方法与C++****描述》--- 殷人昆 **

  1. **时间复杂度** n^1.3 -- n^1.5 说不准 与每次分组的个数gap有关
  2. **空间复杂度** O(1)

** 稳定性** 不稳定 当有几个相同的数字时,排序后相对位置会改变

时间复杂度测试

接下来我们试着用大量数据测试一下。

int[] a = new int[10_0000]; //10万个数据测试

1.orderArray函数实现生成一个基本有序数列,即从小到大排列。

  1. public static void orderArray(int[] a) {
  2. for (int i = 0; i < a.length; i++) {
  3. a[i] = i;
  4. }
  5. }

2.notOrderArray函数生成一个倒序数列,即从大到小排列。

  1. public static void notOrderArray(int[] a) {
  2. for (int i = 0; i < a.length; i++) {
  3. a[i] = a.length-i;
  4. }
  5. }

3.randomArray函数生成一个随机无序数列。

  1. public static void randomArray(int[] a) {
  2. Random random = new Random();
  3. for (int i = 0; i < a.length; i++) {
  4. a[i] = random.nextInt(10_0000);
  5. }
  6. }

4.testInsertSort函数测试 System.currentTimeMillis() 返回值单位是毫秒

  1. public static void testInsertSort(int[] a){
  2. int[] tmpArray = Arrays.copyOf(a,a.length);
  3. long startTime = System.currentTimeMillis(); //注意用long接收
  4. shellSort(tmpArray);
  5. long endTime = System.currentTimeMillis(); //返回单位是毫秒
  6. System.out.println("直接插入排序耗时:"+(endTime-startTime));
  7. }

5.main函数调用执行

  1. public static void main(String[] args) {
  2. int[] a = new int[10_0000];
  3. //有序
  4. System.out.println("基本有序数列");
  5. orderArray(a);
  6. testInsertSort(a);
  7. //倒序
  8. System.out.println("逆序数列");
  9. notOrderArray(a);
  10. testInsertSort(a);
  11. //随机乱序
  12. System.out.println("无序数列");
  13. randomArray(a);
  14. testInsertSort(a);
  15. }

运行结果

希尔排序和直接插入排序都属于插入排序,而希尔排序更是直接插入排序的优化。对比上文直接插入排序的测试结果。

可以看出,希尔排序确实快了许多。并且耗时稳定。

完整代码

  1. import java.util.Random;
  2. public class sort {
  3. public static void main(String[] args) {
  4. int[] a = new int[10_0000];
  5. //有序
  6. System.out.println("基本有序数列");
  7. orderArray(a);
  8. testInsertSort(a);
  9. //无序
  10. System.out.println("逆序数列");
  11. notOrderArray(a);
  12. testInsertSort(a);
  13. //乱序
  14. System.out.println("无序数列");
  15. randomArray(a);
  16. testInsertSort(a);
  17. }
  18. //希尔排序 是插入排序的优化
  19. //时间复杂度 n^1.3 -- n^1.5 说不准 与每次分组的个数有关
  20. //空间复杂度 O(1)
  21. //稳定性 不稳定 当有几个相同的数字时,排序后相对位置会改变
  22. public static void shellSort(int[] a){
  23. int gal = a.length;
  24. while(gal>1) {
  25. int j = 0;
  26. gal/=2; //特别之处gal 分组排序 5 3 1.。。
  27. //核心
  28. for (int i = gal; i < a.length; i++) {
  29. j = i-gal;
  30. if(a[j] > a[i]) {
  31. int tmp = a[j];
  32. a[j] = a[i];
  33. a[i] = tmp;
  34. }
  35. }
  36. }
  37. }
  38. //生成有序数组 从小到大排列
  39. public static void orderArray(int[] a) {
  40. for (int i = 0; i < a.length; i++) {
  41. a[i] = i;
  42. }
  43. }
  44. //n无序 其实就是从大到小排列
  45. public static void notOrderArray(int[] a) {
  46. for (int i = 0; i < a.length; i++) {
  47. a[i] = a.length-i;
  48. }
  49. }
  50. //乱序 随机生成序列
  51. public static void randomArray(int[] a) {
  52. Random random = new Random();
  53. for (int i = 0; i < a.length; i++) {
  54. a[i] = random.nextInt(10_0000);
  55. }
  56. }
  57. //大量数据测试
  58. public static void testInsertSort(int[] a){
  59. int[] tmpArray = Arrays.copyOf(a,a.length);
  60. long startTime = System.currentTimeMillis(); //注意用long接收
  61. shellSort(tmpArray);
  62. long endTime = System.currentTimeMillis();
  63. System.out.println("希尔排序耗时:"+(endTime-startTime));
  64. }
  65. }

创作不易,如果本篇博客对您有一定的帮助,大家记得留言+点赞哦。


本文转载自: https://blog.csdn.net/m0_73381672/article/details/132020520
版权归原作者 去北极避暑~ 所有, 如有侵权,请联系我们删除。

“八大排序算法--希尔排序(动图理解)”的评论:

还没有评论