热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

那些年我们排过的序之希尔排序

基本思想先取一个小于n的整数d1作为第一个增量,把文件的全部记录分成d1个组。所有距离为d1的倍数的记录放在同一个组中。先在各组内进行直接插入排序;然后

基本思想

先取一个小于n的整数d1作为第一个增量,把文件的全部记录分成d1个组。所有距离为d1的倍数的记录放在同一个组中。先在各组内进行直接插入排序;然后,取第二个增量d2

该方法实质上是一种分组插入方法引

比较相隔较远距离(称为增量)的数,使得数移动时能跨过多个元素,则进行一次比[1]较就可能消除多个元素交换。D.L.shell于1959年在以他名字命名的排序算法中实现了这一思想。算法先将要排序的一组数按某个增量d分成若干组,每组中记录的下标相差d.对每组中全部元素进行排序,然后再用一个较小的增量对它进行,在每组中再进行排序。当增量减到1时,整个要排序的数被分成一组,排序完成。

一般的初次取序列的一半为增量,以后每次减半,直到增量为1。

排序过程

假设待排序文件有10个记录,其关键字分别是:

四九,三八,六五,九七,七六,一三,二七,四九,五五,零四。

增量序列的取值依次为:

五,三,一[2]

缩小增量

希尔排序属于插入类排序,是将整个无序列分割成若干小的子序列分别进行插入排序。

排序过程:先取一个正整数d1

[3] 三趟结果

零四 一三 二七 三八 四九 四九 五五 六五 七六 九七

Shell排序

Shell排序的算法实现:

1. 不设监视哨的算法描述

void ShellPass(SeqList R,int d)

{//希尔排序中的一趟排序,d为当前增量

for(i&#61;d&#43;1;i<&#61;n&#xff1b;i&#43;&#43;) //将R[d&#43;1&#xff0e;&#xff0e;n]分别插入各组当前的有序区

if(R[ i ].key

R[0]&#61;R[i];j&#61;i-d&#xff1b; //R[0]只是暂存单元&#xff0c;不是哨兵

do {//查找R的插入位置

R[j&#43;d]&#61;R[j]&#xff1b; //后移记录

j&#61;j-d&#xff1b; //查找前一记录

}while(j>0&&R[0].key

R[j&#43;d]&#61;R[0]&#xff1b; //插入R到正确的位置上

} //endif

优劣

不需要大量的辅助空间&#xff0c;和归并排序一样容易实现。希尔排序是基于插入排序的一种算法&#xff0c; 在此算法基础之上增加了一个新的特性&#xff0c;提高了效率。希尔排序的时间复杂度与增量序列的选取有关&#xff0c;例如希尔增量时间复杂度为O(n²)&#xff0c;而Hibbard增量的希尔排序的时间复杂度为O(N(3/2))&#xff0c;但是现今仍然没有人能找出希尔排序的精确下界。希尔排序没有快速排序算法快 O(N*(logN))&#xff0c;因此中等大小规模表现良好&#xff0c;对规模非常大的数据排序不是最优选择。但是比O(N^2)复杂度的算法快得多。并且希尔排序非常容易实现&#xff0c;算法代码短而简单。 此外&#xff0c;希尔算法在最坏的情况下和平均情况下执行效率相差不是很多&#xff0c;与此同时快速排序在最坏的情况下执行的效率会非常差。专家们提倡&#xff0c;几乎任何排序工作在开始时都可以用希尔排序&#xff0c;若在实际使用中证明它不够快&#xff0c;再改成快速排序这样更高级的排序算法. 本质上讲&#xff0c;希尔排序算法的一种改进&#xff0c;减少了其复制的次数&#xff0c;速度要快很多。 原因是&#xff0c;当N值很大时数据项每一趟排序需要的个数很少&#xff0c;但数据项的距离很长。当N值减小时每一趟需要和动的数据增多&#xff0c;此时已经接近于它们排序后的最终位置。 正是这两种情况的结合才使希尔排序效率比插入排序高很多。

时间性能

1&#xff0e;增量序列的选择

Shell排序的执行时间依赖于增量序列。

好的增量序列的共同特征&#xff1a;

① 最后一个增量必须为1&#xff1b;

② 应该尽量避免序列中的值(尤其是相邻的值)互为倍数的情况。

有人通过大量的实验&#xff0c;给出了较好的结果&#xff1a;当n较大时&#xff0c;比较和移动的次数约在nl.25到1.6n1.25之间。

2&#xff0e;Shell排序的时间性能优于直接插入排序

希尔排序的时间性能优于直接插入排序的原因&#xff1a;

①当文件初态基本有序时直接插入排序所需的比较和移动次数均较少。

②当n值较小时&#xff0c;n和n2的差别也较小&#xff0c;即直接插入排序的最好时间复杂度O(n)和最坏时间复杂度0(n2)差别不大。

③在希尔排序开始时增量较大&#xff0c;分组较多&#xff0c;每组的记录数目少&#xff0c;故各组内直接插入较快&#xff0c;后来增量di逐渐缩小&#xff0c;分组数逐渐减少&#xff0c;而各组的记录数目逐渐增多&#xff0c;但由于已经按di-1作为距离排过序&#xff0c;使文件较接近于有序状态&#xff0c;所以新的一趟排序过程也较快。

因此&#xff0c;希尔排序在效率上较直接插入排序有较大的改进。

希尔排序是按照不同步长对元素进行插入排序&#xff0c;当刚开始元素很无序的时候&#xff0c;步长最大&#xff0c;所以插入排序的元素个数很少&#xff0c;速度很快&#xff1b;当元素基本有序了&#xff0c;步长很小&#xff0c;插入排序对于有序的序列效率很高。所以&#xff0c;希尔排序的时间复杂度会比o(n^2)好一些。由于多次插入排序&#xff0c;我们知道一次插入排序是稳定的&#xff0c;不会改变相同元素的相对顺序&#xff0c;但在不同的插入排序过程中&#xff0c;相同的元素可能在各自的插入排序中移动&#xff0c;最后其稳定性就会被打乱&#xff0c;所以shell排序是不稳定的。

举例阐述&#xff1a;

例如&#xff0c;假设有这样一组数[ 13 14 94 33 82 25 59 94 65 23 45 27 73 25 39 10 ]&#xff0c;如果我们以步长为5开始进行排序&#xff0c;我们可以通过将这列表放在有5列的表中来更好地描述算法&#xff0c;这样他们就应该看起来是这样&#xff1a;

13 14 94 33 82
25 59 94 65 23
45 27 73 25 39
10

然后我们对每列进行排序&#xff1a;

10 14 73 25 23
13 27 94 33 39
25 59 94 65 82
45

将上述四行数字&#xff0c;依序接在一起时我们得到&#xff1a;[ 10 14 73 25 23 13 27 94 33 39 25 59 94 65 82 45 ].这时10已经移至正确位置了&#xff0c;然后再以3为步长进行排序&#xff1a;

10 14 73
25 23 13
27 94 33
39 25 59
94 65 82
45

排序之后变为&#xff1a;

10 14 13
25 23 33
27 25 59
39 65 73
45 94 82
94

最后以1步长进行排序&#xff08;此时就是简单的插入排序了&#xff09;。

图示&#xff1a;

C&#43;&#43;代码实现&#xff1a;

 

#include
 
void  shellsort(int *data, size_t size);

 

 int i, j, temp;
 int gap &#61; 0;

 

int main()
{
     const int n &#61; 5;

 

     int a[] &#61; {5, 4, 3, 2, 1};
  shellsort(a,5);
   /*  while (gap<&#61;n)
     {
          gap &#61; gap * 3 &#43; 1;
     }
     while (gap > 0)
     {
         for ( i &#61; gap; i          {
             j &#61; i - gap;
             temp &#61; a[i];            
             while (( j >&#61; 0 ) && ( a[j] > temp ))
             {
                 a[j &#43; gap] &#61; a[j];
                 j &#61; j - gap;
             }
             a[j &#43; gap] &#61; temp;
         }
         gap &#61; ( gap - 1 ) / 3;
     } */
  for(i&#61;0;i<5;i&#43;&#43;)
     printf("%d\n",a[i]);
 }

 


void shellsort(int *data, size_t size)
{

 

 int key;
  int j &#61; 0;
     for (gap &#61; size / 2; gap > 0; gap /&#61; 2)
         for ( i &#61; gap; i          {
 
              key &#61; data[i];
            
              for( j &#61; i -gap; j >&#61; 0 && data[j] > key; j -&#61;gap)
              {
                 data[j&#43;gap] &#61; data[j];
               } 
              data[j&#43;gap] &#61; key;
          }
 }


转载于:https://www.cnblogs.com/hedengfeng/p/3407974.html


推荐阅读
  • Linux环境变量函数getenv、putenv、setenv和unsetenv详解
    本文详细解释了Linux中的环境变量函数getenv、putenv、setenv和unsetenv的用法和功能。通过使用这些函数,可以获取、设置和删除环境变量的值。同时给出了相应的函数原型、参数说明和返回值。通过示例代码演示了如何使用getenv函数获取环境变量的值,并打印出来。 ... [详细]
  • vue使用
    关键词: ... [详细]
  • Linux服务器密码过期策略、登录次数限制、私钥登录等配置方法
    本文介绍了在Linux服务器上进行密码过期策略、登录次数限制、私钥登录等配置的方法。通过修改配置文件中的参数,可以设置密码的有效期、最小间隔时间、最小长度,并在密码过期前进行提示。同时还介绍了如何进行公钥登录和修改默认账户用户名的操作。详细步骤和注意事项可参考本文内容。 ... [详细]
  • Iamtryingtomakeaclassthatwillreadatextfileofnamesintoanarray,thenreturnthatarra ... [详细]
  • 向QTextEdit拖放文件的方法及实现步骤
    本文介绍了在使用QTextEdit时如何实现拖放文件的功能,包括相关的方法和实现步骤。通过重写dragEnterEvent和dropEvent函数,并结合QMimeData和QUrl等类,可以轻松实现向QTextEdit拖放文件的功能。详细的代码实现和说明可以参考本文提供的示例代码。 ... [详细]
  • 本文介绍了C++中省略号类型和参数个数不确定函数参数的使用方法,并提供了一个范例。通过宏定义的方式,可以方便地处理不定参数的情况。文章中给出了具体的代码实现,并对代码进行了解释和说明。这对于需要处理不定参数的情况的程序员来说,是一个很有用的参考资料。 ... [详细]
  • 本文主要解析了Open judge C16H问题中涉及到的Magical Balls的快速幂和逆元算法,并给出了问题的解析和解决方法。详细介绍了问题的背景和规则,并给出了相应的算法解析和实现步骤。通过本文的解析,读者可以更好地理解和解决Open judge C16H问题中的Magical Balls部分。 ... [详细]
  • 本文讨论了使用差分约束系统求解House Man跳跃问题的思路与方法。给定一组不同高度,要求从最低点跳跃到最高点,每次跳跃的距离不超过D,并且不能改变给定的顺序。通过建立差分约束系统,将问题转化为图的建立和查询距离的问题。文章详细介绍了建立约束条件的方法,并使用SPFA算法判环并输出结果。同时还讨论了建边方向和跳跃顺序的关系。 ... [详细]
  • 本文讨论了一个关于cuowu类的问题,作者在使用cuowu类时遇到了错误提示和使用AdjustmentListener的问题。文章提供了16个解决方案,并给出了两个可能导致错误的原因。 ... [详细]
  • 本文介绍了P1651题目的描述和要求,以及计算能搭建的塔的最大高度的方法。通过动态规划和状压技术,将问题转化为求解差值的问题,并定义了相应的状态。最终得出了计算最大高度的解法。 ... [详细]
  • sklearn数据集库中的常用数据集类型介绍
    本文介绍了sklearn数据集库中常用的数据集类型,包括玩具数据集和样本生成器。其中详细介绍了波士顿房价数据集,包含了波士顿506处房屋的13种不同特征以及房屋价格,适用于回归任务。 ... [详细]
  • 本文介绍了UVALive6575题目Odd and Even Zeroes的解法,使用了数位dp和找规律的方法。阶乘的定义和性质被介绍,并给出了一些例子。其中,部分阶乘的尾零个数为奇数,部分为偶数。 ... [详细]
  • 本文介绍了解决二叉树层序创建问题的方法。通过使用队列结构体和二叉树结构体,实现了入队和出队操作,并提供了判断队列是否为空的函数。详细介绍了解决该问题的步骤和流程。 ... [详细]
  • 本文介绍了C函数ispunct()的用法及示例代码。ispunct()函数用于检查传递的字符是否是标点符号,如果是标点符号则返回非零值,否则返回零。示例代码演示了如何使用ispunct()函数来判断字符是否为标点符号。 ... [详细]
  • 本文介绍了一个程序,可以输出1000内能被3整除且个位数为6的所有整数。程序使用了循环和条件判断语句来筛选符合条件的整数,并将其输出。 ... [详细]
author-avatar
心梦飞扬-吕
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有