首页 > 系统相关 >排序之希尔排序(shell sort)

排序之希尔排序(shell sort)

时间:2022-11-03 12:32:19浏览次数:48  
标签:sort arr shell int 插入排序 gap 有序 序列 排序

前言

  本篇博客是在伍迷兄的博客基础上进行的,其​​博客地址​​点击就可以进去,里面好博客很多,我的排序算法都来自于此;一些数据结构方面的概念我就不多阐述了,伍迷兄的博客中都有详细讲解,而我写这些博客只是记录自己学习过程,加入了一些自己的理解,同时也希望给别人提供帮助。

前提故事

   骚年在上次与博主进行了直接插入排序的讨论后,找到了博主,说:“博主,对于直接插入排序,我有重大的发现”,博主想了想,就问:“什么发现?”,骚年:“我发现了如下两点”

    1)当序列的个数比较少时,直接插入排序效率高;这个好理解,个数比较少,那么插入的次数也就少了,博主就说:“恩,这个发现不难,却也需要细心”。

    2)如果序列本身就是基本有序,那么直接插入排序效率高;博主:“嗯?”,骚年解释道:“你看直接插入排序的核心代码:”

       

for(int i=1; i<len; i++){                            
for(int j=i-1; j>=0&&arr[j]>arr[j+1]; j--){
swap(arr,j,j+1);
}
}

    骚年接着道:“如果序列有序,那么j>=0&&arr[j]>arr[j+1]条件就是不满足的,插入操作就不会执行,效率自然就高了。”

    博主:“然后了?”。

    骚年:“那么我们是不是可以在这两点上做点事,来提高直接插入排序在普通序列上的效率了?”。

    上述两个条件过于苛刻,现实中记录少或者基本有序都属于特殊情,有条件当然是好,条件不存在,我们创造条件,也是可以去做的;骚年与博主进行了研究与讨论,我们可以对序列进行分组,分割成若干个子序列,然后对每个子序列分别进行直接插入排序,当整个序列都基本有序时,注意只是基本有序时,再对全体记录进行一次直接插入排序

    此时一定有人开始疑惑了。这不对呀,比如我们现在有序列是{5,3,7,9,1,6,4,8,2},现在将它分成三组,{5,3,7}, {9,1,6},{4,8,2},哪怕将它们各自排序排好了,变成{3,5,7},{1,6,9},{2,4,8},再合并它们成 {3,5,7,1,6,9,2,4,8},此时,这个序列还是杂乱无序,谈不上基本有序,要排序还是重来一遍直接插入有序,这样做有用吗?需要强调一下, 所谓的基本有序,就是小的关键字基本在前面,大的基本在后面,不大不小的基本在中间,像{2,1,3,6,4,7,5,8,9}这样可以称为基本有序了。 但像{3,5,7,1,6,9,2,4,8}这样的7在第三位,2在倒数第三位就谈不上基本有序。
        那么问题就来了,我们分割待排序记录的目的是减少待排序记录的个数,并使整个序列向基本有序发展。而如上面这样分完组后,就各自排序的方法达不到我们的要求。因此,我们需要采取跳跃分割的策略:将相距某个“增量”的记录组成一个子序列,这样才能保证在子序列内分别进行直接插入排序后得到的结果是基本有序而不是局部有序

基本思想

  将整个序列按照相距某个“增量”进行拆分,然后逐个对子序列进行直接插入排序,使得得到的结果基本有序,最后对基本有序的序列进行一次直接插入排序,使得整个序列有序

代码实现

  java实现

/**
* 希尔排序
* @param arr 目标序列
*/
public static void shellSort(int[] arr){
int len = arr.length;
for(int gap=len/2; gap>=1; gap=gap/2){ //拆分整个序列,元素间距为gap(也就是增量)
//对子序列进行直接插入排序
for(int i=gap+1; i<len; i++){
for(int j=i-gap; j>=0&&arr[j]>arr[j+gap]; j=j-gap){
swap(arr,j,j+gap);
}
}
}
}

View Code

执行过程模拟

  1)程序开始执行,初始序列为{5,3,7,9,1,6,4,8,2},如下图:

排序之希尔排序(shell sort)_子序列

  2)初始gap=len/2=4

    2.1)i=gap=4,初始j=0;比较arr[j]与arr[j+gap],即arr[0]与arr[4],如下图:

排序之希尔排序(shell sort)_直接插入排序_02

      j=j-gap=-4,跳出,i++,i=5

    2.2)i=5,j=i-gap=1,arr[1]=3 < arr[5]=6,不交换数据,如下图:

    

排序之希尔排序(shell sort)_排序_03

      j=j-gap=-3,跳出,i++,i=6

    2.3)同理,当i=6,7时,序列如下图:

 

排序之希尔排序(shell sort)_直接插入排序_04

    2.4)当i=8时,序列如下:

排序之希尔排序(shell sort)_排序_05

    那么gap=4时,最终序列为

排序之希尔排序(shell sort)_直接插入排序_06

  3)gap=gap/2=2

    3.1)i=gap=2,j=i-gap=0,arr[0]=1 < arr[2]=4不交换,j=j-gap=-2,i++,此时序列为:

排序之希尔排序(shell sort)_i++_07

    3.2)i=3,j=i-gap=1,arr[1]=3 < arr[3]=8,不交换,j=j-gap=-1,i++,此时序列为:

排序之希尔排序(shell sort)_子序列_08

    3.3)同理,i=4时:

排序之希尔排序(shell sort)_直接插入排序_09

    3.4)i=5时:

排序之希尔排序(shell sort)_i++_10

    3.5)i=6时:

排序之希尔排序(shell sort)_希尔_11

    3.6)i=7时:

排序之希尔排序(shell sort)_i++_12

    3.7)i=8时:

排序之希尔排序(shell sort)_希尔_13

  4)gap=gap/2=1,此时

for(int gap=len/2; gap>=1; gap=gap/2){                    //拆分整个序列,元素间距为gap(也就是增量)
//对子序列进行直接插入排序
for(int i=gap; i<len; i++){
for(int j=i-gap; j>=0&&arr[j]>arr[j+gap]; j=j-gap){
swap(arr,j,j+gap);
}
}

    就是

//对子序列进行直接插入排序
for(int i=1; i<len; i++){
for(int j=i-1; j>=0&&arr[j]>arr[j+1]; j=j-1){
swap(arr,j,j+1);
}
}

    相信大家都发现了,上面代码就是我们的直接插入排序,那么具体的模拟过程我也就不再赘述了,不懂的可以去看​​排序之直接插入排序​​

  至此,整个序列就有序了。

难解之处

通过这段代码的剖析,相信大家有些明白,希尔排序的关键并不是随便的分组后各自排序,而是将相隔某个“增量”的记录组成一个子序列,实现跳跃式的移动,使得排序的效率提高。这里“增量”的选取就非常关键了,本例中是以gap=gap/2的方式选取增量的,可究竟应该选取什么样的 增量才是最好,目前还是一个数学难题,迄今为止还没有人找到一种最好的增量序列。不过大量的研究表明,当增量序列为dlta[k]=2t-k+1-1(0≤k≤t≤⌊log2(n+1)⌋)时,可以获得不错的效率。需要注意的是,增量序列的最后一个增量值必须等于1才行。 



标签:sort,arr,shell,int,插入排序,gap,有序,序列,排序
From: https://blog.51cto.com/u_13423706/5819404

相关文章

  • List<map> 用stream聚合后 排序变乱序解决办法
    Map<Object,List<Map<String,Object>>>name=list.stream().collect(Collectors.groupingBy(x->x.get("name"),LinkedHashMap::new,Collectors.toList()));  加......
  • 17、怎样对简单列表元素排序
    题目:  简单列表:元素类型不是复合类型(列表、元组、字典)形式1:[20,50,10,40,30]形式2:['bb','ee','aa','dd','cc']  知识点:怎样原地排序?怎样不改变原列表排序?怎样......
  • 执行shell脚本时,几种方式的区别。
    执行shell脚本文件时,一定是sourcestart_py.sh不能是bashstart_py.sh也不能是shstart_py.sh还不能是./start_py.sh其区别在于,source启动的shell脚本,是在父进程中......
  • Shell 学习笔记小结
    1.变量声明变量以 ​​a-zA-Z​​ 开头,不包含特殊字符等号两边没有空格不与保留字符重名PATH="/user/yihui"PATH="/user/yihui"使用变量前加$符号,表示引用变量,可以用......
  • Termux-连接xshell
    1、安装ssh即可termux的安装命令为:"pkginstallopenssh"2、termux的ssh默认端口是8022,shell中也要设置端口为80223、安装好ssh后设置密码,命令行直接输入:"passw......
  • shell传参内容超过10个如何获取
    编写脚本中如果我们命令行传参个数超过10个,无法获取第九个以后的值测试:(可以看到,从第10个传参开始,无法获取正确传参内容)[root@localhost]#cattest.sh#!/bin/bashtes......
  • shell语法
    shell语法1摘自:https://www.acwing.com/file_system/file/content/whole/index/content/2855883/概论shell是我们通过命令行与操作系统沟通的语言。shell脚本可以直接......
  • 巧用shell脚本批量替换字符串
    ​作者:田逸(formyz)需求描述​有一个网站,因为域名变更,除了需要重新做域名解析外,还需要对网站目录的包含原域名的文件进行替换。包含域名(主机名)关键字的文件相当的多,它们分布在......
  • shell编程之数组
    1什么是数组数组(Array)是有序的元素序列。若将有限个类型相同的变量的集合命名,那么这个名称为数组名。组成数组的各个变量称为数组的分量,也称为数组的元素,有时也称为下......
  • Java自定义排序:继承Comparable接口,重写compareTo方法(排序规则)
    importjava.util.Arrays;importjava.util.TreeMap;/***学习自定义排序:继承Comparable接口,重写compareTo方法(排序规则)。*TreeMap容器的Key是自动排序的,key为......