排序算法 希尔排序 ShellSort -- C语言实现

希尔排序

概述

希尔排序,也称递减增量排序算法,也称缩小增量排序,是插入排序的一种更高效的改进版本。但希尔排序是非稳定排序算法。

希尔排序是基于插入排序的以下两点性质而提出改进方法的:

  • 插入排序在对几乎已经排好序的数据操作时,效率高,即可以达到线性排序的效率;
  • 但插入排序一般来说是低效的,因为插入排序每次只能将数据移动一位;

希尔排序的基本思想是:先将整个待排序的记录序列分割成为若干子序列分别进行直接插入排序,待整个序列中的记录"基本有序"时,再对全体记录进行依次直接插入排序。

插入排序主要性能浪费在挪动上,坏情况下需要每次插入都要挪动多次.

希尔排序将整体有序化,大大减小每轮挪动的次数.

希尔排序依旧有挪动的性能浪费,只是概率减小

小规模数据优化:对于小规模的数据集,希尔排序可能比其他算法更快,因为其时间复杂度接近线性。因此希尔/插入排序可以在其他排序过程中,取代"合并有序"的部分,以提高效率.

优缺点

  • 不需要大量的辅助空间,和归并排序一样容易实现。

  • 希尔排序是基于插入排序的一种算法, 在此算法基础之上增加了一个新的特性,提高了效率。希尔排序的时间的时间复杂度为O(n3/2),希尔排序时间复杂度的下界是n*log2n。希尔排序没有快速排序算法快 O(n(logn)),因此中等大小规模表现良好,对规模非常大的数据排序不是最优选择。但是比O( )复杂度的算法快得多。并且希尔排序非常容易实现,算法代码短而简单。 此外,希尔算法在最坏的情况下和平均情况下执行效率相差不是很多,与此同时快速排序在最坏的情况下执行的效率会非常差。专家们提倡,几乎任何排序工作在开始时都可以用希尔排序,若在实际使用中证明它不够快,再改成快速排序这样更高级的排序算法.

  • 本质上讲,希尔排序算法是直接插入排序算法的一种改进,减少了其复制的次数,速度要快很多。 原因是,当n值很大时数据项每一趟排序需要移动的个数很少,但数据项的距离很长。当n值减小时每一趟需要移动的数据增多,此时已经接近于它们排序后的最终位置。 正是这两种情况的结合才使希尔排序效率比插入排序高很多。Shell算法的性能与所选取的分组长度序列有很大关系。只对特定的待排序记录序列,可以准确地估算关键词的比较次数和对象移动次数。想要弄清关键词比较次数和记录移动次数与增量选择之间的关系,并给出完整的数学分析,今仍然是数学难题。

  • 在实际情况下,IntroSort等高级混合排序算法倾向于使用插入排序而不是希尔排序,主要是因为插入排序在处理小规模数组时足够高效,并且插入排序是稳定的,同时避免了希尔排序可能带来的复杂性和稳定性问题。此外,插入排序的实现更为简单,这有助于减少整体算法的复杂度和维护成本。

算法步骤

选择一个增量序列 t1,t2,……,tk,其中 ti > tj, tk = 1;

按增量序列个数 k,对序列进行 k 趟排序;

每趟排序,根据对应的增量 ti,将待排序列分割成若干长度为 m 的子序列,分别对各子表进行直接插入排序。仅增量因子为 1 时,整个序列作为一个表来处理,表长度即为整个序列的长度。

代码实现

注:以升序举例,排序数组为a[n]

算法来源:

优化直接插入排序,结合直接插入特性,先整体有序(相对),再局部有序,充分发挥插入排序特性.

算法思想:

前n-1个有序数组,第n个为待排序(插入)元素,通过插入让第n个待排序数组也有序.

具体状态:

  1. a[n-1]<a[n] ,有序

  2. 至少存在a[n] < a[0],a[1],...,a[n-1],需要排序

    其中边界为an[n]<a[0]

  3. n == 1:边界

    一个一定是有序的

  4. i == n-2为最后一组有序数组的边界,n-1为最后一个需要插入的元素

希尔排序是不稳定排序

希尔排序特征:

  • 迭代范围:从小到大
  • 插入过程范围:从大到小
  • 插入过程:挪动覆盖
  • 几乎和插入排序一样,在插入上优化,理解插入再理解希尔就容易

image-20240812131348152

image-20240812131818730

void ShellSort(int* a, int n)
{
    int gap = n;
    while (gap > 1) {
        gap /= 2;    			//任何数除以2,最后一定能得到1,gap==1就是最后一轮
        //gap = gap/3+1;  //除以3不一定能得到1,需要+1保证一定得到1.

        for (int i = 0; i < n - gap; i += 1) { //最后一轮排序,需要保证end+gap<n,即end<n-gap
            int end = i;
            int tmp = a[end + gap];  //间隔排序

            while (end >= 0) {
                if (a[end] > tmp) {
                    a[end + gap] = a[end];
                    end -= gap;
                }
                else {
                    break;
                }
            }
            a[end + gap] = tmp;
        }
    }
}
posted @ 2024-08-06 21:50  HJfjfK  阅读(12)  评论(0编辑  收藏  举报