js-希尔排序简介(插入排序升级)

55 阅读3分钟

插入排序定义:维护一个有序区,把元素一个个插入有序区的适当位置,直到所有元素都有序为止。

插入排序工作量相对比较小:

1.数组元素较少的时候,插入排序的工作量会比较小。因为插入排序的时间复杂度是o(n²),工作量和n²成正比,如果n比较小,那么排序的工作量自然会小很多。

2.当数组大部分元素有序的时候。插入排序的工作量也会相对较小。因为在这种情况下,数组中的元素并不需要进行频繁的比较和交换。

希尔排序定义:让元素分组,让元素两两一组,同组两个元素之间的跨度,是数组总长度的一半。

例子:定义无序数组为:[12, 3, 1, 46, 0, 5]

分组跨度为3。

元素12,46为一组,元素3,0为一组,元素1,5为一组。一共三组。

我们让每组元素进行独立排序,排序方法用直接插入排序即可。由于每一种的元素只有2个,所以插入排序的工作量很少。排序之后的数组为 [12,0,1,46,3,5]

第一轮排序完,数组整体的有序程序得到了显著提高,使得后续再进行直接插入排序的工作量大大减少。这种做法,可以理解为对原始数组的"粗略调整"。

我们把分组跨度进一步减小,把跨度缩小为原来的一半后向下取整,让跨度为1,也就等同于做直接插入排序。

经过之前的一系列粗略调整,直接插入排序的工作量减少了很多。

像这样逐步分组进行粗调,再进行直接插入排序的思想,就是希尔排序。

所使用的分组跨度(3,1),被称为希尔排序的增量。

示例代码:从小到大排序

shell_sort_asc(arr) {
      // 参数为需要排序的数组
      // 获取初始的增量,第一轮跨度为3,第二轮跨度为1(等于插入排序)
      let gap = Math.floor(arr.length / 2);

      while (gap >= 1) {
        // 增量小于1时结束循环

        // 从gap开始遍历,因为插入排序假设第一个是有序的
        for (let i = gap; i < arr.length; i++) {
          let j = i; // 为插入排序从后向前排序提供条件

          // 如果排序的后面的数字小于前面的,交换两个数的位置
          while (arr[j] < arr[j - gap] && j - gap >= 0) {
            let temp = arr[j];
            arr[j] = arr[j - gap];
            arr[j - gap] = temp;

            // j减小gap从后向前遍历
            j -= gap;
            console.log("shell_sort", j, gap, i, arr);
          }
          console.log("shell_sort1", j, gap, i, arr);
        }
        console.log("shell_sort222222222222222", gap);
        // 增量每次都减小一半
        gap = Math.floor(gap / 2);
      }

      return arr;
    },

示例代码:从大到小排序

shell_sort_all_desc(arr) {
      // 参数为需要排序的数组
      // 获取初始的增量
      let gap = Math.floor(arr.length / 2);

      while (gap >= 1) {
        // 增量小于1时结束循环

        // 从gap开始遍历,因为插入排序假设第一个是有序的
        for (let i = gap; i < arr.length; i++) {
          let j = i; // 为插入排序从后向前排序提供条件

          // 如果排序的后面的数字大于前面的,交换两个数的位置
          while (arr[j] > arr[j - gap] && j - gap >= 0) {
            let temp = arr[j];
            arr[j] = arr[j - gap];
            arr[j - gap] = temp;

            // j减小gap从后向前遍历
            j -= gap;
          }
        }

        // 增量每次都减小一半
        gap = Math.floor(gap / 2);
      }

      return arr;
    },

稳定排序定义:待排序的记录序列中可能存在两个或两个以上关键字相等的记录。排序前的序列中Ri领先于Rj(即i<j).若在排序后的序列中Ri仍然领先于Rj,则称所用的方法是稳定的。

希尔排序不是稳定排序。例子:

数组:[5,8,6,5,4,2,1,7]

按照希尔增量分组,跨度为4,5跟4为一组。

数组第0项换到去数组第4项。

第一轮粗调后,相同的元素5,在排序之后改变了次序。由此可见希尔排序是一个不稳定排序。