排序算法 —— 希尔排序(图文超详细)


排序算法:

1、直接插入排序

2、选择排序

3、堆排序

希尔排序(直接插入排序的优化)

希尔排序是将数据分组,将每一组进行插入排序。
每一组排成有序后,最后整体就变有序了。

1.分组思想


上图中gap为5,说明要分成5组。
这5组分别用了五种颜色的线条连接起来了。

第1组:9、4
第2组:1、8
第3组:2、6
第4组:5、3
第5组:7、5

为什么要采取上面的分组方法呢?换一种方法可以吗?
例如:挨着的元素分为一组。


如果是上面的这种分组方式的话,排序之后会变成下面的情况。



如果是最开始的分组方法的话

如果是按照最开始的分组思想分组的话,最后会排序成

可以发现左边都是叫小的数据,右边都是较大的数据。
更方便把分成的每一个组进行插入排序。

2.缩小增量的过程

前面gap为5的情况排序后会变成下面情况


按照gap把数据分成了两组。

当数据很大的时候,数据的间隔很大。
小的数据会往前方,大的数据会往后放。

gap会逐渐缩小,间隔也会逐渐缩小。

整体的数据会更加趋于有序,这个时候使用直接插入排序效率会更高。



gap为2的时候会排序成下面情况

此时分为了1组,再排序一次后变成下面情况

此时的数据即为有序的。

3.排序步骤

3.1 排序五组数据的情况

  1. gap为5,将数据分为5组,图中红色线画中的为第一组。定义一个 i 变量指向这一组的第二个数据,定义一个 j 变量指向 i - gap 的位置。
  2. 将 i 下标的值放到定义的 tmp 中,然后与 j下标 的值比较。
    若 j 下标的值较大,将 j 下标的值放到 j + gap 的位置。

    执行后:
  3. j 变量向 j - gap 位置走,若这个位置的下标为负数。
    则要将 tmp 的值放到 j + gap的位置。

    j 变量此时在-5下标处,要将 tmp 的值放到 j + 5的位置。

    这一组数据此时为有序了。
    排序下一组数据,**i++**即可,j 变量依然是在 i - gap 的位置。
    后面4组数据类似,不在演示。
    最终排序结果是:

3.2 排序两组数据的情况

  1. 此时 gap 为2,数据此时分为了两组。第一组由红色线画出(4、2、5、8、5),第二组由蓝色线画出(1、3、9、6、7)。
    i 变量指向这一组的第二个数据, j 变量指向 i - gap 的位置。

  2. 将 i 下标的值放到定义的 tmp 中,然后与 j下标 的值比较。
    若 j 下标的值较大,将 j 下标的值放到 j + gap 的位置。

    执行后:

  3. j 变量向 j - gap 位置走,若这个位置的下标为负数。
    则要将 tmp 的值放到 j + gap的位置。

    j 变量此时在-2下标处,要将 tmp 的值放到 j + 2的位置。

    这一组数据中的 2 和 4 此时为有序了。
    排序下一组数据,i++ 即可,j 变量依然是在 i - gap 的位置。
    后面剩下的数据类似,不在演示。
    最终排序结果是:

3.3 排序一组数据的情况

  1. i 变量指向第二个数据, j 变量指向 i - gap 的位置。
  2. 将 i 下标的值放到定义的 tmp 中,然后与 j下标 的值比较。
    若 j 下标的值较大,将 j 下标的值放到 j + gap 的位置。

    执行后:
  3. j 变量向 j - gap 位置走,若这个位置的下标为负数。
    则要将 tmp 的值放到 j + gap的位置。

    j 变量此时在-1下标处,要将 tmp 的值放到 j + 1的位置。

    此时 前两个数据有序了,后面的数据排序过程类似。
    排序下一组数据,i++ 即可,j 变量依然是在 i - gap 的位置。
    最终结果是:

4.代码分析

4.1 如何设置数据组数

  1. 定义 gap 将数组的长度赋值给这个变量。
int gap = array2.length;//为数组的长度 - 为10
  1. 设置循环,每次使 gap 除以2来得到5、2、1三个组。
  2. 在循环内部调用直接插入排序方法。
 while (gap > 1) {
     gap /= 2;//先是分成了5组,然后是2组,再是1组
     shell(array2, gap);//调用直接插入排序方法
 }

4.2 直接插入排序实现思路

  1. 遍历数组,i 变量从 gap 下标位置开始
for (int i = gap; i < array2.length ; i++){
}
  1. j 变量从 i- gap 位置开始
  int j = i - gap;
  1. j 变量要每次减去 gap 个位置,j 此时的位置要是负数就比较 j 和 tmp 的值。
for (; j >= 0; j-=gap){
}
  1. j 和 tmp 如何比较
 if (array2[j] > tmp) {
     //j下标的值大,将j下标的值放到j变量加上一个gap的位置上
     array2[j + gap] = array2[j];
 }else {
     //j下标的值较小,j下标的值要直接放到j变量加上一个gap的位置上
     break;
 }
  1. j 下标为负数的情况
 //此时j下标的值是负数了,将tmp的值放到j变量加上一个gap的位置上
 array2[j + gap] = tmp;

更加详细的直接插入排序讲解请参考我的另一篇文章。


文章链接:http://t.csdn.cn/rBDzh

5. 整体代码实现

 public static void shellSort(int[] array2) {
     int gap = array2.length;//为数组的长度 - 为10
     while (gap > 1) {
         gap /= 2;//先是分成了5组,然后是2组,再是1组
         shell(array2, gap);//调用直接插入排序方法
     }
 }

 //实现直接插入排序方法
 public static void shell(int[] array2, int gap) {
     //i下标从第一组的第二个数据开始
     for (int i = gap; i < array2.length ; i++) {
         int tmp = array2[i];//tmp存放i下标的值
         int j = i - gap;//j下标为i-gap个位置
         //j每次-gap个位置
         for (; j >= 0; j-=gap) {
              if (array2[j] > tmp) {
                //j下标的值大,将j下标的值放到j变量加上一个gap的位置上
                array2[j + gap] = array2[j];
              }else {
                 //j下标的值较小,j下标的值要直接放到j变量加上一个gap的位置上
                 break;
              }
          }
          //此时j下标的值是负数了,将tmp的值放到j变量加上一个gap的位置上
          array2[j + gap] = tmp;
      }
 }

  • 169
    点赞
  • 397
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 10
    评论
冒泡排序是一种简单的排序算法,它重复地遍历待排序的元素,比较相邻元素的大小,如果顺序错误就交换它们,直到没有需要交换的元素为止。下面是冒泡排序的C语言实现: ```c void bubbleSort(int arr[], int n) { for(int i = 0; i < n-1; i++) { int flag = 0; //是否冒泡 for(int j = 0; j < n-1; j++) { if(arr[j > arr[j+1]) { swap(&arr[j], &arr[j+1]); flag = 1; } } if(flag == 0) { //如果没有发生交换,说明数组已经有序,可以提前结束循环 break; } } } ``` 在这个实现中,冒泡排序函数`bubbleSort`接受一个整型数组`arr`和数组长度`n`作为参数。它使用两层循环来遍历数组,并通过比较相邻元素的大小来进行交换。内层循环每次将最大的元素冒泡到数组的末尾。外层循环控制了冒泡的轮数,每一轮都会将当前未排序的最大元素放在正确的位置上。如果在某一轮的冒泡过程中没有发生交换,说明数组已经有序,可以提前结束循环,从而提高算法的效率。 需要注意的是,上述代码中使用了一个`swap`函数来交换两个元素的值,你可以根据需要自行实现该函数。此外,为了减少冒泡排序的时间复杂度,可以在内层循环中添加一个标志位`flag`,用于标记是否发生了交换。如果某一轮的冒泡过程中没有发生交换,说明数组已经有序,可以提前结束循环。这样可以避免不必要的比较和交换操作,提高排序的效率。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 10
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

与大师约会

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值