首页 > 系统相关 >详解十大经典排序算法(四):希尔排序(Shell Sort)

详解十大经典排序算法(四):希尔排序(Shell Sort)

时间:2023-12-20 12:01:22浏览次数:30  
标签:Sort arr Shell int 间隔 gap 序列 排序


算法原理

希尔排序是一种基于插入排序的排序算法,也被称为缩小增量排序。它通过将待排序的序列分割成若干个子序列,对每个子序列进行插入排序,然后逐步缩小增量,最终使整个序列有序。

算法描述

希尔排序(Shell Sort)是一种基于插入排序的算法,由Donald Shell于1959年提出。它是插入排序的一种更高效的改进版本。希尔排序的核心思想是将原始数据分成多个子序列,先对每个子序列进行插入排序,然后逐步减少子序列的数量,最终对整个数据序列进行插入排序。

这个算法的关键特点是其间隔序列的选择,即每次排序时元素之间的间隔。开始时,间隔较大,随着算法的进行,间隔逐渐减小,最后间隔为1,即普通的插入排序。通过这种方式,希尔排序可以快速减少大量的初期乱序,从而提高排序效率。

希尔排序的步骤如下:

  1. 选择合适的间隔序列:最初的版本使用序列长度的一半作为第一个间隔,然后逐步减半,直到间隔为1。现代版本可能会使用不同的间隔序列。
  2. 分组并排序:按照当前间隔将数组分为多个子序列,独立地对每个子序列应用插入排序。
  3. 减少间隔并重复:减少间隔大小,重复上述过程,直到间隔减至1。
  4. 最终排序:当间隔减至1时,进行一次标准的插入排序,此时由于序列已经部分排序,这一步骤会非常快。

动画演示

详解十大经典排序算法(四):希尔排序(Shell Sort)_排序算法

代码实现

public static void shellSort(int arr[]) {
        int n = arr.length;

        //设置排序的间隔(gap)。
        //初始间隔设置为数组长度的一半,每次循环后间隔减半。
        //循环继续直到间隔减至0。
        for (int gap = n / 2; gap > 0; gap /= 2) {

            //内层循环从gap开始,对数组进行遍历。
            for (int i = gap; i < n; i += 1) {
                int temp = arr[i];//存储当前元素的值。
                int j;//用于后续的内嵌循环,用于比较和交换元素。

                //此循环用于将较大的元素向右移动。
                //当j大于等于gap且arr[j - gap](当前元素的前一个间隔位置的元素)大于temp(当前元素)时,将arr[j - gap]向右移动到arr[j]的位置。
                for (j = i; j >= gap && arr[j - gap] > temp; j -= gap) {
                    arr[j] = arr[j - gap];
                }
                //此处的j 是上面循环 j -= gap 后的。
                arr[j] = temp;
            }
        }


    }

算法复杂度

时间复杂度(最坏)

时间复杂度(最好)

时间复杂度(平均)

空间复杂度

稳定性

O(n^2)

O(nlogn)

O(n^1.5)

O(1)

不稳定

  • 最好情况:O(nlogn)
  • 平均情况:取决于间隔序列。对于原始的间隔序列(减半序列),平均时间复杂度为O(n^1.5)。
  • 最坏情况:取决于间隔序列。对于某些不佳的间隔序列,最坏情况可以达到O(n^2)。
  • 空间复杂度:O(1)。希尔排序是一种原地排序算法。

希尔排序的优化方式

<<<前方高能 量力而行>>>

希尔排序的性能在很大程度上依赖于间隔序列的选择。不同的间隔序列会导致算法的性能有显著差异。优化希尔排序通常涉及使用更有效的间隔序列,而不是初始版本中的简单递减序列(如数组长度的一半递减至1)。以下是几种常见的优化方法:

  1. 优化间隔序列
  • Hibbard序列:使用形式为 2^k - 1 的间隔,如 1, 3, 7, 15, 31, … 这样的序列可以使希尔排序达到大约 O(n^(3/2)) 的时间复杂度。
  • Sedgewick序列:这是一种更复杂的间隔序列,可以进一步优化性能。一个常见的Sedgewick序列是 1, 5, 19, 41, 109, …
  • Knuth序列:使用形式为 (3^k - 1) / 2 的间隔,如 1, 4, 13, 40, 121, …

这些序列都是试图在排序过程中更有效地打乱数组,从而避免某些特定情况下的糟糕性能。

  1. 动态间隔

一种更高级的优化是根据数组的特定特征动态选择间隔。这种方法通常适用于特定类型的数据或在需要极致优化时使用。

下面是一个使用Hibbard间隔序列的希尔排序的Java实现示例:

public static void sort(int[] arr) {
        int n = arr.length;
        int h = 1;

        // 计算最大间隔
        while ((h * 2) - 1 < n) {
            h = h * 2;
        }
        h = h - 1;

        // 希尔排序
        while (h >= 1) {
            for (int i = h; i < n; i++) {
                int temp = arr[i];
                int j;
                for (j = i; j >= h && arr[j - h] > temp; j -= h) {
                    arr[j] = arr[j - h];
                }
                arr[j] = temp;
            }
            h = (h + 1) / 2 - 1; // 更新间隔为下一个较小的Hibbard数
        }
    }

这段代码首先计算最大的Hibbard间隔,然后在每次迭代中减小间隔,直到间隔为1。每个间隔都会对数组进行一轮希尔排序。这种使用Hibbard间隔的方法通常比简单的递减间隔方法表现得更好。


相关概念
稳定:如果a原本在b前面,而a=b,排序之后a仍然在b的前面。
不稳定:如果a原本在b的前面,而a=b,排序之后 a 可能会出现在 b 的后面。
时间复杂度:对排序数据的总的操作次数。反映当n变化时,操作次数呈现什么规律。
空间复杂度:是指算法在计算机内执行时所需存储空间的度量,它也是数据规模n的函数。


标签:Sort,arr,Shell,int,间隔,gap,序列,排序
From: https://blog.51cto.com/xaye/8905306

相关文章

  • 堆排序
    堆排序heapInsert&heapify排序思路来源一周刷爆LeetCode,算法大神左神(左程云)耗时100天打造算法与数据结构基础到笔记内容问题描述对一个数组进行大根堆排序算法思路heapInsert:视为用户一个个插入新数值,由下往上比较heapify:视为对所有子树排序,由子树的头结点从上往......
  • Linux--VM设置静态IP--VM&XShell连接
     1.配置环境Linux:CentOS7远程:XShell7 2.设置①打开虚拟机登录 cd/-->ipaddr 输入:cd/etc/sysconfig/network-scripts回车输入:viifcfg-ens33 --->进入网卡配置文件(必须在左下角Insert模式时编写可直接按......
  • beanshell
    beanshellbeanshell是一种java源代码解释器,具有脚本语言的特性使用beanshell可以是jmeter实现更多的业务需求 vars.get() 从jmeter中获得变量值vars.put()  把数据保存为jmeter的变量log.info()   打印props.get()  读取jmeter.properties文件里的内容 ......
  • [LeetCode Hot 100] LeetCode33. 搜索旋转排序数组
    题目描述思路如果nums[left]<=nums[mid],则[left,mid]有序如果nums[left]>nums[mid],则[mid,right]有序方法一:classSolution{publicintsearch(int[]nums,inttarget){if(nums==null||nums.length==0)return-1;intleft=0,ri......
  • [LeetCode Hot 100] LeetCode34.在排序数组中查找元素的第一个和最后一个位置
    题目描述思路:二分查找之寻找左右侧边界两个关键点:1.数组有序;2.时间复杂度O(logn)方法一:classSolution{publicint[]searchRange(int[]nums,inttarget){if(nums.length==0||nums==null){returnnewint[]{-1,-1};}......
  • [排序,贪心,置换环]洛谷P1327&&P8637,双倍经验
    前置知识:置换环,最小交换次数https://blog.csdn.net/yunxiaoqinghe/article/details/113153795?ops_request_misc=&request_id=&biz_id=102&utm_term=%E6%9C%80%E5%B0%91%E4%BB%BB%E6%84%8F%E4%BA%A4%E6%8D%A2%E6%8E%92%E5%BA%8F%E8%AF%81%E6%98%8E%E7%94%A8%E7%BD%AE%E6%8D......
  • [AGC054C] Roughly Sorted 题解
    题意定义一种操作为交换\(a_{i}\)和\(a_{i-1}\)。对于一个长度为\(n\)的排列,你需要操作若干次,使这个序列变合法,一个序列合法指:满足对于每一个\(1\lei\len\),都满足包含\(a_i\)的逆序对的个数不超过\(k\),并且要求最小化操作次数。现在给定一个操作后的序列,询问原序列可......
  • 无涯教程-Java - SortedSet 集合接口函数
    SortedSet接口扩展了Set并声明了按升序排序的集合的行为。除了Set定义的那些方法外,SortedSet接口还声明了下表中概述的方法-如果尝试使用null对象并且集合中不允许使用null,则抛出NullPointerException。Sr.No.Method&Remark1Comparatorcomparator()返回调用排序集的比......
  • 数据结构算法---二叉排序树
    二叉排序树(BinarySearchTree,BST),也称为二叉搜索树或二叉查找树,是一种经典的数据结构,它满足以下性质:对于树中的每个节点,其左子树中的所有节点的值都小于该节点的值。对于树中的每个节点,其右子树中的所有节点的值都大于该节点的值。左子树和右子树也都是二叉排序树。基于这些性......
  • 数据结构算法---冒泡排序
    冒泡排序(BubbleSort)是一种简单的排序算法,它重复地遍历要排序的列表,比较相邻两个元素并按照大小交换位置,直到整个列表排序完成。这种排序算法得名于越小的元素会经由交换慢慢"浮"到列表的顶端。下面是冒泡排序的基本步骤:从列表的第一个元素开始,比较它与下一个元素的大小。如果......